root/threadman/trunk/threadman.cpp @ 688

Revision 688, 18.8 kB (checked in by tapted, 2 years ago)

Add a cmake optimise option and reduce the risk of a deadlock, holding a lock when waiting

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision url Rev Revision
RevLine 
[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]44extern "C" {
45
[613]46static const int KILLTHREAD = 1; ///< Call ExitThread when an exception is caught
[595]47
[594]48static
49EXCEPTION_DISPOSITION
50win32_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]144namespace {
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 */
195class 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]237public:
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]256bool 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]279bool 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]304SyncThreads::SyncData::SyncData(ThreadMan::THREAD f, ThreadMan::PRIORITY priority)
[285]305:
[402]306fn(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]312SyncThreads::SyncData::SyncData(ThreadMan::Runner *r, ThreadMan::PRIORITY priority)
313:
314fn(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]320SyncThreads::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
329int 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
337int 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
365void SyncThreads::SyncData::stop() {
366    if (running) {
367        running = false;
368        SDL_SemPost(wait_count);
369    }
370}
371
372int SyncThreads::SyncData::wait() {
373    int ret = -1;
374    if (thr) {
375        SDL_WaitThread(thr, &ret);
376        thr = 0;
377    }
378    return ret;
379}
380
381void SyncThreads::SyncData::kill() {
382    if (running) {
383        running = false;
384        SDL_KillThread(thr);
385        thr = 0;
386    }
387}
388
389void 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
402void 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
423void 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]435SyncThreads::SyncThreads() : mut(0), bail(false) {
[287]436    mut = SDL_CreateMutex();
[285]437}
438
439SyncThreads::~SyncThreads() {
440    if (mut)
441        SDL_DestroyMutex(mut);
442}
443
[661]444FnData::FnData(ThreadMan::THREAD f, void* d, ThreadMan *tm, ThreadMan::PRIORITY priority)
[282]445:
[687]446fn(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]452FnData::FnData(ThreadMan::Runner *r, void* d, ThreadMan *tm, ThreadMan::PRIORITY priority)
453:
454fn(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]460ThreadMan::ThreadMan() : thrmut(0), syncman(0) {
461    thrmut = SDL_CreateMutex();
462    syncman = new SyncThreads();
[285]463}
464
465ThreadMan::~ThreadMan() {
[287]466    if (thrmut)
467        SDL_DestroyMutex(thrmut);
468    if (syncman)
469        delete syncman;
[285]470}
471
[494]472int ThreadMan::ThreadKiller::wait() {
473    int retval;
474    SDL_WaitThread(thr, &retval);
475    return retval;
476}
477
[661]478bool ThreadMan::ThreadKiller::done() {
479    int ignored;
480    if (finished)
481        SDL_WaitThread(thr, &ignored);
482    return finished;
483}
484
485ThreadMan::ThreadKiller::ThreadKiller(SDL_Thread *t)
486:
487thr(t), finished(false)
488{}
489
490bool ThreadMan::ThreadKiller::operator==(SDL_Thread *t) {
491    return thr == t;
492}
493
[282]494void 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]504void 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]520void 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
528void 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]534namespace {
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]549bool 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]556bool 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]563void ThreadMan::stopSync() {
564    if (syncman)
[641]565        syncman->stopAll();
566}
567
568void ThreadMan::joinSync() {
569    if (syncman)
[285]570        syncman->waitAll();
571}
[289]572
573void 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]601void ThreadMan::sleep(unsigned  ms) {
602    SDL_Delay(ms);
603}
604
605int ThreadMan::thread_id() {
606    return SDL_ThreadID();
607}
608
[661]609ThreadMan& ThreadMan::tman() {
610    static ThreadMan *threadman = new ThreadMan();
611    return *threadman;
612}
Note: See TracBrowser for help on using the browser.