]> git.saurik.com Git - wxWidgets.git/blob - src/os2/thread.cpp
Minor tweek for WXPM
[wxWidgets.git] / src / os2 / thread.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: thread.cpp
3 // Purpose: wxThread Implementation. For Unix ports, see e.g. src/gtk
4 // Author: Original from Wolfram Gloger/Guilhem Lavaux
5 // Modified by: David Webster
6 // Created: 04/22/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ----------------------------------------------------------------------------
13 // headers
14 // ----------------------------------------------------------------------------
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #if wxUSE_THREADS
20
21 #include <stdio.h>
22
23 #include "wx/module.h"
24 #include "wx/thread.h"
25
26 #define INCL_DOSSEMAPHORES
27 #define INCL_DOSPROCESS
28 #define INCL_ERRORS
29 #include <os2.h>
30 #include <bseerr.h>
31
32 // the possible states of the thread ("=>" shows all possible transitions from
33 // this state)
34 enum wxThreadState
35 {
36 STATE_NEW, // didn't start execution yet (=> RUNNING)
37 STATE_RUNNING, // thread is running (=> PAUSED, CANCELED)
38 STATE_PAUSED, // thread is temporarily suspended (=> RUNNING)
39 STATE_CANCELED, // thread should terminate a.s.a.p. (=> EXITED)
40 STATE_EXITED // thread is terminating
41 };
42
43 // ----------------------------------------------------------------------------
44 // static variables
45 // ----------------------------------------------------------------------------
46
47 // id of the main thread - the one which can call GUI functions without first
48 // calling wxMutexGuiEnter()
49 static ULONG s_ulIdMainThread = 0;
50 wxMutex* p_wxMainMutex;
51
52 // OS2 substitute for Tls pointer the current parent thread object
53 wxThread* m_pThread; // pointer to the wxWindows thread object
54
55 // if it's FALSE, some secondary thread is holding the GUI lock
56 static bool s_bGuiOwnedByMainThread = TRUE;
57
58 // critical section which controls access to all GUI functions: any secondary
59 // thread (i.e. except the main one) must enter this crit section before doing
60 // any GUI calls
61 static wxCriticalSection *s_pCritsectGui = NULL;
62
63 // critical section which protects s_nWaitingForGui variable
64 static wxCriticalSection *s_pCritsectWaitingForGui = NULL;
65
66 // number of threads waiting for GUI in wxMutexGuiEnter()
67 static size_t s_nWaitingForGui = 0;
68
69 // are we waiting for a thread termination?
70 static bool s_bWaitingForThread = FALSE;
71
72 // ============================================================================
73 // OS/2 implementation of thread classes
74 // ============================================================================
75
76 // ----------------------------------------------------------------------------
77 // wxMutex implementation
78 // ----------------------------------------------------------------------------
79 class wxMutexInternal
80 {
81 public:
82 HMTX m_vMutex;
83 };
84
85 wxMutex::wxMutex()
86 {
87 APIRET ulrc;
88
89 p_internal = new wxMutexInternal;
90 ulrc = ::DosCreateMutexSem(NULL, &p_internal->m_vMutex, 0L, FALSE);
91 if (ulrc != 0)
92 {
93 wxLogSysError(_("Can not create mutex."));
94 }
95 m_locked = 0;
96 }
97
98 wxMutex::~wxMutex()
99 {
100 if (m_locked > 0)
101 wxLogDebug(wxT("Warning: freeing a locked mutex (%d locks)."), m_locked);
102 ::DosCloseMutexSem(p_internal->m_vMutex);
103 p_internal->m_vMutex = NULL;
104 }
105
106 wxMutexError wxMutex::Lock()
107 {
108 APIRET ulrc;
109
110 ulrc = ::DosRequestMutexSem(p_internal->m_vMutex, SEM_INDEFINITE_WAIT);
111
112 switch (ulrc)
113 {
114 case ERROR_TOO_MANY_SEM_REQUESTS:
115 return wxMUTEX_BUSY;
116
117 case NO_ERROR:
118 // ok
119 break;
120
121 case ERROR_INVALID_HANDLE:
122 case ERROR_INTERRUPT:
123 case ERROR_SEM_OWNER_DIED:
124 wxLogSysError(_("Couldn't acquire a mutex lock"));
125 return wxMUTEX_MISC_ERROR;
126
127 case ERROR_TIMEOUT:
128 default:
129 wxFAIL_MSG(wxT("impossible return value in wxMutex::Lock"));
130 }
131 m_locked++;
132 return wxMUTEX_NO_ERROR;
133 }
134
135 wxMutexError wxMutex::TryLock()
136 {
137 ULONG ulrc;
138
139 ulrc = ::DosRequestMutexSem(p_internal->m_vMutex, SEM_IMMEDIATE_RETURN /*0L*/);
140 if (ulrc == ERROR_TIMEOUT || ulrc == ERROR_TOO_MANY_SEM_REQUESTS)
141 return wxMUTEX_BUSY;
142
143 m_locked++;
144 return wxMUTEX_NO_ERROR;
145 }
146
147 wxMutexError wxMutex::Unlock()
148 {
149 APIRET ulrc;
150
151 if (m_locked > 0)
152 m_locked--;
153
154 ulrc = ::DosReleaseMutexSem(p_internal->m_vMutex);
155 if (ulrc != 0)
156 {
157 wxLogSysError(_("Couldn't release a mutex"));
158 return wxMUTEX_MISC_ERROR;
159 }
160 return wxMUTEX_NO_ERROR;
161 }
162
163 // ----------------------------------------------------------------------------
164 // wxCondition implementation
165 // ----------------------------------------------------------------------------
166
167 class wxConditionInternal
168 {
169 public:
170 HEV m_vEvent;
171 int m_nWaiters;
172 };
173
174 wxCondition::wxCondition()
175 {
176 APIRET ulrc;
177 ULONG ulCount;
178
179 p_internal = new wxConditionInternal;
180 ulrc = ::DosCreateEventSem(NULL, &p_internal->m_vEvent, 0L, FALSE);
181 if (ulrc != 0)
182 {
183 wxLogSysError(_("Can not create event object."));
184 }
185 p_internal->m_nWaiters = 0;
186 // ?? just for good measure?
187 ::DosResetEventSem(p_internal->m_vEvent, &ulCount);
188 }
189
190 wxCondition::~wxCondition()
191 {
192 ::DosCloseEventSem(p_internal->m_vEvent);
193 delete p_internal;
194 p_internal = NULL;
195 }
196
197 void wxCondition::Wait(
198 wxMutex& rMutex
199 )
200 {
201 rMutex.Unlock();
202 p_internal->m_nWaiters++;
203 ::DosWaitEventSem(p_internal->m_vEvent, SEM_INDEFINITE_WAIT);
204 p_internal->m_nWaiters--;
205 rMutex.Lock();
206 }
207
208 bool wxCondition::Wait(
209 wxMutex& rMutex
210 , unsigned long ulSec
211 , unsigned long ulMillisec)
212 {
213 APIRET ulrc;
214
215 rMutex.Unlock();
216 p_internal->m_nWaiters++;
217 ulrc = ::DosWaitEventSem(p_internal->m_vEvent, ULONG((ulSec * 1000L) + ulMillisec));
218 p_internal->m_nWaiters--;
219 rMutex.Lock();
220
221 return (ulrc != ERROR_TIMEOUT);
222 }
223
224 void wxCondition::Signal()
225 {
226 ::DosPostEventSem(p_internal->m_vEvent);
227 }
228
229 void wxCondition::Broadcast()
230 {
231 int i;
232
233 for (i = 0; i < p_internal->m_nWaiters; i++)
234 {
235 if (::DosPostEventSem(p_internal->m_vEvent) != 0)
236 {
237 wxLogSysError(_("Couldn't change the state of event object."));
238 }
239 }
240 }
241
242 // ----------------------------------------------------------------------------
243 // wxCriticalSection implementation
244 // ----------------------------------------------------------------------------
245
246 class wxCriticalSectionInternal
247 {
248 public:
249 // init the critical section object
250 wxCriticalSectionInternal()
251 { }
252
253 // free the associated ressources
254 ~wxCriticalSectionInternal()
255 { }
256
257 private:
258 };
259
260 // ----------------------------------------------------------------------------
261 // wxCriticalSection implementation
262 // ----------------------------------------------------------------------------
263
264 wxCriticalSection::wxCriticalSection()
265 {
266 m_critsect = new wxCriticalSectionInternal;
267 }
268
269 wxCriticalSection::~wxCriticalSection()
270 {
271 delete m_critsect;
272 }
273
274 void wxCriticalSection::Enter()
275 {
276 ::DosEnterCritSec();
277 }
278
279 void wxCriticalSection::Leave()
280 {
281 ::DosExitCritSec();
282 }
283
284 // ----------------------------------------------------------------------------
285 // wxThread implementation
286 // ----------------------------------------------------------------------------
287
288 // wxThreadInternal class
289 // ----------------------
290
291 class wxThreadInternal
292 {
293 public:
294 inline wxThreadInternal()
295 {
296 m_hThread = 0;
297 m_eState = STATE_NEW;
298 m_nPriority = 0;
299 }
300
301 // create a new (suspended) thread (for the given thread object)
302 bool Create(wxThread* pThread);
303
304 // suspend/resume/terminate
305 bool Suspend();
306 bool Resume();
307 inline void Cancel() { m_eState = STATE_CANCELED; }
308
309 // thread state
310 inline void SetState(wxThreadState eState) { m_eState = eState; }
311 inline wxThreadState GetState() const { return m_eState; }
312
313 // thread priority
314 inline void SetPriority(unsigned int nPriority) { m_nPriority = nPriority; }
315 inline unsigned int GetPriority() const { return m_nPriority; }
316
317 // thread handle and id
318 inline TID GetHandle() const { return m_hThread; }
319 TID GetId() const { return m_hThread; }
320
321 // thread function
322 static DWORD OS2ThreadStart(wxThread *thread);
323
324 private:
325 // Threads in OS/2 have only an ID, so m_hThread is both it's handle and ID
326 // PM also has no real Tls mechanism to index pointers by so we'll just
327 // keep track of the wxWindows parent object here.
328 TID m_hThread; // handle and ID of the thread
329 wxThreadState m_eState; // state, see wxThreadState enum
330 unsigned int m_nPriority; // thread priority in "wx" units
331 };
332
333 ULONG wxThreadInternal::OS2ThreadStart(
334 wxThread* pThread
335 )
336 {
337 m_pThread = pThread;
338
339 DWORD dwRet = (DWORD)pThread->Entry();
340
341 pThread->p_internal->SetState(STATE_EXITED);
342 pThread->OnExit();
343
344 delete pThread;
345 m_pThread = NULL;
346 return dwRet;
347 }
348
349 bool wxThreadInternal::Create(
350 wxThread* pThread
351 )
352 {
353 APIRET ulrc;
354
355 ulrc = ::DosCreateThread( &m_hThread
356 ,(PFNTHREAD)wxThreadInternal::OS2ThreadStart
357 ,(ULONG)pThread
358 ,CREATE_SUSPENDED | STACK_SPARSE
359 ,8192L
360 );
361 if(ulrc != 0)
362 {
363 wxLogSysError(_("Can't create thread"));
364
365 return FALSE;
366 }
367
368 // translate wxWindows priority to the PM one
369 ULONG ulOS2_Priority;
370
371 if (m_nPriority <= 20)
372 ulOS2_Priority = PRTYC_NOCHANGE;
373 else if (m_nPriority <= 40)
374 ulOS2_Priority = PRTYC_IDLETIME;
375 else if (m_nPriority <= 60)
376 ulOS2_Priority = PRTYC_REGULAR;
377 else if (m_nPriority <= 80)
378 ulOS2_Priority = PRTYC_TIMECRITICAL;
379 else if (m_nPriority <= 100)
380 ulOS2_Priority = PRTYC_FOREGROUNDSERVER;
381 else
382 {
383 wxFAIL_MSG(wxT("invalid value of thread priority parameter"));
384 ulOS2_Priority = PRTYC_REGULAR;
385 }
386 ulrc = ::DosSetPriority( PRTYS_THREAD
387 ,ulOS2_Priority
388 ,0
389 ,(ULONG)m_hThread
390 );
391 if (ulrc != 0)
392 {
393 wxLogSysError(_("Can't set thread priority"));
394 }
395 return TRUE;
396 }
397
398 bool wxThreadInternal::Suspend()
399 {
400 ULONG ulrc = ::DosSuspendThread(m_hThread);
401
402 if (ulrc != 0)
403 {
404 wxLogSysError(_("Can not suspend thread %lu"), m_hThread);
405 return FALSE;
406 }
407 m_eState = STATE_PAUSED;
408 return TRUE;
409 }
410
411 bool wxThreadInternal::Resume()
412 {
413 ULONG ulrc = ::DosResumeThread(m_hThread);
414
415 if (ulrc != 0)
416 {
417 wxLogSysError(_("Can not suspend thread %lu"), m_hThread);
418 return FALSE;
419 }
420 m_eState = STATE_PAUSED;
421 return TRUE;
422 }
423
424 // static functions
425 // ----------------
426
427 wxThread *wxThread::This()
428 {
429 wxThread* pThread = m_pThread;
430 return pThread;
431 }
432
433 bool wxThread::IsMain()
434 {
435 PTIB ptib;
436 PPIB ppib;
437
438 ::DosGetInfoBlocks(&ptib, &ppib);
439
440 if (ptib->tib_ptib2->tib2_ultid == s_ulIdMainThread)
441 return TRUE;
442 return FALSE;
443 }
444
445 #ifdef Yield
446 #undef Yield
447 #endif
448
449 void wxThread::Yield()
450 {
451 ::DosSleep(0);
452 }
453
454 void wxThread::Sleep(
455 unsigned long ulMilliseconds
456 )
457 {
458 ::DosSleep(ulMilliseconds);
459 }
460
461 // create/start thread
462 // -------------------
463
464 wxThreadError wxThread::Create()
465 {
466 if ( !p_internal->Create(this) )
467 return wxTHREAD_NO_RESOURCE;
468
469 return wxTHREAD_NO_ERROR;
470 }
471
472 wxThreadError wxThread::Run()
473 {
474 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
475
476 if ( p_internal->GetState() != STATE_NEW )
477 {
478 // actually, it may be almost any state at all, not only STATE_RUNNING
479 return wxTHREAD_RUNNING;
480 }
481 return Resume();
482 }
483
484 // suspend/resume thread
485 // ---------------------
486
487 wxThreadError wxThread::Pause()
488 {
489 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
490
491 return p_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
492 }
493
494 wxThreadError wxThread::Resume()
495 {
496 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
497
498 return p_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
499 }
500
501 // stopping thread
502 // ---------------
503
504 wxThread::ExitCode wxThread::Delete()
505 {
506 ExitCode rc = 0;
507 ULONG ulrc;
508
509 // Delete() is always safe to call, so consider all possible states
510 if (IsPaused())
511 Resume();
512
513 if (IsRunning())
514 {
515 if (IsMain())
516 {
517 // set flag for wxIsWaitingForThread()
518 s_bWaitingForThread = TRUE;
519 wxBeginBusyCursor();
520 }
521
522 TID hThread;
523
524 {
525 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
526
527 p_internal->Cancel();
528 hThread = p_internal->GetHandle();
529 }
530
531 // we can't just wait for the thread to terminate because it might be
532 // calling some GUI functions and so it will never terminate before we
533 // process the Windows messages that result from these functions
534
535 do
536 {
537 ulrc = ::DosWaitThread( &hThread
538 ,DCWW_NOWAIT
539 );
540 switch (ulrc)
541 {
542 case ERROR_INTERRUPT:
543 case ERROR_INVALID_THREADID:
544 // error
545 wxLogSysError(_("Can not wait for thread termination"));
546 Kill();
547 return (ExitCode)-1;
548
549 case 0:
550 // thread we're waiting for terminated
551 break;
552
553 case ERROR_THREAD_NOT_TERMINATED:
554 // new message arrived, process it
555 if (!wxTheApp->DoMessage())
556 {
557 // WM_QUIT received: kill the thread
558 Kill();
559 return (ExitCode)-1;
560 }
561 if (IsMain())
562 {
563 // give the thread we're waiting for chance to exit
564 // from the GUI call it might have been in
565 if ((s_nWaitingForGui > 0) && wxGuiOwnedByMainThread())
566 {
567 wxMutexGuiLeave();
568 }
569 }
570 break;
571
572 default:
573 wxFAIL_MSG(wxT("unexpected result of DosWatiThread"));
574 }
575 } while (ulrc != 0);
576
577 if (IsMain())
578 {
579 s_bWaitingForThread = FALSE;
580 wxEndBusyCursor();
581 }
582
583 ::DosExit(EXIT_THREAD, ulrc);
584 }
585 rc = (ExitCode)ulrc;
586 return rc;
587 }
588
589 wxThreadError wxThread::Kill()
590 {
591 if (!IsRunning())
592 return wxTHREAD_NOT_RUNNING;
593
594 ::DosKillThread(p_internal->GetHandle());
595 delete this;
596 return wxTHREAD_NO_ERROR;
597 }
598
599 void wxThread::Exit(
600 void* pStatus
601 )
602 {
603 delete this;
604 ::DosExit(EXIT_THREAD, ULONG(pStatus));
605 wxFAIL_MSG(wxT("Couldn't return from DosExit()!"));
606 }
607
608 void wxThread::SetPriority(
609 unsigned int nPrio
610 )
611 {
612 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
613
614 p_internal->SetPriority(nPrio);
615 }
616
617 unsigned int wxThread::GetPriority() const
618 {
619 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
620
621 return p_internal->GetPriority();
622 }
623
624 unsigned long wxThread::GetID() const
625 {
626 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
627
628 return (unsigned long)p_internal->GetId();
629 }
630
631 bool wxThread::IsRunning() const
632 {
633 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
634
635 return p_internal->GetState() == STATE_RUNNING;
636 }
637
638 bool wxThread::IsAlive() const
639 {
640 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
641
642 return (p_internal->GetState() == STATE_RUNNING) ||
643 (p_internal->GetState() == STATE_PAUSED);
644 }
645
646 bool wxThread::IsPaused() const
647 {
648 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
649
650 return (p_internal->GetState() == STATE_PAUSED);
651 }
652
653 bool wxThread::TestDestroy()
654 {
655 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
656
657 return p_internal->GetState() == STATE_CANCELED;
658 }
659
660 wxThread::wxThread()
661 {
662 p_internal = new wxThreadInternal();
663 }
664
665 wxThread::~wxThread()
666 {
667 delete p_internal;
668 }
669
670 // ----------------------------------------------------------------------------
671 // Automatic initialization for thread module
672 // ----------------------------------------------------------------------------
673
674 class wxThreadModule : public wxModule
675 {
676 public:
677 virtual bool OnInit();
678 virtual void OnExit();
679
680 private:
681 DECLARE_DYNAMIC_CLASS(wxThreadModule)
682 };
683
684 IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
685
686 bool wxThreadModule::OnInit()
687 {
688 s_pCritsectWaitingForGui = new wxCriticalSection();
689
690 s_pCritsectGui = new wxCriticalSection();
691 s_pCritsectGui->Enter();
692
693 PTIB ptib;
694 PPIB ppib;
695
696 ::DosGetInfoBlocks(&ptib, &ppib);
697
698 s_ulIdMainThread = ptib->tib_ptib2->tib2_ultid;
699 return TRUE;
700 }
701
702 void wxThreadModule::OnExit()
703 {
704 if (s_pCritsectGui)
705 {
706 s_pCritsectGui->Leave();
707 delete s_pCritsectGui;
708 s_pCritsectGui = NULL;
709 }
710
711 wxDELETE(s_pCritsectWaitingForGui);
712 }
713
714 // ----------------------------------------------------------------------------
715 // Helper functions
716 // ----------------------------------------------------------------------------
717
718 // Does nothing under OS/2 [for now]
719 void WXDLLEXPORT wxWakeUpMainThread()
720 {
721 }
722
723 void WXDLLEXPORT wxMutexGuiLeave()
724 {
725 wxCriticalSectionLocker enter(*s_pCritsectWaitingForGui);
726
727 if ( wxThread::IsMain() )
728 {
729 s_bGuiOwnedByMainThread = FALSE;
730 }
731 else
732 {
733 // decrement the number of waiters now
734 wxASSERT_MSG( s_nWaitingForGui > 0,
735 wxT("calling wxMutexGuiLeave() without entering it first?") );
736
737 s_nWaitingForGui--;
738
739 wxWakeUpMainThread();
740 }
741
742 s_pCritsectGui->Leave();
743 }
744
745 void WXDLLEXPORT wxMutexGuiLeaveOrEnter()
746 {
747 wxASSERT_MSG( wxThread::IsMain(),
748 wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") );
749
750 wxCriticalSectionLocker enter(*s_pCritsectWaitingForGui);
751
752 if ( s_nWaitingForGui == 0 )
753 {
754 // no threads are waiting for GUI - so we may acquire the lock without
755 // any danger (but only if we don't already have it)
756 if (!wxGuiOwnedByMainThread())
757 {
758 s_pCritsectGui->Enter();
759
760 s_bGuiOwnedByMainThread = TRUE;
761 }
762 //else: already have it, nothing to do
763 }
764 else
765 {
766 // some threads are waiting, release the GUI lock if we have it
767 if (wxGuiOwnedByMainThread())
768 {
769 wxMutexGuiLeave();
770 }
771 //else: some other worker thread is doing GUI
772 }
773 }
774
775 bool WXDLLEXPORT wxGuiOwnedByMainThread()
776 {
777 return s_bGuiOwnedByMainThread;
778 }
779
780 #endif
781 // wxUSE_THREADS