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
|