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