diff options
Diffstat (limited to 'dep/g3dlite/include/G3D/GThread.h')
-rw-r--r-- | dep/g3dlite/include/G3D/GThread.h | 177 |
1 files changed, 163 insertions, 14 deletions
diff --git a/dep/g3dlite/include/G3D/GThread.h b/dep/g3dlite/include/G3D/GThread.h index 58437efc3fb..8670fef4e76 100644 --- a/dep/g3dlite/include/G3D/GThread.h +++ b/dep/g3dlite/include/G3D/GThread.h @@ -2,18 +2,21 @@ @file GThread.h @created 2005-09-22 - @edited 2007-01-31 + @edited 2010-09-10 */ -#ifndef G3D_GTHREAD_H -#define G3D_GTHREAD_H +#ifndef G3D_GThread_h +#define G3D_GThread_h #include "G3D/platform.h" #include "G3D/ReferenceCount.h" +#include "G3D/ThreadSet.h" +#include "G3D/Vector2int32.h" +#include "G3D/SpawnBehavior.h" #include <string> -#ifndef G3D_WIN32 +#ifndef G3D_WINDOWS # include <pthread.h> # include <signal.h> #endif @@ -21,7 +24,9 @@ namespace G3D { -typedef ReferenceCountedPointer<class GThread> GThreadRef; +typedef shared_ptr<class GThread> GThreadRef; + + /** Platform independent thread implementation. You can either subclass and @@ -32,11 +37,11 @@ typedef ReferenceCountedPointer<class GThread> GThreadRef; dropping all pointers (and causing deallocation) of a GThread does NOT stop the underlying process. - @sa G3D::GMutex, G3D::Spinlock, G3D::AtomicInt32 + \sa G3D::GMutex, G3D::Spinlock, G3D::AtomicInt32, G3D::ThreadSet */ class GThread : public ReferenceCountedObject { private: - // "Status" is a reserved work on FreeBSD + // "Status" is a reserved word on FreeBSD enum GStatus {STATUS_CREATED, STATUS_STARTED, STATUS_RUNNING, STATUS_COMPLETED}; // Not implemented on purpose, don't use @@ -44,21 +49,21 @@ private: GThread& operator=(const GThread&); bool operator==(const GThread&); -#ifdef G3D_WIN32 +#ifdef G3D_WINDOWS static DWORD WINAPI internalThreadProc(LPVOID param); #else static void* internalThreadProc(void* param); -#endif //G3D_WIN32 +#endif //G3D_WINDOWS volatile GStatus m_status; // Thread handle to hold HANDLE and pthread_t -#ifdef G3D_WIN32 +#ifdef G3D_WINDOWS HANDLE m_handle; HANDLE m_event; #else pthread_t m_handle; -#endif //G3D_WIN32 +#endif //G3D_WINDOWS std::string m_name; @@ -68,8 +73,11 @@ protected: virtual void threadMain() = 0; public: - typedef ReferenceCountedPointer<class GThread> Ref; - enum SpawnBehavior {USE_NEW_THREAD, USE_CURRENT_THREAD}; + + /** Returns System::numCores(); put here to break a dependence on System.h */ + static int numCores(); + + typedef shared_ptr<class GThread> Ref; GThread(const std::string& name); @@ -110,12 +118,153 @@ public: void waitForCompletion(); /** Returns thread name */ - inline const std::string& name() { + const std::string& name() { return m_name; } + + /** For backwards compatibility to G3D 8.xx */ + static const SpawnBehavior USE_CURRENT_THREAD = G3D::USE_CURRENT_THREAD; + /** For backwards compatibility to G3D 8.xx */ + static const SpawnBehavior USE_NEW_THREAD = G3D::USE_NEW_THREAD; + + enum { + /** Tells GThread::runConcurrently() and GThread::runConcurrently2D() to + use System::numCores() threads.*/ + NUM_CORES = -100 + }; + + /** + \brief Iterates over a 2D region using multiple threads and + blocks until all threads have completed. <p> Evaluates \a + object->\a method(\a x, \a y) for every <code>start.x <= x < + upTo.x</code> and <code>start.y <= y < upTo.y</code>. + Iteration is row major, so each thread can expect to see + successive x values. </p> + \param maxThreads + Maximum number of threads to use. By default at most one + thread per processor core will be used. + + Example: + + \code + class RayTracer { + public: + void trace(const Vector2int32& pixel) { + ... + } + + void traceAll() { + GThread::runConcurrently2D(Point2int32(0,0), Point2int32(w,h), this, &RayTracer::trace); + } + }; + \endcode + */ + template<class Class> + static void runConcurrently2D + (const Vector2int32& start, + const Vector2int32& upTo, + Class* object, + void (Class::*method)(int x, int y), + int maxThreads = NUM_CORES) { + _internal_runConcurrently2DHelper(start, upTo, object, method, static_cast<void (Class::*)(int, int, int)>(NULL), maxThreads); + } + + /** Like the other version of runConcurrently2D, but tells the + method the thread index that it is running on. That enables + the caller to manage per-thread state. + */ + template<class Class> + static void runConcurrently2D + (const Vector2int32& start, + const Vector2int32& upTo, + Class* object, + void (Class::*method)(int x, int y, int threadID), + int maxThreads = NUM_CORES) { + _internal_runConcurrently2DHelper(start, upTo, object, static_cast<void (Class::*)(int, int)>(NULL), method, maxThreads); + } + }; + + // Can't use an inherited class inside of its parent on g++ 4.2.1 if it is later a template parameter + + /** For use by runConcurrently2D. Designed for arbitrary iteration, although only used for + interlaced rows in the current implementation. */ + template<class Class> + class _internalGThreadWorker : public GThread { + public: + /** Start for this thread, which differs from the others */ + const int threadID; + const Vector2int32 start; + const Vector2int32 upTo; + const Vector2int32 stride; + Class* object; + void (Class::*method1)(int x, int y); + void (Class::*method2)(int x, int y, int threadID); + + _internalGThreadWorker(int threadID, + const Vector2int32& start, + const Vector2int32& upTo, + Class* object, + void (Class::*method1)(int x, int y), + void (Class::*method2)(int x, int y, int threadID), + const Vector2int32& stride) : + GThread("runConcurrently2D worker"), + threadID(threadID), + start(start), + upTo(upTo), + stride(stride), + object(object), + method1(method1), + method2(method2) {} + + virtual void threadMain() { + for (int y = start.y; y < upTo.y; y += stride.y) { + // Run whichever method was provided + if (method1) { + for (int x = start.x; x < upTo.x; x += stride.x) { + (object->*method1)(x, y); + } + } else { + for (int x = start.x; x < upTo.x; x += stride.x) { + (object->*method2)(x, y, threadID); + } + } + } + } + }; + + + template<class Class> + void _internal_runConcurrently2DHelper + (const Vector2int32& start, + const Vector2int32& upTo, + Class* object, + void (Class::*method1)(int x, int y), + void (Class::*method2)(int x, int y, int threadID), + int maxThreads) { + + // Create a group of threads + if (maxThreads == GThread::NUM_CORES) { + maxThreads = GThread::numCores(); + } + + const int numRows = upTo.y - start.y; + const int numThreads = min(maxThreads, numRows); + const Vector2int32 stride(1, numThreads); + ThreadSet threadSet; + for (int t = 0; t < numThreads; ++t) { + threadSet.insert(shared_ptr<_internalGThreadWorker<Class> >(new _internalGThreadWorker<Class>(t, start + Vector2int32(0, t), upTo, object, method1, method2, stride))); + } + + // Run the threads, reusing the current thread and blocking until + // all complete + threadSet.start(USE_CURRENT_THREAD); + threadSet.waitForCompletion(); + } + + } // namespace G3D #endif //G3D_GTHREAD_H |