aboutsummaryrefslogtreecommitdiff
path: root/dep/g3dlite/include/G3D/GThread.h
blob: 8670fef4e761996bcf24a693380030e2e9a343a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
/** 
  @file GThread.h
 
  @created 2005-09-22
  @edited  2010-09-10

 */

#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_WINDOWS
#   include <pthread.h>
#   include <signal.h>
#endif


namespace G3D {
        
typedef shared_ptr<class GThread> GThreadRef;



/**
 Platform independent thread implementation.  You can either subclass and 
 override GThread::threadMain or call the create method with a method.

 Beware of reference counting and threads.  If circular references exist between
 GThread subclasses then neither class will ever be deallocated.  Also, 
 dropping all pointers (and causing deallocation) of a GThread does NOT 
 stop the underlying process.

 \sa G3D::GMutex, G3D::Spinlock, G3D::AtomicInt32, G3D::ThreadSet
*/
class GThread : public ReferenceCountedObject {
private:
    // "Status" is a reserved word on FreeBSD
    enum GStatus {STATUS_CREATED, STATUS_STARTED, STATUS_RUNNING, STATUS_COMPLETED};

    // Not implemented on purpose, don't use
    GThread(const GThread &);
    GThread& operator=(const GThread&);
    bool operator==(const GThread&);

#ifdef G3D_WINDOWS
    static DWORD WINAPI internalThreadProc(LPVOID param);
#else
    static void* internalThreadProc(void* param);
#endif //G3D_WINDOWS

    volatile GStatus     m_status;

    // Thread handle to hold HANDLE and pthread_t
#ifdef G3D_WINDOWS
    HANDLE              m_handle;
    HANDLE              m_event;
#else
    pthread_t           m_handle;
#endif //G3D_WINDOWS

    std::string         m_name;

protected:

    /** Overriden by the thread implementor */
    virtual void threadMain() = 0;

public:

    /** 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);

    virtual ~GThread();

    /** Constructs a basic GThread without requiring a subclass.

        @param proc The global or static function for the threadMain() */
    static GThreadRef create(const std::string& name, void (*proc)(void*), void* param = NULL);

    /** Starts the thread and executes threadMain().  Returns false if
       the thread failed to start (either because it was already started
       or because the OS refused).
       
       @param behavior If USE_CURRENT_THREAD, rather than spawning a new thread, this routine
       runs threadMain on the current thread.
       */
    bool start(SpawnBehavior behavior = USE_NEW_THREAD);

    /** Terminates the thread without notifying or
        waiting for a cancelation point. */
    void terminate();

    /**
        Returns true if threadMain is currently executing.  This will
        only be set when the thread is actually running and might not
        be set when start() returns. */
    bool running() const;

    /** True after start() has been called, even through the thread
        may have already completed(), or be currently running().*/
    bool started() const;

    /** Returns true if the thread has exited. */
    bool completed() const;

    /** Waits for the thread to finish executing. */
    void waitForCompletion();

    /** Returns thread 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