1 /*
2  * barrier -- mouse and keyboard sharing utility
3  * Copyright (C) 2012-2016 Symless Ltd.
4  * Copyright (C) 2002 Chris Schoeneman
5  *
6  * This package is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * found in the file LICENSE that should have accompanied this file.
9  *
10  * This package is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #if defined(_MSC_VER) && !defined(_MT)
20 #    error multithreading compile option is required
21 #endif
22 
23 #include "arch/win32/ArchMultithreadWindows.h"
24 #include "arch/Arch.h"
25 #include "arch/XArch.h"
26 
27 #include <process.h>
28 
29 //
30 // note -- implementation of condition variable taken from:
31 //   http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
32 // titled "Strategies for Implementing POSIX Condition Variables
33 // on Win32."  it also provides an implementation that doesn't
34 // suffer from the incorrectness problem described in our
35 // corresponding header but it is slower, still unfair, and
36 // can cause busy waiting.
37 //
38 
39 //
40 // ArchThreadImpl
41 //
42 
43 class ArchThreadImpl {
44 public:
45     ArchThreadImpl();
46     ~ArchThreadImpl();
47 
48 public:
49     int                    m_refCount;
50     HANDLE                m_thread;
51     DWORD                m_id;
52     IArchMultithread::ThreadFunc    m_func;
53     void*                m_userData;
54     HANDLE                m_cancel;
55     bool                m_cancelling;
56     HANDLE                m_exit;
57     void*                m_result;
58     void*                m_networkData;
59 };
60 
ArchThreadImpl()61 ArchThreadImpl::ArchThreadImpl() :
62     m_refCount(1),
63     m_thread(NULL),
64     m_id(0),
65     m_func(NULL),
66     m_userData(NULL),
67     m_cancelling(false),
68     m_result(NULL),
69     m_networkData(NULL)
70 {
71     m_exit   = CreateEvent(NULL, TRUE, FALSE, NULL);
72     m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL);
73 }
74 
~ArchThreadImpl()75 ArchThreadImpl::~ArchThreadImpl()
76 {
77     CloseHandle(m_exit);
78     CloseHandle(m_cancel);
79 }
80 
81 
82 //
83 // ArchMultithreadWindows
84 //
85 
86 ArchMultithreadWindows*    ArchMultithreadWindows::s_instance = NULL;
87 
ArchMultithreadWindows()88 ArchMultithreadWindows::ArchMultithreadWindows()
89 {
90     assert(s_instance == NULL);
91     s_instance = this;
92 
93     // no signal handlers
94     for (size_t i = 0; i < kNUM_SIGNALS; ++i) {
95         m_signalFunc[i]     = NULL;
96         m_signalUserData[i] = NULL;
97     }
98 
99     // create mutex for thread list
100     m_threadMutex = newMutex();
101 
102     // create thread for calling (main) thread and add it to our
103     // list.  no need to lock the mutex since we're the only thread.
104     m_mainThread           = new ArchThreadImpl;
105     m_mainThread->m_thread = NULL;
106     m_mainThread->m_id     = GetCurrentThreadId();
107     insert(m_mainThread);
108 }
109 
~ArchMultithreadWindows()110 ArchMultithreadWindows::~ArchMultithreadWindows()
111 {
112     s_instance = NULL;
113 
114     // clean up thread list
115     for (ThreadList::iterator index  = m_threadList.begin();
116                                index != m_threadList.end(); ++index) {
117         delete *index;
118     }
119 
120     // done with mutex
121     delete m_threadMutex;
122 }
123 
124 void
setNetworkDataForCurrentThread(void * data)125 ArchMultithreadWindows::setNetworkDataForCurrentThread(void* data)
126 {
127     lockMutex(m_threadMutex);
128     ArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
129     thread->m_networkData = data;
130     unlockMutex(m_threadMutex);
131 }
132 
133 void*
getNetworkDataForThread(ArchThread thread)134 ArchMultithreadWindows::getNetworkDataForThread(ArchThread thread)
135 {
136     lockMutex(m_threadMutex);
137     void* data = thread->m_networkData;
138     unlockMutex(m_threadMutex);
139     return data;
140 }
141 
142 HANDLE
getCancelEventForCurrentThread()143 ArchMultithreadWindows::getCancelEventForCurrentThread()
144 {
145     lockMutex(m_threadMutex);
146     ArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
147     unlockMutex(m_threadMutex);
148     return thread->m_cancel;
149 }
150 
151 ArchMultithreadWindows*
getInstance()152 ArchMultithreadWindows::getInstance()
153 {
154     return s_instance;
155 }
156 
157 ArchCond
newCondVar()158 ArchMultithreadWindows::newCondVar()
159 {
160     ArchCondImpl* cond                       = new ArchCondImpl;
161     cond->m_events[ArchCondImpl::kSignal]    = CreateEvent(NULL,
162                                                     FALSE, FALSE, NULL);
163     cond->m_events[ArchCondImpl::kBroadcast] = CreateEvent(NULL,
164                                                     TRUE,  FALSE, NULL);
165     cond->m_waitCountMutex                    = newMutex();
166     cond->m_waitCount                         = 0;
167     return cond;
168 }
169 
170 void
closeCondVar(ArchCond cond)171 ArchMultithreadWindows::closeCondVar(ArchCond cond)
172 {
173     CloseHandle(cond->m_events[ArchCondImpl::kSignal]);
174     CloseHandle(cond->m_events[ArchCondImpl::kBroadcast]);
175     closeMutex(cond->m_waitCountMutex);
176     delete cond;
177 }
178 
179 void
signalCondVar(ArchCond cond)180 ArchMultithreadWindows::signalCondVar(ArchCond cond)
181 {
182     // is anybody waiting?
183     lockMutex(cond->m_waitCountMutex);
184     const bool hasWaiter = (cond->m_waitCount > 0);
185     unlockMutex(cond->m_waitCountMutex);
186 
187     // wake one thread if anybody is waiting
188     if (hasWaiter) {
189         SetEvent(cond->m_events[ArchCondImpl::kSignal]);
190     }
191 }
192 
193 void
broadcastCondVar(ArchCond cond)194 ArchMultithreadWindows::broadcastCondVar(ArchCond cond)
195 {
196     // is anybody waiting?
197     lockMutex(cond->m_waitCountMutex);
198     const bool hasWaiter = (cond->m_waitCount > 0);
199     unlockMutex(cond->m_waitCountMutex);
200 
201     // wake all threads if anybody is waiting
202     if (hasWaiter) {
203         SetEvent(cond->m_events[ArchCondImpl::kBroadcast]);
204     }
205 }
206 
207 bool
waitCondVar(ArchCond cond,ArchMutex mutex,double timeout)208 ArchMultithreadWindows::waitCondVar(ArchCond cond,
209                             ArchMutex mutex, double timeout)
210 {
211     // prepare to wait
212     const DWORD winTimeout = (timeout < 0.0) ? INFINITE :
213                                 static_cast<DWORD>(1000.0 * timeout);
214 
215     // make a list of the condition variable events and the cancel event
216     // for the current thread.
217     HANDLE handles[4];
218     handles[0] = cond->m_events[ArchCondImpl::kSignal];
219     handles[1] = cond->m_events[ArchCondImpl::kBroadcast];
220     handles[2] = getCancelEventForCurrentThread();
221 
222     // update waiter count
223     lockMutex(cond->m_waitCountMutex);
224     ++cond->m_waitCount;
225     unlockMutex(cond->m_waitCountMutex);
226 
227     // release mutex.  this should be atomic with the wait so that it's
228     // impossible for another thread to signal us between the unlock and
229     // the wait, which would lead to a lost signal on broadcasts.
230     // however, we're using a manual reset event for broadcasts which
231     // stays set until we reset it, so we don't lose the broadcast.
232     unlockMutex(mutex);
233 
234     // wait for a signal or broadcast
235     // TODO: this doesn't always signal when kill signals are sent
236     DWORD result = WaitForMultipleObjects(3, handles, FALSE, winTimeout);
237 
238     // cancel takes priority
239     if (result != WAIT_OBJECT_0 + 2 &&
240         WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) {
241         result = WAIT_OBJECT_0 + 2;
242     }
243 
244     // update the waiter count and check if we're the last waiter
245     lockMutex(cond->m_waitCountMutex);
246     --cond->m_waitCount;
247     const bool last = (result == WAIT_OBJECT_0 + 1 && cond->m_waitCount == 0);
248     unlockMutex(cond->m_waitCountMutex);
249 
250     // reset the broadcast event if we're the last waiter
251     if (last) {
252         ResetEvent(cond->m_events[ArchCondImpl::kBroadcast]);
253     }
254 
255     // reacquire the mutex
256     lockMutex(mutex);
257 
258     // cancel thread if necessary
259     if (result == WAIT_OBJECT_0 + 2) {
260         ARCH->testCancelThread();
261     }
262 
263     // return success or failure
264     return (result == WAIT_OBJECT_0 + 0 ||
265             result == WAIT_OBJECT_0 + 1);
266 }
267 
268 ArchMutex
newMutex()269 ArchMultithreadWindows::newMutex()
270 {
271     ArchMutexImpl* mutex = new ArchMutexImpl;
272     InitializeCriticalSection(&mutex->m_mutex);
273     return mutex;
274 }
275 
276 void
closeMutex(ArchMutex mutex)277 ArchMultithreadWindows::closeMutex(ArchMutex mutex)
278 {
279     DeleteCriticalSection(&mutex->m_mutex);
280     delete mutex;
281 }
282 
283 void
lockMutex(ArchMutex mutex)284 ArchMultithreadWindows::lockMutex(ArchMutex mutex)
285 {
286     EnterCriticalSection(&mutex->m_mutex);
287 }
288 
289 void
unlockMutex(ArchMutex mutex)290 ArchMultithreadWindows::unlockMutex(ArchMutex mutex)
291 {
292     LeaveCriticalSection(&mutex->m_mutex);
293 }
294 
295 ArchThread
newThread(ThreadFunc func,void * data)296 ArchMultithreadWindows::newThread(ThreadFunc func, void* data)
297 {
298     lockMutex(m_threadMutex);
299 
300     // create thread impl for new thread
301     ArchThreadImpl* thread = new ArchThreadImpl;
302     thread->m_func          = func;
303     thread->m_userData      = data;
304 
305     // create thread
306     unsigned int id = 0;
307     thread->m_thread = reinterpret_cast<HANDLE>(_beginthreadex(NULL, 0,
308                                 threadFunc, (void*)thread, 0, &id));
309     thread->m_id     = static_cast<DWORD>(id);
310 
311     // check if thread was started
312     if (thread->m_thread == 0) {
313         // failed to start thread so clean up
314         delete thread;
315         thread = NULL;
316     }
317     else {
318         // add thread to list
319         insert(thread);
320 
321         // increment ref count to account for the thread itself
322         refThread(thread);
323     }
324 
325     // note that the child thread will wait until we release this mutex
326     unlockMutex(m_threadMutex);
327 
328     return thread;
329 }
330 
331 ArchThread
newCurrentThread()332 ArchMultithreadWindows::newCurrentThread()
333 {
334     lockMutex(m_threadMutex);
335     ArchThreadImpl* thread = find(GetCurrentThreadId());
336     unlockMutex(m_threadMutex);
337     assert(thread != NULL);
338     return thread;
339 }
340 
341 void
closeThread(ArchThread thread)342 ArchMultithreadWindows::closeThread(ArchThread thread)
343 {
344     assert(thread != NULL);
345 
346     // decrement ref count and clean up thread if no more references
347     if (--thread->m_refCount == 0) {
348         // close the handle (main thread has a NULL handle)
349         if (thread->m_thread != NULL) {
350             CloseHandle(thread->m_thread);
351         }
352 
353         // remove thread from list
354         lockMutex(m_threadMutex);
355         assert(findNoRefOrCreate(thread->m_id) == thread);
356         erase(thread);
357         unlockMutex(m_threadMutex);
358 
359         // done with thread
360         delete thread;
361     }
362 }
363 
364 ArchThread
copyThread(ArchThread thread)365 ArchMultithreadWindows::copyThread(ArchThread thread)
366 {
367     refThread(thread);
368     return thread;
369 }
370 
371 void
cancelThread(ArchThread thread)372 ArchMultithreadWindows::cancelThread(ArchThread thread)
373 {
374     assert(thread != NULL);
375 
376     // set cancel flag
377     SetEvent(thread->m_cancel);
378 }
379 
380 void
setPriorityOfThread(ArchThread thread,int n)381 ArchMultithreadWindows::setPriorityOfThread(ArchThread thread, int n)
382 {
383     struct PriorityInfo {
384     public:
385         DWORD        m_class;
386         int            m_level;
387     };
388     static const PriorityInfo s_pClass[] = {
389         { IDLE_PRIORITY_CLASS,     THREAD_PRIORITY_IDLE         },
390         { IDLE_PRIORITY_CLASS,     THREAD_PRIORITY_LOWEST       },
391         { IDLE_PRIORITY_CLASS,     THREAD_PRIORITY_BELOW_NORMAL },
392         { IDLE_PRIORITY_CLASS,     THREAD_PRIORITY_NORMAL       },
393         { IDLE_PRIORITY_CLASS,     THREAD_PRIORITY_ABOVE_NORMAL },
394         { IDLE_PRIORITY_CLASS,     THREAD_PRIORITY_HIGHEST      },
395         { NORMAL_PRIORITY_CLASS,   THREAD_PRIORITY_LOWEST       },
396         { NORMAL_PRIORITY_CLASS,   THREAD_PRIORITY_BELOW_NORMAL },
397         { NORMAL_PRIORITY_CLASS,   THREAD_PRIORITY_NORMAL       },
398         { NORMAL_PRIORITY_CLASS,   THREAD_PRIORITY_ABOVE_NORMAL },
399         { NORMAL_PRIORITY_CLASS,   THREAD_PRIORITY_HIGHEST      },
400         { HIGH_PRIORITY_CLASS,     THREAD_PRIORITY_LOWEST       },
401         { HIGH_PRIORITY_CLASS,     THREAD_PRIORITY_BELOW_NORMAL },
402         { HIGH_PRIORITY_CLASS,     THREAD_PRIORITY_NORMAL       },
403         { HIGH_PRIORITY_CLASS,     THREAD_PRIORITY_ABOVE_NORMAL },
404         { HIGH_PRIORITY_CLASS,     THREAD_PRIORITY_HIGHEST      },
405         { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_IDLE         },
406         { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST       },
407         { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL },
408         { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL       },
409         { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL },
410         { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST      },
411         { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_TIME_CRITICAL}
412     };
413 #if defined(_DEBUG)
414     // don't use really high priorities when debugging
415     static const size_t s_pMax  = 13;
416 #else
417     static const size_t s_pMax  = sizeof(s_pClass) / sizeof(s_pClass[0]) - 1;
418 #endif
419     static const size_t s_pBase = 8;    // index of normal priority
420 
421     assert(thread != NULL);
422 
423     size_t index;
424     if (n > 0 && s_pBase < (size_t)n) {
425         // lowest priority
426         index = 0;
427     }
428     else {
429         index = (size_t)((int)s_pBase - n);
430         if (index > s_pMax) {
431             // highest priority
432             index = s_pMax;
433         }
434     }
435     SetPriorityClass(GetCurrentProcess(), s_pClass[index].m_class);
436     SetThreadPriority(thread->m_thread, s_pClass[index].m_level);
437 }
438 
439 void
testCancelThread()440 ArchMultithreadWindows::testCancelThread()
441 {
442     // find current thread
443     lockMutex(m_threadMutex);
444     ArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
445     unlockMutex(m_threadMutex);
446 
447     // test cancel on thread
448     testCancelThreadImpl(thread);
449 }
450 
451 bool
wait(ArchThread target,double timeout)452 ArchMultithreadWindows::wait(ArchThread target, double timeout)
453 {
454     assert(target != NULL);
455 
456     lockMutex(m_threadMutex);
457 
458     // find current thread
459     ArchThreadImpl* self = findNoRef(GetCurrentThreadId());
460 
461     // ignore wait if trying to wait on ourself
462     if (target == self) {
463         unlockMutex(m_threadMutex);
464         return false;
465     }
466 
467     // ref the target so it can't go away while we're watching it
468     refThread(target);
469 
470     unlockMutex(m_threadMutex);
471 
472     // convert timeout
473     DWORD t;
474     if (timeout < 0.0) {
475         t = INFINITE;
476     }
477     else {
478         t = (DWORD)(1000.0 * timeout);
479     }
480 
481     // wait for this thread to be cancelled or woken up or for the
482     // target thread to terminate.
483     HANDLE handles[2];
484     handles[0] = target->m_exit;
485     handles[1] = self->m_cancel;
486     DWORD result = WaitForMultipleObjects(2, handles, FALSE, t);
487 
488     // cancel takes priority
489     if (result != WAIT_OBJECT_0 + 1 &&
490         WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) {
491         result = WAIT_OBJECT_0 + 1;
492     }
493 
494     // release target
495     closeThread(target);
496 
497     // handle result
498     switch (result) {
499     case WAIT_OBJECT_0 + 0:
500         // target thread terminated
501         return true;
502 
503     case WAIT_OBJECT_0 + 1:
504         // this thread was cancelled.  does not return.
505         testCancelThreadImpl(self);
506 
507     default:
508         // timeout or error
509         return false;
510     }
511 }
512 
513 bool
isSameThread(ArchThread thread1,ArchThread thread2)514 ArchMultithreadWindows::isSameThread(ArchThread thread1, ArchThread thread2)
515 {
516     return (thread1 == thread2);
517 }
518 
519 bool
isExitedThread(ArchThread thread)520 ArchMultithreadWindows::isExitedThread(ArchThread thread)
521 {
522     // poll exit event
523     return (WaitForSingleObject(thread->m_exit, 0) == WAIT_OBJECT_0);
524 }
525 
526 void*
getResultOfThread(ArchThread thread)527 ArchMultithreadWindows::getResultOfThread(ArchThread thread)
528 {
529     lockMutex(m_threadMutex);
530     void* result = thread->m_result;
531     unlockMutex(m_threadMutex);
532     return result;
533 }
534 
535 IArchMultithread::ThreadID
getIDOfThread(ArchThread thread)536 ArchMultithreadWindows::getIDOfThread(ArchThread thread)
537 {
538     return static_cast<ThreadID>(thread->m_id);
539 }
540 
541 void
setSignalHandler(ESignal signal,SignalFunc func,void * userData)542 ArchMultithreadWindows::setSignalHandler(
543                 ESignal signal, SignalFunc func, void* userData)
544 {
545     lockMutex(m_threadMutex);
546     m_signalFunc[signal]     = func;
547     m_signalUserData[signal] = userData;
548     unlockMutex(m_threadMutex);
549 }
550 
551 void
raiseSignal(ESignal signal)552 ArchMultithreadWindows::raiseSignal(ESignal signal)
553 {
554     lockMutex(m_threadMutex);
555     if (m_signalFunc[signal] != NULL) {
556         m_signalFunc[signal](signal, m_signalUserData[signal]);
557         ARCH->unblockPollSocket(m_mainThread);
558     }
559     else if (signal == kINTERRUPT || signal == kTERMINATE) {
560         ARCH->cancelThread(m_mainThread);
561     }
562     unlockMutex(m_threadMutex);
563 }
564 
565 ArchThreadImpl*
find(DWORD id)566 ArchMultithreadWindows::find(DWORD id)
567 {
568     ArchThreadImpl* impl = findNoRef(id);
569     if (impl != NULL) {
570         refThread(impl);
571     }
572     return impl;
573 }
574 
575 ArchThreadImpl*
findNoRef(DWORD id)576 ArchMultithreadWindows::findNoRef(DWORD id)
577 {
578     ArchThreadImpl* impl = findNoRefOrCreate(id);
579     if (impl == NULL) {
580         // create thread for calling thread which isn't in our list and
581         // add it to the list.  this won't normally happen but it can if
582         // the system calls us under a new thread, like it does when we
583         // run as a service.
584         impl           = new ArchThreadImpl;
585         impl->m_thread = NULL;
586         impl->m_id     = GetCurrentThreadId();
587         insert(impl);
588     }
589     return impl;
590 }
591 
592 ArchThreadImpl*
findNoRefOrCreate(DWORD id)593 ArchMultithreadWindows::findNoRefOrCreate(DWORD id)
594 {
595     // linear search
596     for (ThreadList::const_iterator index  = m_threadList.begin();
597                                      index != m_threadList.end(); ++index) {
598         if ((*index)->m_id == id) {
599             return *index;
600         }
601     }
602     return NULL;
603 }
604 
605 void
insert(ArchThreadImpl * thread)606 ArchMultithreadWindows::insert(ArchThreadImpl* thread)
607 {
608     assert(thread != NULL);
609 
610     // thread shouldn't already be on the list
611     assert(findNoRefOrCreate(thread->m_id) == NULL);
612 
613     // append to list
614     m_threadList.push_back(thread);
615 }
616 
617 void
erase(ArchThreadImpl * thread)618 ArchMultithreadWindows::erase(ArchThreadImpl* thread)
619 {
620     for (ThreadList::iterator index  = m_threadList.begin();
621                                index != m_threadList.end(); ++index) {
622         if (*index == thread) {
623             m_threadList.erase(index);
624             break;
625         }
626     }
627 }
628 
629 void
refThread(ArchThreadImpl * thread)630 ArchMultithreadWindows::refThread(ArchThreadImpl* thread)
631 {
632     assert(thread != NULL);
633     assert(findNoRefOrCreate(thread->m_id) != NULL);
634     ++thread->m_refCount;
635 }
636 
637 void
testCancelThreadImpl(ArchThreadImpl * thread)638 ArchMultithreadWindows::testCancelThreadImpl(ArchThreadImpl* thread)
639 {
640     assert(thread != NULL);
641 
642     // poll cancel event.  return if not set.
643     const DWORD result = WaitForSingleObject(thread->m_cancel, 0);
644     if (result != WAIT_OBJECT_0) {
645         return;
646     }
647 
648     // update cancel state
649     lockMutex(m_threadMutex);
650     bool cancel          = !thread->m_cancelling;
651     thread->m_cancelling = true;
652     ResetEvent(thread->m_cancel);
653     unlockMutex(m_threadMutex);
654 
655     // unwind thread's stack if cancelling
656     if (cancel) {
657         throw XThreadCancel();
658     }
659 }
660 
661 unsigned int __stdcall
threadFunc(void * vrep)662 ArchMultithreadWindows::threadFunc(void* vrep)
663 {
664     // get the thread
665     ArchThreadImpl* thread = static_cast<ArchThreadImpl*>(vrep);
666 
667     // run thread
668     s_instance->doThreadFunc(thread);
669 
670     // terminate the thread
671     return 0;
672 }
673 
674 void
doThreadFunc(ArchThread thread)675 ArchMultithreadWindows::doThreadFunc(ArchThread thread)
676 {
677     // wait for parent to initialize this object
678     lockMutex(m_threadMutex);
679     unlockMutex(m_threadMutex);
680 
681     void* result = NULL;
682     try {
683         // go
684         result = (*thread->m_func)(thread->m_userData);
685     }
686 
687     catch (XThreadCancel&) {
688         // client called cancel()
689     }
690     catch (...) {
691         // note -- don't catch (...) to avoid masking bugs
692         SetEvent(thread->m_exit);
693         closeThread(thread);
694         throw;
695     }
696 
697     // thread has exited
698     lockMutex(m_threadMutex);
699     thread->m_result = result;
700     unlockMutex(m_threadMutex);
701     SetEvent(thread->m_exit);
702 
703     // done with thread
704     closeThread(thread);
705 }
706