| [282] | 1 | /* $Id$ $URL$ */ |
|---|
| 2 | |
|---|
| 3 | /**\file threadman.cpp |
|---|
| 4 | * Definition of ThreadMan |
|---|
| 5 | */ |
|---|
| 6 | |
|---|
| 7 | #include "threadman.h" |
|---|
| [661] | 8 | |
|---|
| 9 | #include <SDL_thread.h> |
|---|
| [662] | 10 | #include <SDL_timer.h> |
|---|
| [661] | 11 | |
|---|
| [282] | 12 | #include <algorithm> |
|---|
| 13 | #include <functional> |
|---|
| [285] | 14 | #include <map> |
|---|
| 15 | #include <queue> |
|---|
| [598] | 16 | #include <assert.h> |
|---|
| [282] | 17 | |
|---|
| [289] | 18 | #ifdef __unix__ |
|---|
| 19 | #include <unistd.h> |
|---|
| 20 | #else |
|---|
| 21 | # ifdef __WIN32__ |
|---|
| 22 | # include <windows.h> |
|---|
| [594] | 23 | # ifndef IGNORE_WIN32_EXCEPTIONS |
|---|
| 24 | # include <winbase.h> |
|---|
| 25 | # include <winnt.h> |
|---|
| 26 | # include <excpt.h> |
|---|
| 27 | # include <time.h> |
|---|
| 28 | # endif |
|---|
| [289] | 29 | # else |
|---|
| 30 | # include <stdio.h> |
|---|
| 31 | # endif |
|---|
| 32 | #endif |
|---|
| 33 | |
|---|
| [624] | 34 | #ifdef _MSC_VER |
|---|
| 35 | //this needs some work yet for Microsoft compilers |
|---|
| 36 | #define IGNORE_WIN32_EXCEPTIONS |
|---|
| 37 | #endif |
|---|
| 38 | |
|---|
| 39 | #if defined __WIN32__ && !defined IGNORE_WIN32_EXCEPTIONS |
|---|
| 40 | #ifndef _MSC_VER |
|---|
| [594] | 41 | #warning "Using Win32 signal/exception handler for threads in threadman" |
|---|
| 42 | #warning "define IGNORE_WIN32_EXCEPTIONS if you don't want this" |
|---|
| [624] | 43 | #endif |
|---|
| [594] | 44 | extern "C" { |
|---|
| 45 | |
|---|
| [613] | 46 | static const int KILLTHREAD = 1; ///< Call ExitThread when an exception is caught |
|---|
| [595] | 47 | |
|---|
| [594] | 48 | static |
|---|
| 49 | EXCEPTION_DISPOSITION |
|---|
| 50 | win32_threadsignal_handler(struct _EXCEPTION_RECORD* er, |
|---|
| 51 | void* establisher_frame, /* establisher frame */ |
|---|
| 52 | struct _CONTEXT* context_record, /* context record */ |
|---|
| 53 | void* dispatcher_context) { /* dispatcher context */ |
|---|
| 54 | /* |
|---|
| 55 | typedef struct _EXCEPTION_RECORD { |
|---|
| 56 | DWORD ExceptionCode; |
|---|
| 57 | DWORD ExceptionFlags; |
|---|
| 58 | struct _EXCEPTION_RECORD *ExceptionRecord; |
|---|
| 59 | PVOID ExceptionAddress; |
|---|
| 60 | DWORD NumberParameters; |
|---|
| 61 | DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; |
|---|
| 62 | } EXCEPTION_RECORD,*PEXCEPTION_RECORD,*LPEXCEPTION_RECORD; |
|---|
| 63 | */ |
|---|
| 64 | |
|---|
| 65 | FILE* err = fopen("exception_log.txt", "a"); |
|---|
| 66 | time_t now = time(0); |
|---|
| 67 | if (!err) |
|---|
| 68 | err = stderr; |
|---|
| [595] | 69 | fprintf(err, "===> %s\t Caught a \"signal\" exception in Win32 at @0x%p: Code = 0x%x, Flags = 0x%x\n", |
|---|
| [594] | 70 | ctime(&now), er->ExceptionAddress, (unsigned)er->ExceptionCode, (unsigned)er->ExceptionFlags ); |
|---|
| [595] | 71 | fprintf(err, "\tSDL_ThreadID() = %lu\n", (unsigned long)SDL_ThreadID()); |
|---|
| [594] | 72 | |
|---|
| 73 | if (er->ExceptionFlags & 1) |
|---|
| 74 | fprintf(err, "\tEH_NONCONTINUABLE\n"); |
|---|
| 75 | if (er->ExceptionFlags & 2) |
|---|
| 76 | fprintf(err, "\tEH_UNWINDING\n"); |
|---|
| 77 | if (er->ExceptionFlags & 4) |
|---|
| 78 | fprintf(err, "\tEH_EXIT_UNWIND\n"); |
|---|
| 79 | if (er->ExceptionFlags & 8) |
|---|
| 80 | fprintf(err, "\tEH_STACK_INVALID\n"); |
|---|
| 81 | if (er->ExceptionFlags & 0x10) |
|---|
| 82 | fprintf(err, "\tEH_NESTED_CALL\n"); |
|---|
| 83 | |
|---|
| 84 | const char* strcode = "Unknown"; |
|---|
| 85 | switch (er->ExceptionCode) { |
|---|
| 86 | case EXCEPTION_ACCESS_VIOLATION : strcode = "ACCESS_VIOLATION"; break; |
|---|
| 87 | case EXCEPTION_DATATYPE_MISALIGNMENT : strcode = "DATATYPE_MISALIGNMENT"; break; |
|---|
| 88 | case EXCEPTION_BREAKPOINT : strcode = "BREAKPOINT"; break; |
|---|
| 89 | case EXCEPTION_SINGLE_STEP : strcode = "SINGLE_STEP"; break; |
|---|
| 90 | case EXCEPTION_ARRAY_BOUNDS_EXCEEDED : strcode = "ARRAY_BOUNDS_EXCEEDED"; break; |
|---|
| 91 | case EXCEPTION_FLT_DENORMAL_OPERAND : strcode = "FLOAT_DENORMAL_OPERAND"; break; |
|---|
| 92 | case EXCEPTION_FLT_DIVIDE_BY_ZERO : strcode = "FLOAT_DIVIDE_BY_ZERO"; break; |
|---|
| 93 | case EXCEPTION_FLT_INEXACT_RESULT : strcode = "FLOAT_INEXACT_RESULT"; break; |
|---|
| 94 | case EXCEPTION_FLT_INVALID_OPERATION : strcode = "FLOAT_INVALID_OPERATION"; break; |
|---|
| 95 | case EXCEPTION_FLT_OVERFLOW : strcode = "FLOAT_OVERFLOW"; break; |
|---|
| 96 | case EXCEPTION_FLT_STACK_CHECK : strcode = "FLOAT_STACK_CHECK"; break; |
|---|
| 97 | case EXCEPTION_FLT_UNDERFLOW : strcode = "FLOAT_UNDERFLOW"; break; |
|---|
| 98 | case EXCEPTION_INT_DIVIDE_BY_ZERO : strcode = "INTEGER_DIVIDE_BY_ZERO"; break; |
|---|
| 99 | case EXCEPTION_INT_OVERFLOW : strcode = "INTEGER_OVERFLOW"; break; |
|---|
| 100 | case EXCEPTION_PRIV_INSTRUCTION : strcode = "PRIVILEGED_INSTRUCTION"; break; |
|---|
| 101 | case EXCEPTION_IN_PAGE_ERROR : strcode = "IN_PAGE_ERROR"; break; |
|---|
| 102 | case EXCEPTION_ILLEGAL_INSTRUCTION : strcode = "ILLEGAL_INSTRUCTION"; break; |
|---|
| 103 | case EXCEPTION_NONCONTINUABLE_EXCEPTION : strcode = "NONCONTINUABLE_EXCEPTION"; break; |
|---|
| 104 | case EXCEPTION_STACK_OVERFLOW : strcode = "STACK_OVERFLOW"; break; |
|---|
| 105 | case EXCEPTION_INVALID_DISPOSITION : strcode = "INVALID_DISPOSITION"; break; |
|---|
| 106 | case EXCEPTION_GUARD_PAGE : strcode = "GUARD_PAGE_VIOLATION"; break; |
|---|
| 107 | case EXCEPTION_INVALID_HANDLE : strcode = "INVALID_HANDLE"; break; |
|---|
| 108 | } |
|---|
| 109 | |
|---|
| 110 | fprintf(err, "\tCode type: %s\n", strcode); |
|---|
| 111 | |
|---|
| [595] | 112 | if (KILLTHREAD) { |
|---|
| 113 | fprintf(err, "\tI'm going to try killing the thread... This might utterly break things\n"); |
|---|
| 114 | } |
|---|
| 115 | fprintf(err, "\n"); |
|---|
| 116 | |
|---|
| [594] | 117 | fflush(err); |
|---|
| 118 | if (err != stderr) |
|---|
| 119 | fclose(err); |
|---|
| 120 | |
|---|
| [595] | 121 | |
|---|
| 122 | if (KILLTHREAD) { |
|---|
| 123 | ExitThread(er->ExceptionCode); |
|---|
| 124 | return ExceptionContinueExecution; |
|---|
| 125 | } |
|---|
| 126 | |
|---|
| [594] | 127 | return ExceptionContinueSearch; |
|---|
| 128 | /* Options are: |
|---|
| 129 | ExceptionContinueExecution |
|---|
| 130 | ExceptionContinueSearch |
|---|
| 131 | ExceptionNestedException |
|---|
| 132 | ExceptionCollidedUnwind |
|---|
| 133 | */ |
|---|
| 134 | } |
|---|
| 135 | |
|---|
| 136 | } |
|---|
| 137 | #define BEGIN_HANDLE_WIN32_SIGNALS() __try1(win32_threadsignal_handler) |
|---|
| 138 | #define END_HANDLE_WIN32_SIGNALS() __except1 |
|---|
| 139 | #else |
|---|
| 140 | #define BEGIN_HANDLE_WIN32_SIGNALS() |
|---|
| 141 | #define END_HANDLE_WIN32_SIGNALS() |
|---|
| 142 | #endif |
|---|
| 143 | |
|---|
| [661] | 144 | namespace { |
|---|
| 145 | /** Function data */ |
|---|
| 146 | struct FnData { |
|---|
| [687] | 147 | ThreadMan::THREAD fn; ///< The *actual* thread entry point |
|---|
| [661] | 148 | void * data; ///< Data passed to the thread by \a run() |
|---|
| 149 | SDL_Thread *thr; ///< Handle on the thread |
|---|
| 150 | SDL_sem *startup_sem; ///< Semaphore to indicate to thread that it can call thread_done |
|---|
| 151 | ThreadMan *tman; ///< Constructor of this data |
|---|
| 152 | ThreadMan::PRIORITY pri; ///< The priority to set the thread to, once started |
|---|
| [687] | 153 | ThreadMan::Runner *runner; ///< If fn == NULL, use runner->run(data) |
|---|
| [661] | 154 | |
|---|
| 155 | /** Constructor creates/starts the thread and posts to the semaphore */ |
|---|
| 156 | FnData (ThreadMan::THREAD f, void* d, ThreadMan *tm, ThreadMan::PRIORITY pri); |
|---|
| [687] | 157 | /** Constructor taking a runner */ |
|---|
| 158 | FnData (ThreadMan::Runner *r, void* d, ThreadMan *tm, ThreadMan::PRIORITY pri); |
|---|
| [661] | 159 | /** Destructor just nukes the semaphore */ |
|---|
| [687] | 160 | ~FnData() { |
|---|
| 161 | if (runner) |
|---|
| 162 | delete runner; |
|---|
| 163 | if (startup_sem) |
|---|
| 164 | SDL_DestroySemaphore(startup_sem); |
|---|
| 165 | } |
|---|
| 166 | /** Tell the FnData that its safe to delete itself */ |
|---|
| 167 | void disown() { |
|---|
| 168 | SDL_SemPost(startup_sem); |
|---|
| 169 | } |
|---|
| [661] | 170 | private: |
|---|
| 171 | FnData(const FnData&); |
|---|
| 172 | FnData& operator=(const FnData&); |
|---|
| 173 | }; |
|---|
| 174 | |
|---|
| 175 | /** Function passed to SDL_CreateThread */ |
|---|
| 176 | int run_detached (void *fndata) { |
|---|
| 177 | int ignored = -1; |
|---|
| 178 | //BEGIN_HANDLE_WIN32_SIGNALS(); |
|---|
| 179 | FnData * f = (FnData*)fndata; |
|---|
| 180 | ThreadMan::setMyPriority(f->pri); |
|---|
| [687] | 181 | if (f->fn) { |
|---|
| 182 | ignored = f->fn(f->data); |
|---|
| 183 | } else if (f->runner) { |
|---|
| 184 | ignored = f->runner->run(f->data); |
|---|
| 185 | } |
|---|
| [661] | 186 | SDL_SemWait(f->startup_sem); |
|---|
| 187 | f->tman->thread_done(f->thr); |
|---|
| 188 | delete f; |
|---|
| 189 | //END_HANDLE_WIN32_SIGNALS(); |
|---|
| 190 | return ignored; |
|---|
| 191 | } |
|---|
| 192 | } |
|---|
| 193 | |
|---|
| [285] | 194 | /** Synchronised threads manager */ |
|---|
| 195 | class SyncThreads { |
|---|
| 196 | /** Data for each synchronised thread */ |
|---|
| 197 | struct SyncData { |
|---|
| 198 | typedef std::queue<void*> WAITING_DATA; ///< Type for queue of waiting params to \a fn |
|---|
| 199 | ThreadMan::THREAD fn; ///< The function to call on each post() |
|---|
| 200 | WAITING_DATA waiting; ///< Queue of data waiting to be passed to \a fn |
|---|
| 201 | SDL_sem *wait_count; ///< Semaphore, indicating the number of items in \a waiting |
|---|
| 202 | SDL_mutex *queue_mutex; ///< Mutex for \a waiting |
|---|
| 203 | SDL_Thread *thr; ///< Thread that is calling \a fn |
|---|
| 204 | volatile bool running; ///< Whether we are currently running |
|---|
| [289] | 205 | ThreadMan::PRIORITY pri; ///< Thread priority to set |
|---|
| [402] | 206 | ThreadMan::Runner *runner; ///< If fn == NULL, use runner->run(data) |
|---|
| [285] | 207 | |
|---|
| 208 | /** Thread loop (for \a thr) */ |
|---|
| 209 | int run(); |
|---|
| 210 | /** Set running to false and post to \a waiting */ |
|---|
| 211 | void stop(); |
|---|
| 212 | /** Wait for \a run to finish */ |
|---|
| 213 | int wait(); |
|---|
| 214 | /** Kill, gracelessly */ |
|---|
| 215 | void kill(); |
|---|
| 216 | /** Calls run() */ |
|---|
| 217 | static int start(void* data); |
|---|
| 218 | |
|---|
| 219 | /** Constructor for synchronised thread \a f */ |
|---|
| [289] | 220 | SyncData(ThreadMan::THREAD f, ThreadMan::PRIORITY); |
|---|
| [402] | 221 | /** Constructor for Runner \a r */ |
|---|
| 222 | SyncData(ThreadMan::Runner *r, ThreadMan::PRIORITY); |
|---|
| [285] | 223 | /** Destructor -- cleans up IPC vars */ |
|---|
| 224 | ~SyncData(); |
|---|
| [526] | 225 | private: |
|---|
| 226 | SyncData(const SyncData&) {} |
|---|
| 227 | SyncData& operator=(const SyncData&) {return *this;} |
|---|
| [285] | 228 | }; |
|---|
| 229 | |
|---|
| 230 | typedef std::map<ThreadMan::THREAD, SyncData*> SYNCS; ///< Type to map thread pointers to SyncData |
|---|
| [402] | 231 | typedef std::vector<SyncData*> RSYNCS; ///< Type to map thread pointers to SyncData |
|---|
| [285] | 232 | SYNCS sync; ///< Maps thread pointers to SyncData |
|---|
| [402] | 233 | RSYNCS rsync; ///< Maps runner pointers to SyncData |
|---|
| [285] | 234 | SDL_mutex *mut; ///< Mutex for \a sync |
|---|
| [641] | 235 | volatile bool bail; ///< Whether to bail out of threads |
|---|
| 236 | |
|---|
| [285] | 237 | public: |
|---|
| 238 | /** Schedule a call to \a fn with \a data */ |
|---|
| [420] | 239 | bool post(ThreadMan::THREAD fn, void* data, ThreadMan::PRIORITY priority, SDL_Thread **thr); |
|---|
| [402] | 240 | /** Schedule a call to \a r with \a data */ |
|---|
| [420] | 241 | bool post(ThreadMan::Runner *r, void* data, ThreadMan::PRIORITY priority, SDL_Thread **thr); |
|---|
| [285] | 242 | |
|---|
| 243 | /** Indicate that all synchronised threads should stop */ |
|---|
| 244 | void stopAll(); |
|---|
| 245 | /** Wait for all synchronised threads to finish */ |
|---|
| 246 | void waitAll(); |
|---|
| 247 | /** Kill all synchronised threads, gracelessly */ |
|---|
| 248 | void killAll(); |
|---|
| 249 | |
|---|
| 250 | /** Constructor -- creates mutex */ |
|---|
| 251 | SyncThreads(); |
|---|
| 252 | /** Destructor -- destroys mutex. \note does <em>NOT</em> call stopAll(), waitAll() or killAll() */ |
|---|
| 253 | ~SyncThreads(); |
|---|
| 254 | }; |
|---|
| 255 | |
|---|
| [420] | 256 | bool SyncThreads::post(ThreadMan::THREAD fn, void* data, ThreadMan::PRIORITY priority, SDL_Thread **thr) { |
|---|
| [648] | 257 | if (bail) |
|---|
| 258 | return false; |
|---|
| [285] | 259 | SDL_mutexP(mut); |
|---|
| [647] | 260 | if (bail) { |
|---|
| 261 | SDL_mutexV(mut); |
|---|
| [641] | 262 | return false; |
|---|
| [647] | 263 | } |
|---|
| [285] | 264 | SYNCS::iterator it = sync.find(fn); |
|---|
| 265 | if (it == sync.end()) |
|---|
| [289] | 266 | it = sync.insert(SYNCS::value_type(fn, new SyncData(fn, priority))).first; |
|---|
| [285] | 267 | SDL_mutexV(mut); |
|---|
| 268 | if (!it->second->thr) |
|---|
| 269 | return false; |
|---|
| [420] | 270 | if (thr) |
|---|
| 271 | *thr = it->second->thr; |
|---|
| [285] | 272 | SDL_mutexP(it->second->queue_mutex); |
|---|
| 273 | it->second->waiting.push(data); |
|---|
| 274 | SDL_mutexV(it->second->queue_mutex); |
|---|
| 275 | SDL_SemPost(it->second->wait_count); |
|---|
| 276 | return true; |
|---|
| 277 | } |
|---|
| 278 | |
|---|
| [420] | 279 | bool SyncThreads::post(ThreadMan::Runner *r, void* data, ThreadMan::PRIORITY priority, SDL_Thread **thr) { |
|---|
| [402] | 280 | SDL_mutexP(mut); |
|---|
| 281 | RSYNCS::iterator it = rsync.begin(); |
|---|
| 282 | const RSYNCS::iterator end = rsync.end(); |
|---|
| 283 | for (; it != end; ++it) |
|---|
| 284 | if (*r == *(*it)->runner) |
|---|
| 285 | break; |
|---|
| 286 | if (it == end) { |
|---|
| 287 | rsync.push_back(RSYNCS::value_type(new SyncData(r, priority))); |
|---|
| 288 | it = rsync.end() -1; |
|---|
| [404] | 289 | } else if (r->cleanup) { |
|---|
| 290 | delete r; |
|---|
| [402] | 291 | } |
|---|
| 292 | SDL_mutexV(mut); |
|---|
| 293 | if (!(*it)->thr) |
|---|
| 294 | return false; |
|---|
| [420] | 295 | if (thr) |
|---|
| 296 | *thr = (*it)->thr; |
|---|
| [402] | 297 | SDL_mutexP((*it)->queue_mutex); |
|---|
| 298 | (*it)->waiting.push(data); |
|---|
| 299 | SDL_mutexV((*it)->queue_mutex); |
|---|
| 300 | SDL_SemPost((*it)->wait_count); |
|---|
| 301 | return true; |
|---|
| 302 | } |
|---|
| 303 | |
|---|
| [289] | 304 | SyncThreads::SyncData::SyncData(ThreadMan::THREAD f, ThreadMan::PRIORITY priority) |
|---|
| [285] | 305 | : |
|---|
| [402] | 306 | fn(f), wait_count(SDL_CreateSemaphore(0)), queue_mutex(SDL_CreateMutex()), thr(0), running(true), pri(priority), runner(0) { |
|---|
| [285] | 307 | if (wait_count && queue_mutex) |
|---|
| 308 | thr = SDL_CreateThread(start, this); |
|---|
| 309 | running = thr; |
|---|
| 310 | } |
|---|
| 311 | |
|---|
| [402] | 312 | SyncThreads::SyncData::SyncData(ThreadMan::Runner *r, ThreadMan::PRIORITY priority) |
|---|
| 313 | : |
|---|
| 314 | fn(0), wait_count(SDL_CreateSemaphore(0)), queue_mutex(SDL_CreateMutex()), thr(0), running(true), pri(priority), runner(r) { |
|---|
| 315 | if (wait_count && queue_mutex) |
|---|
| 316 | thr = SDL_CreateThread(start, this); |
|---|
| 317 | running = thr; |
|---|
| 318 | } |
|---|
| 319 | |
|---|
| [285] | 320 | SyncThreads::SyncData::~SyncData() { |
|---|
| [662] | 321 | if (runner) |
|---|
| 322 | delete runner; |
|---|
| [285] | 323 | if (wait_count) |
|---|
| 324 | SDL_DestroySemaphore(wait_count); |
|---|
| 325 | if (queue_mutex) |
|---|
| 326 | SDL_DestroyMutex(queue_mutex); |
|---|
| 327 | } |
|---|
| 328 | |
|---|
| 329 | int SyncThreads::SyncData::start(void *data) { |
|---|
| [594] | 330 | int ret = -1; |
|---|
| [654] | 331 | //BEGIN_HANDLE_WIN32_SIGNALS(); |
|---|
| [594] | 332 | ret = ((SyncData*)data)->run(); |
|---|
| [654] | 333 | //END_HANDLE_WIN32_SIGNALS(); |
|---|
| [594] | 334 | return ret; |
|---|
| [285] | 335 | } |
|---|
| 336 | |
|---|
| 337 | int SyncThreads::SyncData::run() { |
|---|
| [289] | 338 | ThreadMan::setMyPriority(pri); |
|---|
| [285] | 339 | int intr = SDL_SemWait(wait_count); |
|---|
| 340 | int lastret = 0; |
|---|
| 341 | while (running) { |
|---|
| 342 | if (intr) { |
|---|
| 343 | intr = SDL_SemWait(wait_count); |
|---|
| 344 | continue; // interrupted |
|---|
| 345 | } |
|---|
| 346 | SDL_mutexP(queue_mutex); |
|---|
| 347 | if (waiting.empty()) { |
|---|
| 348 | SDL_mutexV(queue_mutex); |
|---|
| 349 | intr = SDL_SemWait(wait_count); |
|---|
| 350 | continue; //error! |
|---|
| 351 | } |
|---|
| 352 | void *data = waiting.front(); |
|---|
| 353 | waiting.pop(); |
|---|
| 354 | SDL_mutexV(queue_mutex); |
|---|
| [402] | 355 | if (fn) { |
|---|
| 356 | lastret = fn(data); //run the thread once |
|---|
| 357 | } else { |
|---|
| 358 | lastret = runner->run(data); |
|---|
| 359 | } |
|---|
| [285] | 360 | intr = SDL_SemWait(wait_count); |
|---|
| 361 | } |
|---|
| 362 | return lastret; |
|---|
| 363 | } |
|---|
| 364 | |
|---|
| 365 | void SyncThreads::SyncData::stop() { |
|---|
| 366 | if (running) { |
|---|
| 367 | running = false; |
|---|
| 368 | SDL_SemPost(wait_count); |
|---|
| 369 | } |
|---|
| 370 | } |
|---|
| 371 | |
|---|
| 372 | int SyncThreads::SyncData::wait() { |
|---|
| 373 | int ret = -1; |
|---|
| 374 | if (thr) { |
|---|
| 375 | SDL_WaitThread(thr, &ret); |
|---|
| 376 | thr = 0; |
|---|
| 377 | } |
|---|
| 378 | return ret; |
|---|
| 379 | } |
|---|
| 380 | |
|---|
| 381 | void SyncThreads::SyncData::kill() { |
|---|
| 382 | if (running) { |
|---|
| 383 | running = false; |
|---|
| 384 | SDL_KillThread(thr); |
|---|
| 385 | thr = 0; |
|---|
| 386 | } |
|---|
| 387 | } |
|---|
| 388 | |
|---|
| 389 | void SyncThreads::stopAll() { |
|---|
| [648] | 390 | bail = true; //don't let new stuff get created |
|---|
| [285] | 391 | SDL_mutexP(mut); |
|---|
| [688] | 392 | //holding the mutex here is safe -- stop doesn't block |
|---|
| [285] | 393 | const SYNCS::iterator e = sync.end(); |
|---|
| 394 | for (SYNCS::iterator it = sync.begin(); it != e; ++it) |
|---|
| 395 | it->second->stop(); |
|---|
| [662] | 396 | const RSYNCS::iterator re = rsync.end(); |
|---|
| 397 | for (RSYNCS::iterator it = rsync.begin(); it != re; ++it) |
|---|
| 398 | (*it)->stop(); |
|---|
| [285] | 399 | SDL_mutexV(mut); |
|---|
| 400 | } |
|---|
| 401 | |
|---|
| 402 | void SyncThreads::waitAll() { |
|---|
| 403 | stopAll(); |
|---|
| 404 | SDL_mutexP(mut); |
|---|
| [688] | 405 | SYNCS local_sync; |
|---|
| 406 | RSYNCS local_rsync; |
|---|
| 407 | std::swap(local_sync, sync); |
|---|
| 408 | std::swap(local_rsync, rsync); |
|---|
| 409 | SDL_mutexV(mut); |
|---|
| 410 | |
|---|
| 411 | const SYNCS::iterator e = local_sync.end(); |
|---|
| 412 | for (SYNCS::iterator it = local_sync.begin(); it != e; ++it) { |
|---|
| [285] | 413 | it->second->wait(); |
|---|
| 414 | delete it->second; |
|---|
| 415 | } |
|---|
| [688] | 416 | const RSYNCS::iterator re = local_rsync.end(); |
|---|
| 417 | for (RSYNCS::iterator it = local_rsync.begin(); it != re; ++it) { |
|---|
| [662] | 418 | (*it)->wait(); |
|---|
| 419 | delete *it; |
|---|
| 420 | } |
|---|
| [285] | 421 | } |
|---|
| 422 | |
|---|
| 423 | void SyncThreads::killAll() { |
|---|
| 424 | stopAll(); |
|---|
| 425 | SDL_mutexP(mut); |
|---|
| 426 | const SYNCS::iterator e = sync.end(); |
|---|
| 427 | for (SYNCS::iterator it = sync.begin(); it != e; ++it) { |
|---|
| 428 | it->second->kill(); |
|---|
| 429 | delete it->second; |
|---|
| 430 | } |
|---|
| 431 | sync.clear(); |
|---|
| 432 | SDL_mutexV(mut); |
|---|
| 433 | } |
|---|
| 434 | |
|---|
| [641] | 435 | SyncThreads::SyncThreads() : mut(0), bail(false) { |
|---|
| [287] | 436 | mut = SDL_CreateMutex(); |
|---|
| [285] | 437 | } |
|---|
| 438 | |
|---|
| 439 | SyncThreads::~SyncThreads() { |
|---|
| 440 | if (mut) |
|---|
| 441 | SDL_DestroyMutex(mut); |
|---|
| 442 | } |
|---|
| 443 | |
|---|
| [661] | 444 | FnData::FnData(ThreadMan::THREAD f, void* d, ThreadMan *tm, ThreadMan::PRIORITY priority) |
|---|
| [282] | 445 | : |
|---|
| [687] | 446 | fn(f), data(d), thr(0), startup_sem(SDL_CreateSemaphore(0)), tman(tm), pri(priority), runner(0) { |
|---|
| [282] | 447 | if ((thr = SDL_CreateThread(run_detached, this))) { |
|---|
| 448 | tman->thread_add(thr); |
|---|
| 449 | } |
|---|
| 450 | } |
|---|
| 451 | |
|---|
| [687] | 452 | FnData::FnData(ThreadMan::Runner *r, void* d, ThreadMan *tm, ThreadMan::PRIORITY priority) |
|---|
| 453 | : |
|---|
| 454 | fn(0), data(d), thr(0), startup_sem(SDL_CreateSemaphore(0)), tman(tm), pri(priority), runner(r) { |
|---|
| 455 | if ((thr = SDL_CreateThread(run_detached, this))) { |
|---|
| 456 | tman->thread_add(thr); |
|---|
| 457 | } |
|---|
| 458 | } |
|---|
| 459 | |
|---|
| [287] | 460 | ThreadMan::ThreadMan() : thrmut(0), syncman(0) { |
|---|
| 461 | thrmut = SDL_CreateMutex(); |
|---|
| 462 | syncman = new SyncThreads(); |
|---|
| [285] | 463 | } |
|---|
| 464 | |
|---|
| 465 | ThreadMan::~ThreadMan() { |
|---|
| [287] | 466 | if (thrmut) |
|---|
| 467 | SDL_DestroyMutex(thrmut); |
|---|
| 468 | if (syncman) |
|---|
| 469 | delete syncman; |
|---|
| [285] | 470 | } |
|---|
| 471 | |
|---|
| [494] | 472 | int ThreadMan::ThreadKiller::wait() { |
|---|
| 473 | int retval; |
|---|
| 474 | SDL_WaitThread(thr, &retval); |
|---|
| 475 | return retval; |
|---|
| 476 | } |
|---|
| 477 | |
|---|
| [661] | 478 | bool ThreadMan::ThreadKiller::done() { |
|---|
| 479 | int ignored; |
|---|
| 480 | if (finished) |
|---|
| 481 | SDL_WaitThread(thr, &ignored); |
|---|
| 482 | return finished; |
|---|
| 483 | } |
|---|
| 484 | |
|---|
| 485 | ThreadMan::ThreadKiller::ThreadKiller(SDL_Thread *t) |
|---|
| 486 | : |
|---|
| 487 | thr(t), finished(false) |
|---|
| 488 | {} |
|---|
| 489 | |
|---|
| 490 | bool ThreadMan::ThreadKiller::operator==(SDL_Thread *t) { |
|---|
| 491 | return thr == t; |
|---|
| 492 | } |
|---|
| 493 | |
|---|
| [282] | 494 | void ThreadMan::thread_cleanup() { |
|---|
| 495 | SDL_mutexP(thrmut); |
|---|
| [687] | 496 | //holding a mutex while checking for done is fine |
|---|
| [282] | 497 | threads.erase(std::remove_if(threads.begin(), |
|---|
| 498 | threads.end(), |
|---|
| 499 | std::mem_fun_ref(&ThreadKiller::done)), |
|---|
| 500 | threads.end()); |
|---|
| 501 | SDL_mutexV(thrmut); |
|---|
| 502 | } |
|---|
| 503 | |
|---|
| [494] | 504 | void ThreadMan::stopAsync() { |
|---|
| 505 | /* first, clean up ones we know are stopped */ |
|---|
| 506 | SDL_mutexP(thrmut);//rely on the recursive mutex |
|---|
| 507 | thread_cleanup(); |
|---|
| [687] | 508 | //here we need to release the mutex (blocking on WaitThread while holding a mutex?? you crazy?) |
|---|
| 509 | //but we can't let other threads call done() while we wait. But we are clearing up everything |
|---|
| 510 | //so let's make a copy and clear threads straight away |
|---|
| 511 | THREADS local; //initially empty |
|---|
| 512 | std::swap(local, threads); |
|---|
| 513 | SDL_mutexV(thrmut); |
|---|
| 514 | |
|---|
| 515 | std::for_each(local.begin(), |
|---|
| 516 | local.end(), |
|---|
| [494] | 517 | std::mem_fun_ref(&ThreadKiller::wait)); |
|---|
| 518 | } |
|---|
| 519 | |
|---|
| [282] | 520 | void ThreadMan::thread_done(SDL_Thread *t) { |
|---|
| 521 | SDL_mutexP(thrmut); |
|---|
| 522 | THREADS::iterator it = std::find(threads.begin(), threads.end(), t); |
|---|
| 523 | if (it != threads.end()) |
|---|
| 524 | it->finished = true; |
|---|
| 525 | SDL_mutexV(thrmut); |
|---|
| 526 | } |
|---|
| 527 | |
|---|
| 528 | void ThreadMan::thread_add(SDL_Thread *t) { |
|---|
| 529 | SDL_mutexP(thrmut); |
|---|
| 530 | threads.push_back(THREADS::value_type(t)); |
|---|
| 531 | SDL_mutexV(thrmut); |
|---|
| 532 | } |
|---|
| 533 | |
|---|
| [687] | 534 | namespace { |
|---|
| 535 | /** See if the thread in \a t started, if so, assign to \a thr if non-null */ |
|---|
| 536 | bool check_thread(FnData* t, SDL_Thread **thr) { |
|---|
| 537 | if (!t->thr) { |
|---|
| 538 | delete t; |
|---|
| 539 | return false; |
|---|
| 540 | } |
|---|
| 541 | /* else we are running */ |
|---|
| 542 | if (thr) |
|---|
| 543 | *thr = t->thr; |
|---|
| 544 | t->disown(); |
|---|
| 545 | return true; |
|---|
| 546 | } |
|---|
| 547 | } |
|---|
| 548 | |
|---|
| [420] | 549 | bool ThreadMan::run(THREAD fn, void* data, bool sync, PRIORITY pri, SDL_Thread **thr) { |
|---|
| [285] | 550 | if (sync && syncman) |
|---|
| [420] | 551 | return syncman->post(fn, data, pri, thr); |
|---|
| [282] | 552 | thread_cleanup(); |
|---|
| [687] | 553 | return check_thread(new FnData(fn, data, this, pri), thr); |
|---|
| [282] | 554 | } |
|---|
| [285] | 555 | |
|---|
| [598] | 556 | bool ThreadMan::run(Runner *r, void* data, bool sync, PRIORITY pri, SDL_Thread **thr) { |
|---|
| 557 | if (sync && syncman) |
|---|
| 558 | return syncman->post(r, data, pri, thr); |
|---|
| 559 | thread_cleanup(); |
|---|
| [687] | 560 | return check_thread(new FnData(r, data, this, pri), thr); |
|---|
| [598] | 561 | } |
|---|
| 562 | |
|---|
| [285] | 563 | void ThreadMan::stopSync() { |
|---|
| 564 | if (syncman) |
|---|
| [641] | 565 | syncman->stopAll(); |
|---|
| 566 | } |
|---|
| 567 | |
|---|
| 568 | void ThreadMan::joinSync() { |
|---|
| 569 | if (syncman) |
|---|
| [285] | 570 | syncman->waitAll(); |
|---|
| 571 | } |
|---|
| [289] | 572 | |
|---|
| 573 | void ThreadMan::setMyPriority(ThreadMan::PRIORITY pri) { |
|---|
| 574 | #ifdef __WIN32__ |
|---|
| 575 | switch(pri) { |
|---|
| 576 | case INHERIT: break; |
|---|
| 577 | case NORMAL: ::SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); break; |
|---|
| 578 | case BELOW_NORMAL: ::SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); break; |
|---|
| 579 | case LOW: ::SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); break; |
|---|
| 580 | case IDLE: ::SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); break; |
|---|
| 581 | } |
|---|
| 582 | #else |
|---|
| 583 | # ifdef __unix__ |
|---|
| 584 | switch(pri) { |
|---|
| 585 | case INHERIT: break; |
|---|
| 586 | case NORMAL: ::nice(0); break; |
|---|
| 587 | case BELOW_NORMAL: ::nice(5); break; |
|---|
| 588 | case LOW: ::nice(10); break; |
|---|
| 589 | case IDLE: ::nice(19); break; |
|---|
| 590 | } |
|---|
| [296] | 591 | # else |
|---|
| [289] | 592 | static bool thunk = 1; |
|---|
| 593 | if (thunk) { |
|---|
| [296] | 594 | ::fputs("== CANNOT CHANGE THREAD PRIORITY ON THIS PLATFORM ==", stderr); |
|---|
| [289] | 595 | thunk = 0; |
|---|
| 596 | } |
|---|
| [296] | 597 | # endif |
|---|
| [289] | 598 | #endif |
|---|
| 599 | } |
|---|
| [661] | 600 | |
|---|
| [662] | 601 | void ThreadMan::sleep(unsigned ms) { |
|---|
| 602 | SDL_Delay(ms); |
|---|
| 603 | } |
|---|
| 604 | |
|---|
| 605 | int ThreadMan::thread_id() { |
|---|
| 606 | return SDL_ThreadID(); |
|---|
| 607 | } |
|---|
| 608 | |
|---|
| [661] | 609 | ThreadMan& ThreadMan::tman() { |
|---|
| 610 | static ThreadMan *threadman = new ThreadMan(); |
|---|
| 611 | return *threadman; |
|---|
| 612 | } |
|---|