changeset 1682:6414043df7d8 db-changes

integration mainline->db-changes
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 06 Oct 2015 14:09:30 +0200
parents 1a3c20cd1b53 (current diff) ee4367497d0d (diff)
children 21d31da73374
files CMakeLists.txt Core/MultiThreading/BagOfRunnablesBySteps.cpp Core/MultiThreading/BagOfRunnablesBySteps.h OrthancServer/DicomProtocol/DicomServer.cpp OrthancServer/DicomProtocol/DicomServer.h
diffstat 9 files changed, 240 insertions(+), 312 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Tue Oct 06 11:12:13 2015 +0200
+++ b/CMakeLists.txt	Tue Oct 06 14:09:30 2015 +0200
@@ -117,9 +117,9 @@
   Core/RestApi/RestApiPath.cpp
   Core/RestApi/RestApiOutput.cpp
   Core/RestApi/RestApi.cpp
-  Core/MultiThreading/BagOfRunnablesBySteps.cpp
   Core/MultiThreading/Mutex.cpp
   Core/MultiThreading/ReaderWriterLock.cpp
+  Core/MultiThreading/RunnableWorkersPool.cpp
   Core/MultiThreading/Semaphore.cpp
   Core/MultiThreading/SharedMessageQueue.cpp
   Core/Images/Font.cpp
--- a/Core/MultiThreading/BagOfRunnablesBySteps.cpp	Tue Oct 06 11:12:13 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,190 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "../PrecompiledHeaders.h"
-#include "BagOfRunnablesBySteps.h"
-
-#include "../Logging.h"
-
-#include <stack>
-#include <boost/thread.hpp>
-
-namespace Orthanc
-{
-  struct BagOfRunnablesBySteps::PImpl
-  {
-    boost::mutex mutex_;
-
-    // 1. The set of active runnables, i.e. the runnables that have
-    // not finished their job yet, plus the runnables that have not
-    // been joined yet.
-    typedef std::map<IRunnableBySteps*, boost::thread*>  ActiveRunnables;
-    ActiveRunnables  activeRunnables_;
-    bool             continueActiveRunnables_;
-
-    // 2. Condition variable that is notified when one active runnable
-    // stops.
-    boost::condition_variable oneRunnableHasStopped_;
-
-    // 3. The list of runnables that have stopped but are waiting to be
-    // joined by the collector.
-    typedef std::stack<IRunnableBySteps*>  StoppedRunnables;
-    StoppedRunnables  stoppedRunnables_;
-
-    // 4. Condition variable that is notified when one stopped
-    // runnable has been joined.
-    boost::condition_variable oneRunnableIsJoined_;
-
-    // The thread that joins the runnables after they stop
-    bool continueCollector_;
-    std::auto_ptr<boost::thread> collector_;
-  };
-
-
-
-  void BagOfRunnablesBySteps::RunnableThread(BagOfRunnablesBySteps* bag,
-                                             IRunnableBySteps* runnable)
-  {
-    while (bag->pimpl_->continueActiveRunnables_)
-    {
-      if (!runnable->Step())
-      {
-        break;
-      }
-    }
-
-    {
-      // Register this runnable as having stopped
-      boost::mutex::scoped_lock lock(bag->pimpl_->mutex_);
-      bag->pimpl_->stoppedRunnables_.push(runnable);
-      bag->pimpl_->oneRunnableHasStopped_.notify_one();
-    }
-  }
-
-  
-  void BagOfRunnablesBySteps::CollectorThread(BagOfRunnablesBySteps* bag)
-  {
-    boost::mutex::scoped_lock lock(bag->pimpl_->mutex_);
-
-    while (bag->pimpl_->continueCollector_)
-    {
-      while (!bag->pimpl_->stoppedRunnables_.empty())
-      {
-        std::auto_ptr<IRunnableBySteps> r(bag->pimpl_->stoppedRunnables_.top());
-        bag->pimpl_->stoppedRunnables_.pop();
-
-        assert(r.get() != NULL);
-        assert(bag->pimpl_->activeRunnables_.find(r.get()) != bag->pimpl_->activeRunnables_.end());
-
-        std::auto_ptr<boost::thread> t(bag->pimpl_->activeRunnables_[r.get()]);
-        bag->pimpl_->activeRunnables_.erase(r.get());
-
-        assert(t.get() != NULL);
-        assert(bag->pimpl_->activeRunnables_.find(r.get()) == bag->pimpl_->activeRunnables_.end());
-
-        if (t->joinable())
-        {
-          t->join();
-        }
-
-        bag->pimpl_->oneRunnableIsJoined_.notify_one();
-      }
-
-      bag->pimpl_->oneRunnableHasStopped_.wait(lock);
-    }
-  }
-
-
-  BagOfRunnablesBySteps::BagOfRunnablesBySteps() : pimpl_(new PImpl)
-  {
-    pimpl_->continueActiveRunnables_ = true;
-    pimpl_->continueCollector_ = true;
-
-    // Everyting is set up, the finish listener can be started
-    pimpl_->collector_.reset(new boost::thread(CollectorThread, this));
-  }
-
-
-  BagOfRunnablesBySteps::~BagOfRunnablesBySteps()
-  {
-    if (pimpl_->continueCollector_)
-    {
-      LOG(ERROR) << "INTERNAL ERROR: BagOfRunnablesBySteps::Finalize() should be invoked manually to avoid mess in the destruction order!";
-      Finalize();
-    }
-  }
-
-
-  void BagOfRunnablesBySteps::Add(IRunnableBySteps* runnable)
-  {
-    // Make sure the runnable is deleted is something goes wrong
-    std::auto_ptr<IRunnableBySteps> runnableRabi(runnable);
-
-    boost::mutex::scoped_lock lock(pimpl_->mutex_);
-    boost::thread* t(new boost::thread(RunnableThread, this, runnable));
-
-    pimpl_->activeRunnables_.insert(std::make_pair(runnableRabi.release(), t));
-  }
-
-
-  void BagOfRunnablesBySteps::StopAll()
-  {
-    boost::mutex::scoped_lock lock(pimpl_->mutex_);
-    pimpl_->continueActiveRunnables_ = false;
-
-    while (pimpl_->activeRunnables_.size() > 0)
-    {
-      pimpl_->oneRunnableIsJoined_.wait(lock);
-    }
-
-    pimpl_->continueActiveRunnables_ = true;
-  }
-
-
-
-  void BagOfRunnablesBySteps::Finalize()
-  {
-    if (pimpl_->continueCollector_)
-    {
-      StopAll();
-
-      // Stop the finish listener
-      pimpl_->continueCollector_ = false;
-      pimpl_->oneRunnableHasStopped_.notify_one();  // Awakens the listener
-
-      if (pimpl_->collector_->joinable())
-      {
-        pimpl_->collector_->join();
-      }
-    }
-  }
-}
--- a/Core/MultiThreading/BagOfRunnablesBySteps.h	Tue Oct 06 11:12:13 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "IRunnableBySteps.h"
-
-#include <boost/noncopyable.hpp>
-#include <boost/shared_ptr.hpp>
-
-namespace Orthanc
-{
-  class BagOfRunnablesBySteps : public boost::noncopyable
-  {
-  private:
-    struct PImpl;
-    boost::shared_ptr<PImpl> pimpl_;
-
-    static void RunnableThread(BagOfRunnablesBySteps* bag,
-                               IRunnableBySteps* runnable);
-
-    static void CollectorThread(BagOfRunnablesBySteps* bag);
-
-  public:
-    BagOfRunnablesBySteps();
-
-    ~BagOfRunnablesBySteps();
-
-    void Add(IRunnableBySteps* runnable);
-
-    void StopAll();
-
-    void Finalize();
-  };
-}
--- a/Core/MultiThreading/IRunnableBySteps.h	Tue Oct 06 11:12:13 2015 +0200
+++ b/Core/MultiThreading/IRunnableBySteps.h	Tue Oct 06 14:09:30 2015 +0200
@@ -32,9 +32,11 @@
 
 #pragma once
 
+#include "../IDynamicObject.h"
+
 namespace Orthanc
 {
-  class IRunnableBySteps
+  class IRunnableBySteps : public IDynamicObject
   {
   public:
     virtual ~IRunnableBySteps()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/MultiThreading/RunnableWorkersPool.cpp	Tue Oct 06 14:09:30 2015 +0200
@@ -0,0 +1,157 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "RunnableWorkersPool.h"
+
+#include "SharedMessageQueue.h"
+#include "../OrthancException.h"
+#include "../Logging.h"
+
+namespace Orthanc
+{
+  struct RunnableWorkersPool::PImpl
+  {
+    class Worker
+    {
+    private:
+      const bool&           continue_;
+      SharedMessageQueue&   queue_;
+      boost::thread         thread_;
+ 
+      static void WorkerThread(Worker* that)
+      {
+        while (that->continue_)
+        {
+          std::auto_ptr<IDynamicObject>  obj(that->queue_.Dequeue(100));
+          if (obj.get() != NULL)
+          {
+            try
+            {
+              IRunnableBySteps& runnable = *dynamic_cast<IRunnableBySteps*>(obj.get());
+            
+              bool wishToContinue = runnable.Step();
+
+              if (wishToContinue)
+              {
+                // The runnable wishes to continue, reinsert it at the beginning of the queue
+                that->queue_.Enqueue(obj.release());
+              }
+            }
+            catch (OrthancException& e)
+            {
+              LOG(ERROR) << "Exception in a pool of working threads: " << e.What();
+            }
+          }
+        }
+      }
+
+    public:
+      Worker(const bool& globalContinue,
+             SharedMessageQueue& queue) : 
+        continue_(globalContinue),
+        queue_(queue)
+      {
+        thread_ = boost::thread(WorkerThread, this);
+      }
+
+      void Join()
+      {
+        if (thread_.joinable())
+        {
+          thread_.join();
+        }
+      }
+    };
+
+
+    bool                  continue_;
+    std::vector<Worker*>  workers_;
+    SharedMessageQueue    queue_;
+  };
+
+
+
+  RunnableWorkersPool::RunnableWorkersPool(size_t countWorkers) : pimpl_(new PImpl)
+  {
+    pimpl_->continue_ = true;
+
+    if (countWorkers <= 0)
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    pimpl_->workers_.resize(countWorkers);
+
+    for (size_t i = 0; i < countWorkers; i++)
+    {
+      pimpl_->workers_[i] = new PImpl::Worker(pimpl_->continue_, pimpl_->queue_);
+    }
+  }
+
+
+  void RunnableWorkersPool::Stop()
+  {
+    if (pimpl_->continue_)
+    {
+      pimpl_->continue_ = false;
+
+      for (size_t i = 0; i < pimpl_->workers_.size(); i++)
+      {
+        PImpl::Worker* worker = pimpl_->workers_[i];
+
+        if (worker != NULL)
+        {
+          worker->Join();
+          delete worker;
+        }
+      }
+    }
+  }
+
+
+  RunnableWorkersPool::~RunnableWorkersPool()
+  {
+    Stop();
+  }
+
+
+  void RunnableWorkersPool::Add(IRunnableBySteps* runnable)
+  {
+    if (!pimpl_->continue_)
+    {
+      throw OrthancException(ErrorCode_BadSequenceOfCalls);
+    }
+
+    pimpl_->queue_.Enqueue(runnable);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/MultiThreading/RunnableWorkersPool.h	Tue Oct 06 14:09:30 2015 +0200
@@ -0,0 +1,56 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IRunnableBySteps.h"
+
+#include <boost/shared_ptr.hpp>
+
+namespace Orthanc
+{
+  class RunnableWorkersPool : public boost::noncopyable
+  {
+  private:
+    struct PImpl;
+    boost::shared_ptr<PImpl> pimpl_;
+
+    void Stop();
+
+  public:
+    RunnableWorkersPool(size_t countWorkers);
+
+    ~RunnableWorkersPool();
+
+    void Add(IRunnableBySteps* runnable);  // Takes the ownership
+  };
+}
--- a/OrthancServer/DicomProtocol/DicomServer.cpp	Tue Oct 06 11:12:13 2015 +0200
+++ b/OrthancServer/DicomProtocol/DicomServer.cpp	Tue Oct 06 14:09:30 2015 +0200
@@ -40,6 +40,7 @@
 #include "../Internals/CommandDispatcher.h"
 #include "../OrthancInitialization.h"
 #include "EmbeddedResources.h"
+#include "../../Core/MultiThreading/RunnableWorkersPool.h"
 
 #include <boost/thread.hpp>
 
@@ -52,14 +53,13 @@
 {
   struct DicomServer::PImpl
   {
-    boost::thread thread_;
-
-    //std::set<
+    boost::thread  thread_;
+    T_ASC_Network *network_;
+    std::auto_ptr<RunnableWorkersPool>  workers_;
   };
 
 
-  void DicomServer::ServerThread(DicomServer* server,
-                                 T_ASC_Network *network)
+  void DicomServer::ServerThread(DicomServer* server)
   {
     LOG(INFO) << "DICOM server started";
 
@@ -67,20 +67,13 @@
     {
       /* receive an association and acknowledge or reject it. If the association was */
       /* acknowledged, offer corresponding services and invoke one or more if required. */
-      std::auto_ptr<Internals::CommandDispatcher> dispatcher(Internals::AcceptAssociation(*server, network));
+      std::auto_ptr<Internals::CommandDispatcher> dispatcher(Internals::AcceptAssociation(*server, server->pimpl_->network_));
 
       try
       {
         if (dispatcher.get() != NULL)
         {
-          if (server->isThreaded_)
-          {
-            server->bagOfDispatchers_.Add(dispatcher.release());
-          }
-          else
-          {
-            IRunnableBySteps::RunUntilDone(*dispatcher);
-          }
+          server->pimpl_->workers_->Add(dispatcher.release());
         }
       }
       catch (OrthancException& e)
@@ -90,19 +83,6 @@
     }
 
     LOG(INFO) << "DICOM server stopping";
-
-    if (server->isThreaded_)
-    {
-      server->bagOfDispatchers_.StopAll();
-    }
-
-    /* drop the network, i.e. free memory of T_ASC_Network* structure. This call */
-    /* is the counterpart of ASC_initializeNetwork(...) which was called above. */
-    OFCondition cond = ASC_dropNetwork(&network);
-    if (cond.bad())
-    {
-      LOG(ERROR) << "Error while dropping the network: " << cond.text();
-    }
   }
 
 
@@ -117,8 +97,7 @@
     applicationEntityFilter_ = NULL;
     checkCalledAet_ = true;
     clientTimeout_ = 30;
-    isThreaded_ = true;
-    continue_ = true;
+    continue_ = false;
   }
 
   DicomServer::~DicomServer()
@@ -141,17 +120,6 @@
     return port_;
   }
 
-  void DicomServer::SetThreaded(bool isThreaded)
-  {
-    Stop();
-    isThreaded_ = isThreaded;
-  }
-
-  bool DicomServer::IsThreaded() const
-  {
-    return isThreaded_;
-  }
-
   void DicomServer::SetClientTimeout(uint32_t timeout)
   {
     Stop();
@@ -305,9 +273,8 @@
     Stop();
 
     /* initialize network, i.e. create an instance of T_ASC_Network*. */
-    T_ASC_Network *network;
     OFCondition cond = ASC_initializeNetwork
-      (NET_ACCEPTOR, OFstatic_cast(int, port_), /*opt_acse_timeout*/ 30, &network);
+      (NET_ACCEPTOR, OFstatic_cast(int, port_), /*opt_acse_timeout*/ 30, &pimpl_->network_);
     if (cond.bad())
     {
       LOG(ERROR) << "cannot create network: " << cond.text();
@@ -315,7 +282,8 @@
     }
 
     continue_ = true;
-    pimpl_->thread_ = boost::thread(ServerThread, this, network);
+    pimpl_->workers_.reset(new RunnableWorkersPool(4));   // Use 4 workers - TODO as a parameter?
+    pimpl_->thread_ = boost::thread(ServerThread, this);
   }
 
 
@@ -330,7 +298,15 @@
         pimpl_->thread_.join();
       }
 
-      bagOfDispatchers_.Finalize();
+      pimpl_->workers_.reset(NULL);
+
+      /* drop the network, i.e. free memory of T_ASC_Network* structure. This call */
+      /* is the counterpart of ASC_initializeNetwork(...) which was called above. */
+      OFCondition cond = ASC_dropNetwork(&pimpl_->network_);
+      if (cond.bad())
+      {
+        LOG(ERROR) << "Error while dropping the network: " << cond.text();
+      }
     }
   }
 
--- a/OrthancServer/DicomProtocol/DicomServer.h	Tue Oct 06 11:12:13 2015 +0200
+++ b/OrthancServer/DicomProtocol/DicomServer.h	Tue Oct 06 14:09:30 2015 +0200
@@ -36,12 +36,10 @@
 #include "IMoveRequestHandlerFactory.h"
 #include "IStoreRequestHandlerFactory.h"
 #include "IApplicationEntityFilter.h"
-#include "../../Core/MultiThreading/BagOfRunnablesBySteps.h"
 
 #include <boost/shared_ptr.hpp>
 #include <boost/noncopyable.hpp>
 
-struct T_ASC_Network;
 
 namespace Orthanc
 {
@@ -57,16 +55,12 @@
     bool continue_;
     bool started_;
     uint32_t clientTimeout_;
-    bool isThreaded_;
     IFindRequestHandlerFactory* findRequestHandlerFactory_;
     IMoveRequestHandlerFactory* moveRequestHandlerFactory_;
     IStoreRequestHandlerFactory* storeRequestHandlerFactory_;
     IApplicationEntityFilter* applicationEntityFilter_;
 
-    BagOfRunnablesBySteps bagOfDispatchers_;  // This is used iff the server is threaded
-
-    static void ServerThread(DicomServer* server,
-                             T_ASC_Network *net);
+    static void ServerThread(DicomServer* server);
 
   public:
     DicomServer();
@@ -76,9 +70,6 @@
     void SetPortNumber(uint16_t port);
     uint16_t GetPortNumber() const;
 
-    void SetThreaded(bool isThreaded);
-    bool IsThreaded() const;
-
     void SetClientTimeout(uint32_t timeout);
     uint32_t GetClientTimeout() const;
 
--- a/UnitTestsSources/MultiThreadingTests.cpp	Tue Oct 06 11:12:13 2015 +0200
+++ b/UnitTestsSources/MultiThreadingTests.cpp	Tue Oct 06 14:09:30 2015 +0200
@@ -183,7 +183,7 @@
       outputs.push_back(boost::lexical_cast<std::string>(b));
     }
 
-    Toolbox::USleep(100000);
+    Toolbox::USleep(30000);
 
     return true;
   }
@@ -202,7 +202,7 @@
     {
       printf(">> %s: %0.1f\n", it->c_str(), 100.0f * s->GetProgress(*it));
     }
-    Toolbox::USleep(10000);
+    Toolbox::USleep(3000);
   }
 }