Blender V2.61 - r43446

util_thread.h

Go to the documentation of this file.
00001 /*
00002  * Copyright 2011, Blender Foundation.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 #ifndef __UTIL_THREAD_H__
00020 #define __UTIL_THREAD_H__
00021 
00022 #include <boost/thread.hpp>
00023 #include <queue>
00024 
00025 CCL_NAMESPACE_BEGIN
00026 
00027 #if 0
00028 
00029 /* Use STL for threading */
00030 
00031 using std::thread;
00032 using std::thread_mutex;
00033 typedef std::lock_guard thread_scoped_lock;
00034 using std::condition_variable;
00035 
00036 #else
00037 
00038 /* Use boost for threading */
00039 
00040 using boost::thread;
00041 typedef boost::mutex thread_mutex;
00042 typedef boost::mutex::scoped_lock thread_scoped_lock;
00043 typedef boost::condition_variable thread_condition_variable;
00044 
00045 #endif
00046 
00047 /* Thread Safe Queue to pass tasks from one thread to another. Tasks should be
00048  * pushed into the queue, while the worker thread waits to pop the next task
00049  * off the queue. Once all tasks are into the queue, calling stop() will stop
00050  * the worker threads from waiting for more tasks once all tasks are done. */
00051 
00052 template<typename T> class ThreadQueue
00053 {
00054 public:
00055     ThreadQueue()
00056     {
00057         tot = 0;
00058         tot_done = 0;
00059         do_stop = false;
00060         do_cancel = false;
00061     }
00062 
00063     /* Main thread functions */
00064 
00065     /* push a task to be executed */
00066     void push(const T& value)
00067     {
00068         thread_scoped_lock lock(queue_mutex);
00069         queue.push(value);
00070         tot++;
00071         lock.unlock();
00072 
00073         queue_cond.notify_one();
00074     }
00075 
00076     /* wait until all tasks are done */
00077     void wait_done()
00078     {
00079         thread_scoped_lock lock(done_mutex);
00080 
00081         while(tot_done != tot)
00082             done_cond.wait(lock);
00083     }
00084 
00085     /* stop all worker threads */
00086     void stop()
00087     {
00088         clear();
00089         do_stop = true;
00090         queue_cond.notify_all();
00091     }
00092 
00093     /* cancel all tasks, but keep worker threads running */
00094     void cancel()
00095     {
00096         clear();
00097         do_cancel = true;
00098         wait_done();
00099         do_cancel = false;
00100     }
00101 
00102     /* Worker thread functions
00103      *
00104      * while(queue.worker_wait_pop(task)) {
00105      *      for(..) {
00106      *          ... do work ...
00107      *
00108      *          if(queue.worker_cancel())
00109      *              break;
00110      *      }
00111      *      
00112      *      queue.worker_done();
00113      * }
00114      */
00115 
00116     bool worker_wait_pop(T& value)
00117     {
00118         thread_scoped_lock lock(queue_mutex);
00119 
00120         while(queue.empty() && !do_stop)
00121             queue_cond.wait(lock);
00122 
00123         if(queue.empty())
00124             return false;
00125         
00126         value = queue.front();
00127         queue.pop();
00128 
00129         return true;
00130     }
00131 
00132     void worker_done()
00133     {
00134         thread_scoped_lock lock(done_mutex);
00135         tot_done++;
00136         lock.unlock();
00137 
00138         assert(tot_done <= tot);
00139 
00140         done_cond.notify_all();
00141     }
00142 
00143     bool worker_cancel()
00144     {
00145         return do_cancel;
00146     }
00147 
00148 protected:
00149     void clear()
00150     {
00151         thread_scoped_lock lock(queue_mutex);
00152 
00153         while(!queue.empty()) {
00154             thread_scoped_lock done_lock(done_mutex);
00155             tot_done++;
00156             done_lock.unlock();
00157 
00158             queue.pop();
00159         }
00160 
00161         done_cond.notify_all();
00162     }
00163 
00164     std::queue<T> queue;
00165     thread_mutex queue_mutex;
00166     thread_mutex done_mutex;
00167     thread_condition_variable queue_cond;
00168     thread_condition_variable done_cond;
00169     volatile bool do_stop;
00170     volatile bool do_cancel;
00171     volatile int tot, tot_done;
00172 };
00173 
00174 /* Thread Local Storage
00175  *
00176  * Boost implementation is a bit slow, and Mac OS X __thread is not supported
00177  * but the pthreads implementation is optimized, so we use these macros. */
00178 
00179 #ifdef __APPLE__
00180 
00181 #define tls_ptr(type, name) \
00182     pthread_key_t name
00183 #define tls_set(name, value) \
00184     pthread_setspecific(name, value)
00185 #define tls_get(type, name) \
00186     ((type*)pthread_getspecific(name))
00187 #define tls_create(type, name) \
00188     pthread_key_create(&name, NULL)
00189 #define tls_delete(type, name) \
00190     pthread_key_delete(name);
00191 
00192 #else
00193 
00194 #ifdef __WIN32
00195 #define __thread __declspec(thread)
00196 #endif
00197 
00198 #define tls_ptr(type, name) \
00199     __thread type *name
00200 #define tls_set(name, value) \
00201     name = value
00202 #define tls_get(type, name) \
00203     name
00204 #define tls_create(type, name)
00205 #define tls_delete(type, name)
00206 
00207 #endif
00208 
00209 CCL_NAMESPACE_END
00210 
00211 #endif /* __UTIL_THREAD_H__ */
00212