source: trunk/synergy/lib/arch/CArchMultithreadOS2.cpp@ 2752

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

Two classes (CPMScreen and CPMKeyState) + the hook dll left (and debugging of course).

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