213 lines
4.2 KiB
C
213 lines
4.2 KiB
C
|
/*
|
||
|
* Copyright 2011, Blender Foundation.
|
||
|
*
|
||
|
* 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 2
|
||
|
* of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* 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, write to the Free Software Foundation,
|
||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
#ifndef __UTIL_THREAD_H__
|
||
|
#define __UTIL_THREAD_H__
|
||
|
|
||
|
#include <boost/thread.hpp>
|
||
|
#include <queue>
|
||
|
|
||
|
CCL_NAMESPACE_BEGIN
|
||
|
|
||
|
#if 0
|
||
|
|
||
|
/* Use STL for threading */
|
||
|
|
||
|
using std::thread;
|
||
|
using std::thread_mutex;
|
||
|
typedef std::lock_guard thread_scoped_lock;
|
||
|
using std::condition_variable;
|
||
|
|
||
|
#else
|
||
|
|
||
|
/* Use boost for threading */
|
||
|
|
||
|
using boost::thread;
|
||
|
typedef boost::mutex thread_mutex;
|
||
|
typedef boost::mutex::scoped_lock thread_scoped_lock;
|
||
|
typedef boost::condition_variable thread_condition_variable;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/* Thread Safe Queue to pass tasks from one thread to another. Tasks should be
|
||
|
* pushed into the queue, while the worker thread waits to pop the next task
|
||
|
* off the queue. Once all tasks are into the queue, calling stop() will stop
|
||
|
* the worker threads from waiting for more tasks once all tasks are done. */
|
||
|
|
||
|
template<typename T> class ThreadQueue
|
||
|
{
|
||
|
public:
|
||
|
ThreadQueue()
|
||
|
{
|
||
|
tot = 0;
|
||
|
tot_done = 0;
|
||
|
do_stop = false;
|
||
|
do_cancel = false;
|
||
|
}
|
||
|
|
||
|
/* Main thread functions */
|
||
|
|
||
|
/* push a task to be executed */
|
||
|
void push(const T& value)
|
||
|
{
|
||
|
thread_scoped_lock lock(queue_mutex);
|
||
|
queue.push(value);
|
||
|
tot++;
|
||
|
lock.unlock();
|
||
|
|
||
|
queue_cond.notify_one();
|
||
|
}
|
||
|
|
||
|
/* wait until all tasks are done */
|
||
|
void wait_done()
|
||
|
{
|
||
|
thread_scoped_lock lock(done_mutex);
|
||
|
|
||
|
while(tot_done != tot)
|
||
|
done_cond.wait(lock);
|
||
|
}
|
||
|
|
||
|
/* stop all worker threads */
|
||
|
void stop()
|
||
|
{
|
||
|
clear();
|
||
|
do_stop = true;
|
||
|
queue_cond.notify_all();
|
||
|
}
|
||
|
|
||
|
/* cancel all tasks, but keep worker threads running */
|
||
|
void cancel()
|
||
|
{
|
||
|
clear();
|
||
|
do_cancel = true;
|
||
|
wait_done();
|
||
|
do_cancel = false;
|
||
|
}
|
||
|
|
||
|
/* Worker thread functions
|
||
|
*
|
||
|
* while(queue.worker_wait_pop(task)) {
|
||
|
* for(..) {
|
||
|
* ... do work ...
|
||
|
*
|
||
|
* if(queue.worker_cancel())
|
||
|
* break;
|
||
|
* }
|
||
|
*
|
||
|
* queue.worker_done();
|
||
|
* }
|
||
|
*/
|
||
|
|
||
|
bool worker_wait_pop(T& value)
|
||
|
{
|
||
|
thread_scoped_lock lock(queue_mutex);
|
||
|
|
||
|
while(queue.empty() && !do_stop)
|
||
|
queue_cond.wait(lock);
|
||
|
|
||
|
if(queue.empty())
|
||
|
return false;
|
||
|
|
||
|
value = queue.front();
|
||
|
queue.pop();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void worker_done()
|
||
|
{
|
||
|
thread_scoped_lock lock(done_mutex);
|
||
|
tot_done++;
|
||
|
lock.unlock();
|
||
|
|
||
|
assert(tot_done <= tot);
|
||
|
|
||
|
done_cond.notify_all();
|
||
|
}
|
||
|
|
||
|
bool worker_cancel()
|
||
|
{
|
||
|
return do_cancel;
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
void clear()
|
||
|
{
|
||
|
thread_scoped_lock lock(queue_mutex);
|
||
|
|
||
|
while(!queue.empty()) {
|
||
|
thread_scoped_lock done_lock(done_mutex);
|
||
|
tot_done++;
|
||
|
done_lock.unlock();
|
||
|
|
||
|
queue.pop();
|
||
|
}
|
||
|
|
||
|
done_cond.notify_all();
|
||
|
}
|
||
|
|
||
|
std::queue<T> queue;
|
||
|
thread_mutex queue_mutex;
|
||
|
thread_mutex done_mutex;
|
||
|
thread_condition_variable queue_cond;
|
||
|
thread_condition_variable done_cond;
|
||
|
volatile bool do_stop;
|
||
|
volatile bool do_cancel;
|
||
|
volatile int tot, tot_done;
|
||
|
};
|
||
|
|
||
|
/* Thread Local Storage
|
||
|
*
|
||
|
* Boost implementation is a bit slow, and Mac OS X __thread is not supported
|
||
|
* but the pthreads implementation is optimized, so we use these macros. */
|
||
|
|
||
|
#ifdef __APPLE__
|
||
|
|
||
|
#define tls_ptr(type, name) \
|
||
|
pthread_key_t name
|
||
|
#define tls_set(name, value) \
|
||
|
pthread_setspecific(name, value)
|
||
|
#define tls_get(type, name) \
|
||
|
((type*)pthread_getspecific(name))
|
||
|
#define tls_create(type, name) \
|
||
|
pthread_key_create(&name, NULL)
|
||
|
#define tls_delete(type, name) \
|
||
|
pthread_key_delete(name);
|
||
|
|
||
|
#else
|
||
|
|
||
|
#ifdef __WIN32
|
||
|
#define __thread __declspec(thread)
|
||
|
#endif
|
||
|
|
||
|
#define tls_ptr(type, name) \
|
||
|
__thread type *name
|
||
|
#define tls_set(name, value) \
|
||
|
name = value
|
||
|
#define tls_get(type, name) \
|
||
|
name
|
||
|
#define tls_create(type, name)
|
||
|
#define tls_delete(type, name)
|
||
|
|
||
|
#endif
|
||
|
|
||
|
CCL_NAMESPACE_END
|
||
|
|
||
|
#endif /* __UTIL_THREAD_H__ */
|
||
|
|