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