source: vendor/synergy/current/lib/arch/CArchMultithreadWindows.cpp

Last change on this file was 2749, checked in by bird, 19 years ago

synergy v1.3.1 sources (zip).

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