1 /*
2 * Copyright (C) 2002 - David W. Durham
3 *
4 * This file is part of ReZound, an audio editing application.
5 *
6 * ReZound is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2 of the License,
9 * or (at your option) any later version.
10 *
11 * ReZound is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20 #include "AThread.h"
21
22 #include <stdexcept>
23 #include <string>
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <assert.h>
29 using namespace std;
30
31 #include "clocks.h"
32 #ifdef GOOGLE_PROFILE
33 #include <google/profiler.h>
34 #endif
35 #ifdef __APPLE__
36 #import <Foundation/Foundation.h>
37 //#include "ScopedAutoReleasePool.h"
38 #endif
39
40 #if defined(_WIN32)
41 // *** WIN32 implementation ***
42 #include <windows.h>
43 #include <process.h>
44 #include <crtdbg.h>
45 #include "istring"
46
47 #ifndef __func__
48 # define __func__ __FUNCTION__
49 #endif
50
51 #define THREAD_NONE 0
52
53
54 // initializes gTSKey (if it hasn't already been) used by get/setCurrentThreadObjectTS()
55 static DWORD gTSKey;
initTSKey()56 static void initTSKey()
57 {
58 // allocate the ts-key for TLS data if we haven't already done so
59 static bool TSKeyIsSet = false;
60 if(!TSKeyIsSet) {
61 TSKeyIsSet = true;
62 gTSKey = TlsAlloc();
63 }
64 }
65
66 // associates the given AThread* with the currently running thread so that getCurrentThreadInstance() can later return it
67 // must be called at the beginning of a thread's execution and again at the end of a thread given NULL
setCurrentThreadObjectTS(AThread * t)68 static void setCurrentThreadObjectTS(AThread *t)
69 {
70 TlsSetValue(gTSKey, t);
71 }
72
73 // Returns the AThread* for the currently running thread. The process is aborted
74 // if thread specific data has not been properly initialized using either AThread or CRawThreadHelper.
getCurrentThreadObjectTS()75 static AThread *getCurrentThreadObjectTS() // rename to getCurrentThreadInstance()
76 {
77 AThread *thread = (AThread *)TlsGetValue(gTSKey);
78 if (!thread) {
79 // If this happens, then a thread was created and is trying to use thread specific data
80 // without having first initialized the thread specific data. Thread specific data
81 // must be initialized using CRawThreadHelper when AThread is not used to spawn the thread.
82 assert(("Attempted to access thread specific data for a non AThread", false));
83
84 MessageBox(
85 0,
86 itstring("Attempted to access thread specific data for a non AThread").c_str(),
87 itstring("Error").c_str(),
88 MB_OK);
89 abort();
90
91 }
92 return thread;
93 }
94
95
AThread()96 AThread::AThread()
97 : mThreadID(0)
98 , mThreadHandle(THREAD_NONE)
99 , mRunning(false)
100 , mCancelled(false)
101 , mDeleteOnExit(false)
102 {
103 }
104
~AThread()105 AThread::~AThread()
106 {
107 // TODO consider throwing an exception if it's still isRunning() so that we force applications to make sure that the thread is dead when it gets here
108
109 // just in case the derived class destructor didn't do this
110 setCancelled(true);
111
112 wait();
113
114 if (mDeleteOnExit) {
115 // This is safe because if mDeleteOnExit is true then we are
116 // being destructed from our own thread.
117 clearAllThreadSpecific(true);
118 }
119
120 if (mThreadHandle != THREAD_NONE) {
121 CloseHandle(mThreadHandle);
122 }
123 }
124
start(size_t stackSize)125 void AThread::start(size_t stackSize)
126 {
127 initTSKey();
128
129 if (isRunning()) {
130 throw(runtime_error(string(__func__)+" -- thread is already started"));
131 }
132
133 // this would happen if the thread were waited on and then restarted
134 if (mThreadHandle != THREAD_NONE) {
135 CloseHandle(mThreadHandle);
136 }
137
138 mCancelled = false;
139
140 // NOTE: if you're getting undefined _beginthreadex symbols, then you need to change your code-generation in the project properties to multi-threaded
141 mThreadHandle = (void*)_beginthreadex(NULL, (unsigned int)stackSize, (unsigned (__stdcall*)(void *))AThreadStart, (void*)this, 0, (unsigned*)&mThreadID);
142 if (0 == mThreadHandle) {
143 // error
144 mRunning = false;
145 mThreadID = 0;
146 mThreadHandle = THREAD_NONE;
147 throw(runtime_error(string(__func__)+" -- cannot create thread"));
148 }
149
150 mRunning = true;
151 }
152
wait(int timeout_ms) const153 bool AThread::wait(int timeout_ms) const
154 {
155 // always fail waiting on ourselves.
156 if (getCurrentThreadID() == getThreadID()) return false;
157 bool ret = false;
158
159 if ((mThreadHandle != THREAD_NONE) && (WAIT_TIMEOUT != WaitForSingleObject(const_cast<void*>(mThreadHandle), (timeout_ms < 0 ? INFINITE : timeout_ms) ))) {
160 ret = true;
161 }
162
163 return ret;
164 }
165
yield()166 void AThread::yield()
167 {
168 sleep();
169 }
170
sleep(const int secs,const int msecs)171 void AThread::sleep(const int secs, const int msecs)
172 {
173 clocks::sleep(secs,msecs);
174 }
175
getCurrentThreadID()176 void *AThread::getCurrentThreadID()
177 {
178 return (void*)::GetCurrentThreadId();
179 }
180
isRunning() const181 bool AThread::isRunning() const
182 {
183 return mRunning;
184 }
185
getThreadID() const186 void *AThread::getThreadID() const {
187 return (void*)(mThreadHandle == THREAD_NONE ? 0 : mThreadID);
188 }
189
getHandle() const190 void *AThread::getHandle() const {
191 return mThreadHandle;
192 }
193
AThreadStart(void * temp)194 unsigned __stdcall AThread::AThreadStart(void *temp)
195 {
196 AThread *thread = (AThread*)temp;
197 setCurrentThreadObjectTS(thread);
198
199 try {
200 #ifdef GOOGLE_PROFILE
201 ProfilerRegisterThread();
202 #endif
203 thread->main();
204 #ifdef _DEBUG
205 // handy for debugging... not necessary for release
206 fprintf(stderr, "thread ended 0x%x %d\n",thread->mThreadID,thread->mThreadID);
207 #endif
208 } catch(exception &e) {
209 fprintf(stderr "exception was thrown within thread -- %s\n",e.what());
210
211 // It is important that all threads properly catch their own exceptions!
212 // Please make sure you trap this!!!
213 // We only abort in debug builds so the app doesn't crash in release mode.
214 assert(("UNHANDLED EXCEPTION IN THREAD, PLEASE CATCH ALL EXCEPTIONS",false));
215 }
216 // we always want other unhandled exceptions to crash so debugger or crashdump is triggered
217 //catch(...) {
218 // fprintf(stderr "unhandled exception was thrown within thread\n");
219 //}
220
221 thread->mRunning = false;
222
223 if(thread->mDeleteOnExit){
224 // Let the thread clear its own thread specific data so that
225 // the thread can guarantee that it is still a valid object
226 // when clearAllThreadSpecific() tries to access it.
227 delete thread;
228 } else {
229 // The thread did not clear its own thread specific data, so
230 // we must do it here.
231 clearAllThreadSpecific(true);
232 }
233
234 setCurrentThreadObjectTS(0);
235
236 return 0;
237 }
238
239 #else
240 // *** posix implementation ***
241 #define THREAD_NONE ((pthread_t)0x7fffffff)
242
243 #include <sched.h> // for sched_yield()
244 #include <stdio.h> // for fprintf()
245 #include <stdlib.h> // for abort()
246 #include <string.h>
247 #include <errno.h>
248
249 /* for signal stuff only */
250 #include <signal.h>
251 #include <map>
252 using namespace std;
253
254 #include "CConditionVariable.h"
255 #include "istring"
256 #include "clocks.h"
257
258
259 // initializes gTSKey (if it hasn't already been) used by get/setCurrentThreadObjectTS()
260 static pthread_key_t gTSKey;
initTSKey()261 static void initTSKey()
262 {
263 static bool TSKeyIsSet = false;
264 if(!TSKeyIsSet) {
265 TSKeyIsSet = true;
266 pthread_key_create(&gTSKey, 0);
267 }
268 }
269
270 // associates the given AThread* with the currently running thread so that getCurrentThreadInstance() can later return it
271 // must be called at the beginning of a thread's execution and again at the end of a thread given NULL
setCurrentThreadObjectTS(AThread * t)272 static void setCurrentThreadObjectTS(AThread *t)
273 {
274 pthread_setspecific(gTSKey, t);
275 }
276
277 // returns the AThread* for the currently running thread or NULL if this thread wasn't created with AThread
getCurrentThreadObjectTS()278 static AThread *getCurrentThreadObjectTS() // rename to getCurrentThreadInstance()
279 {
280 return (AThread *)pthread_getspecific(gTSKey);
281 }
282
283
284 /* --------------------- */
285
AThread()286 AThread::AThread()
287 : mThreadID(THREAD_NONE)
288 , mRunning(false)
289 , mDeleteOnExit(false)
290 {
291 }
292
~AThread()293 AThread::~AThread()
294 {
295 // TODO consider throwing an exception if it's still isRunning() so that we force applications to make sure that the thread is dead when it gets here
296
297 // just in case the derived class destructor didn't do this
298 setCancelled(true);
299
300 wait();
301
302 if (mDeleteOnExit) {
303 // This is safe because if mDeleteOnExit is true then we are
304 // being destructed from our own thread.
305 clearAllThreadSpecific(true);
306 }
307 }
308
start(size_t stackSize)309 void AThread::start(size_t stackSize)
310 {
311 initTSKey();
312 CMutexLocker ml(mStartSyncMut);
313
314 if(isRunning()) {
315 throw(runtime_error(string(__func__)+" -- thread is already started"));
316 }
317
318 pthread_attr_t attr;
319 pthread_attr_init(&attr);
320
321 pthread_attr_setstacksize(&attr,stackSize);
322
323 mRunning = true;
324 mCancelled = false;
325
326 if (pthread_create(&mThreadID, &attr, &AThread::AThreadStart, (void*)this) != 0) {
327 mRunning = false;
328 mThreadID=THREAD_NONE;
329 int err = errno;
330 throw(runtime_error(string(__func__)+" -- cannot create thread: ("+istring(err)+") "+strerror(err)));
331 }
332
333 // The creating thread waits until the child has locked mRunningLock. That
334 // should keep anyone from calling wait before that happens.
335 mStartSyncCond.wait(mStartSyncMut);
336 }
337
wait(int timeout_ms) const338 bool AThread::wait(int timeout_ms) const
339 {
340 if (!const_cast<AThread*>(this)->mRunningLock.trylock(timeout_ms)) {
341 return false;
342 }
343
344 bool okay = true;
345
346 if (mThreadID != THREAD_NONE) {
347 okay = (pthread_join(mThreadID, NULL) == 0);
348 if(okay) {
349 const_cast<AThread*>(this)->mThreadID = THREAD_NONE;
350 }
351 }
352
353 const_cast<AThread*>(this)->mRunningLock.unlock();
354 return okay;
355 }
356
yield()357 void AThread::yield()
358 {
359 sched_yield();
360 }
361
sleep(const int secs,const int msecs)362 void AThread::sleep(const int secs, const int msecs)
363 {
364 clocks::sleep(secs, msecs);
365 }
366
getCurrentThreadID()367 void *AThread::getCurrentThreadID()
368 {
369 return (void*)pthread_self();
370 }
371
isRunning() const372 bool AThread::isRunning() const
373 {
374 return mRunning;
375 }
376
getThreadID() const377 void *AThread::getThreadID() const {
378 return (void*)mThreadID;
379 }
380
AThreadStart(void * temp)381 void *AThread::AThreadStart(void* temp)
382 {
383 AThread *thread = (AThread*)temp;
384 setCurrentThreadObjectTS(thread);
385
386 try {
387 // Get the mRunningLock locked, and release the creator thread.
388 thread->mStartSyncMut.lock();
389 thread->mRunningLock.lock();
390 thread->mStartSyncCond.signal();
391 thread->mStartSyncMut.unlock();
392
393 #ifdef GOOGLE_PROFILE
394 ProfilerRegisterThread();
395 #endif
396 #ifdef __APPLE__
397 assert([NSThread isMultiThreaded] == YES);
398
399 #if defined(__USING_ARC__)
400 @autoreleasepool{
401 thread->main();
402 }
403 #else
404 ScopedAutoReleasePool pool;
405 thread->main();
406 #endif
407 #else
408 thread->main();
409 #endif
410 } catch(exception &e) {
411 fprintf(stderr,"exception was thrown within thread -- ID: %s:(%lu);\nmessage: %s\naborting\n", getThreadName().c_str(), (unsigned long)thread->mThreadID, e.what());fflush(stderr);
412
413 // we only abort all the time with linux code, Mac can continue without crashing
414 assert(((void)"UNHANDLED EXCEPTION IN THREAD, ALL EXCEPTIONS MUST BE CAUGHT",false));
415 #if !defined(__APPLE__)
416 abort();
417 } catch(...) {
418 // linux will abort, Mac will not so it will crash with unhandled exception and generate a crash report
419 fprintf(stderr,"unhandled exception was thrown within thread -- ID: %s:(%x); aborting\n", getThreadName().c_str(), thread->mThreadID);fflush(stderr);
420
421 // we only abort all the time with linux code
422 abort();
423 #endif
424 }
425
426 thread->mRunning = false;
427 try { AThread::alarm(0); } catch(...) { /* oh well */ }
428
429 // wake up waiting callers to AThread::wait()
430 thread->mRunningLock.unlock();
431
432 if(thread->mDeleteOnExit){
433 // Let the thread clear its own thread specific data so that
434 // the thread can guarantee that it is still a valid object
435 // when clearAllThreadSpecific() tries to access it.
436 delete thread;
437 } else {
438 // The thread did not clear its own thread specific data, so
439 // we must do it here.
440 clearAllThreadSpecific(true);
441 }
442
443 setCurrentThreadObjectTS(0);
444
445 return NULL;
446 }
447
448
449
450 // --- stuff for using AThread::alarm()
451 /*
452 * This works by having a thread mRunning (upon at least one call to AThread::alarm()) that is responsible
453 * for sending a SIGALARM signal to particular threads that have scheduled and alarm with the
454 * AThread::alarm() function. The worker thread efficiently wakes up to send the signal.
455 *
456 * The sighandler for SIGALRM calls AThread::alarmHandler() always.
457 */
458
459 class CAlarmerThread : public AThread
460 {
461 public:
CAlarmerThread()462 CAlarmerThread()
463 : kill(false)
464 {
465 }
466
~CAlarmerThread()467 virtual ~CAlarmerThread() throw()
468 {
469 try {
470 kill = true;
471 c.signal();
472 wait();
473 } catch(exception &e) {
474 /* oh well it's static anyway */
475 }
476 }
477
main()478 void main()
479 {
480 try {
481 while (!kill) {
482 CMutexLocker ml(m);
483
484 // calculate the minimum time we need to wait for all scheduled alarms
485 clocks::msec_t min_timeout = 60 * 1000; // wake up in 60 seconds to start out with
486 {
487 clocks::msec_t currentTime = clocks::fixed_msec();
488 for(map<void*, clocks::msec_t>::const_iterator i = alarms.begin(); i != alarms.end(); i++) {
489 clocks::msec_t timeout = i->second - currentTime;
490 if (timeout < min_timeout) {
491 min_timeout = timeout;
492 }
493 }
494 }
495
496 if (min_timeout > 0) {
497 c.wait(m, min_timeout);
498 }
499
500 if (kill) {
501 break;
502 }
503
504 // check if any threads need to be signaled
505 {
506 clocks::msec_t currentTime = clocks::fixed_msec();
507 restart_check:
508 for(map<void*, clocks::msec_t>::iterator i = alarms.begin(); i != alarms.end(); i++) {
509 clocks::msec_t timeout=i->second-currentTime;
510 if (timeout <= 0) {
511 pthread_kill((pthread_t)i->first, SIGALRM);
512 alarms.erase(i);
513 // since map::erase() doesn't return the next iterator, we have to just restart the loop
514 goto restart_check;
515 }
516 }
517 }
518 }
519 } catch(exception &e) {
520 fprintf(stderr, "exception in alarmer thread -- %s\n", e.what());
521 fflush(stderr);
522 }
523 }
524
525 private:
526 friend void AThread::alarm(int seconds);
527
528 bool kill;
529 CMutex m;
530 CConditionVariable c;
531 map<void*, clocks::msec_t> alarms;
532
533 } gAlarmerThread;
534
535 // NOTE: that on linux the sig handler set up for SIGALRM is shared across all threads, so all threads that
536 // are going to be using AThread::alarm() must all use the same handler function set by calling
537 // AThread::set_SIGALRM_handler(void (*sighandler)(int)). This is why set_SIGALRM_handler() is static.
538 // It's very likely that an application should call AThread::set_SIGARLM_handler() only once (i.e. not set
539 // per thread.. because it can't be).
SIGALRM_handler(int sig)540 void AThread::SIGALRM_handler(int sig)
541 {
542 // this is invoked IN the thread that is receiving the alarm
543 if(AThread *that = getCurrentThreadObjectTS()) {
544 that->alarmHandler();
545 }
546 }
547
alarm(int seconds)548 void AThread::alarm(int seconds)
549 {
550 if (seconds < 0) {
551 return;
552 }
553
554 if (!gAlarmerThread.isRunning()) {
555 if (seconds == 0) {
556 return; // if it was never mRunning, then no need to cancel
557 }
558
559 // arrange for ::SIGALARM_handler() to be called whenever SIGALRM is sent to this process
560 // it is supposed to be raised only by gAlarmerThread declared above.
561 struct sigaction act;
562 memset(&act, 0, sizeof(act));
563 act.sa_handler = AThread::SIGALRM_handler;
564 sigaction(SIGALRM, &act, NULL);
565
566 gAlarmerThread.start();
567 }
568
569 CMutexLocker ml(gAlarmerThread.m);
570
571 if (seconds>0) {
572 // schedule an alarm
573 clocks::msec_t currentTime = clocks::fixed_msec();
574 gAlarmerThread.alarms[getCurrentThreadID()] = currentTime + (seconds * 1000LL);
575 gAlarmerThread.c.signal();
576 } else {
577 // cancel any set alarm
578 map<void*, clocks::msec_t>::iterator i = gAlarmerThread.alarms.find(getCurrentThreadID());
579 if (i != gAlarmerThread.alarms.end()) {
580 gAlarmerThread.alarms.erase(i);
581 }
582 }
583 }
584
585 #endif
586
587
CRawThreadHelper()588 CRawThreadHelper::CRawThreadHelper()
589 {
590 initTSKey();
591 mDummyThread = new AThread;
592 setCurrentThreadObjectTS(mDummyThread);
593 }
594
~CRawThreadHelper()595 CRawThreadHelper::~CRawThreadHelper()
596 {
597 AThread::clearAllThreadSpecific(true);
598 delete mDummyThread;
599 setCurrentThreadObjectTS(0);
600 }
601
602 // this function and the instance variable below is to allow thread specific data to be set up for the main thread
setupMainThreadPlaceHolder()603 int setupMainThreadPlaceHolder()
604 {
605 // not deleting this on purpose
606 new CRawThreadHelper();
607
608 srand((unsigned)clocks::time_msec());
609 AThread::setThreadName("main");
610 return 0;
611 }
612 static int gMainInitDummy = setupMainThreadPlaceHolder();
613
setSpecific(unsigned key,CThreadSpecificObject * value,bool persistent)614 void AThread::setSpecific(unsigned key, CThreadSpecificObject *value, bool persistent)
615 {
616 AThread *that = getCurrentThreadObjectTS();
617
618 if(value) {
619 value->mKey = key;
620 value->mPersistent = persistent;
621 }
622
623 ThreadSpecificMap_t::iterator i = that->mThreadSpecificMap.find(key);
624 if(i == that->mThreadSpecificMap.end()) {
625 // nothing yet exists with this key .. add it
626
627 if(value) {
628 that->mThreadSpecificMap[key] = value;
629 }
630
631 } else {
632 // something already exists with this key .. replace it
633
634 if(i->second) {
635 if(i->second->mKey > 0) {
636 // we're not being called from CThreadSpecificObject::dtor()
637
638
639 // prevent CThreadSpecificObject::dtor() from recurring on this method
640 i->second->mKey = 0;
641 delete i->second;
642 }
643 }
644 if(value) {
645 i->second = value;
646 } else {
647 that->mThreadSpecificMap.erase(i);
648 }
649 }
650 }
651
getSpecific(unsigned key)652 CThreadSpecificObject *AThread::getSpecific(unsigned key)
653 {
654 AThread *that = getCurrentThreadObjectTS();
655 ThreadSpecificMap_t::const_iterator i = that->mThreadSpecificMap.find(key);
656 if(i != that->mThreadSpecificMap.end()) {
657 return i->second;
658 }
659
660 return NULL;
661 }
662
clearThreadSpecific()663 void AThread::clearThreadSpecific() throw()
664 {
665 clearAllThreadSpecific(false);
666 }
667
clearAllThreadSpecific(bool clearPersistent)668 void AThread::clearAllThreadSpecific(bool clearPersistent) throw()
669 {
670 if(clearPersistent) {
671 // do this so that we'll clear the thread-name for the debugger as well since setThreadName() pokes the runtime debug information
672 setThreadName("");
673 }
674
675 AThread *that = getCurrentThreadObjectTS();
676
677 // clear non-persistent
678 // (we do this first because if some non-persistent's dtor accesses a persistent object, we are sure not to have destroyed it yet)
679 for(ThreadSpecificMap_t::iterator i = that->mThreadSpecificMap.begin(); i != that->mThreadSpecificMap.end();) {
680 // either clear everything or else just items that aren't persistent
681 if(i->second && !i->second->mPersistent) {
682 try {
683 i->second->mKey = 0; // prevent dtor from recurring to setSpecific()
684 delete i->second;
685 } catch(...) {
686 /* oh well */
687 }
688
689 ThreadSpecificMap_t::iterator e(i);
690 ++i;
691 that->mThreadSpecificMap.erase(e);
692 } else {
693 ++i;
694 }
695 }
696
697 // now clear persistent (if requested)
698 if(clearPersistent) {
699 for(ThreadSpecificMap_t::iterator i = that->mThreadSpecificMap.begin(); i != that->mThreadSpecificMap.end();) {
700 if(i->second && i->second->mPersistent) {
701 try {
702 i->second->mKey = 0; // prevent dtor from recurring to setSpecific()
703 delete i->second;
704 } catch(...) {
705 /* oh well */
706 }
707
708 ThreadSpecificMap_t::iterator e(i);
709 ++i;
710 that->mThreadSpecificMap.erase(e);
711 } else {
712 ++i;
713 }
714 }
715 }
716 }
717
718
setThreadNameInDebugger(const char * threadName)719 static void setThreadNameInDebugger(const char *threadName)
720 {
721 #if defined(_DEBUG) && defined(_WIN32)
722 // This code fragment tells the VisualStudio debugger the name of the current thread
723 // See: http://msdn2.microsoft.com/en-us/library/xcb2z8hs(VS.80).aspx
724 #define MS_VC_EXCEPTION 0x406D1388
725
726 #pragma pack(push,8)
727 typedef struct tagTHREADNAME_INFO
728 {
729 DWORD dwType; // Must be 0x1000.
730 LPCSTR szName; // Pointer to name (in user addr space).
731 DWORD dwThreadID; // Thread ID (-1=caller thread).
732 DWORD dwFlags; // Reserved for future use, must be zero.
733 } THREADNAME_INFO;
734 #pragma pack(pop)
735
736 Sleep(10);
737 THREADNAME_INFO info;
738 info.dwType = 0x1000;
739 info.szName = threadName;
740 info.dwThreadID = -1;
741 info.dwFlags = 0;
742
743 __try
744 {
745 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
746 }
747 __except(EXCEPTION_EXECUTE_HANDLER)
748 {}
749 #endif
750 }
751
setThreadName(const string & threadName)752 void AThread::setThreadName(const string &threadName)
753 {
754 if(AThread *t = getCurrentThreadObjectTS()) {
755 t->mThreadName = threadName;
756 }
757 setThreadNameInDebugger(threadName.c_str());
758 }
759
760 static const string gEmptyThreadName;
getThreadName()761 const string &AThread::getThreadName()
762 {
763 const AThread * const t = getCurrentThreadObjectTS();
764 return t ? t->mThreadName : gEmptyThreadName;
765 }
766
getCurrentThread()767 AThread *AThread::getCurrentThread()
768 {
769 return getCurrentThreadObjectTS();
770 }
771
772 // ======================================================
773
CThreadSpecificObject()774 CThreadSpecificObject::CThreadSpecificObject()
775 : mKey(0)
776 {
777 }
778
~CThreadSpecificObject()779 CThreadSpecificObject::~CThreadSpecificObject()
780 {
781 if(mKey > 0) {
782 // make sure we're not in the list any longer
783 try {
784 unsigned tmp = mKey;
785
786 // indicate to setSpecific not to delete this object
787 mKey = 0;
788 AThread::setSpecific(tmp, 0);
789 } catch(...) {
790 /* oh well */
791 }
792 }
793 }
794
795