]> git.saurik.com Git - wxWidgets.git/blob - src/os2/thread.cpp
OS/2 Updates
[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 gs_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 *gs_pCritsectGui = NULL;
62
63 // critical section which protects s_nWaitingForGui variable
64 static wxCriticalSection *gs_pCritsectWaitingForGui = NULL;
65
66 // number of threads waiting for GUI in wxMutexGuiEnter()
67 static size_t gs_nWaitingForGui = 0;
68
69 // are we waiting for a thread termination?
70 static bool gs_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 m_internal = new wxMutexInternal;
90 ulrc = ::DosCreateMutexSem(NULL, &m_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(m_internal->m_vMutex);
103 m_internal->m_vMutex = NULL;
104 }
105
106 wxMutexError wxMutex::Lock()
107 {
108 APIRET ulrc;
109
110 ulrc = ::DosRequestMutexSem(m_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(m_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(m_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 inline wxConditionInternal ()
171 {
172 ::DosCreateEventSem(NULL, &m_vEvent, DC_SEM_SHARED, FALSE);
173 if (!m_vEvent)
174 {
175 wxLogSysError(_("Can not create event semaphore."));
176 }
177 m_nWaiters = 0;
178 }
179
180 inline bool Wait(
181 unsigned long ulTimeout
182 )
183 {
184 APIRET ulrc;
185
186 m_nWaiters++;
187 ulrc = ::DosWaitEventSem(m_vEvent, ulTimeout);
188 m_nWaiters--;
189 return (ulrc != ERROR_TIMEOUT);
190 }
191
192 inline ~wxConditionInternal ()
193 {
194 APIRET ulrc;
195
196 if (m_vEvent)
197 {
198 ulrc = ::DosCloseEventSem(m_vEvent);
199 if (!ulrc)
200 {
201 wxLogLastError("DosCloseEventSem(m_vEvent)");
202 }
203 }
204 }
205
206 HEV m_vEvent;
207 int m_nWaiters;
208 };
209
210 wxCondition::wxCondition()
211 {
212 APIRET ulrc;
213 ULONG ulCount;
214
215 m_internal = new wxConditionInternal;
216 ulrc = ::DosCreateEventSem(NULL, &m_internal->m_vEvent, 0L, FALSE);
217 if (ulrc != 0)
218 {
219 wxLogSysError(_("Can not create event object."));
220 }
221 m_internal->m_nWaiters = 0;
222 // ?? just for good measure?
223 ::DosResetEventSem(m_internal->m_vEvent, &ulCount);
224 }
225
226 wxCondition::~wxCondition()
227 {
228 ::DosCloseEventSem(m_internal->m_vEvent);
229 delete m_internal;
230 m_internal = NULL;
231 }
232
233 void wxCondition::Wait()
234 {
235 (void)m_internal->Wait(SEM_INDEFINITE_WAIT);
236 }
237
238 bool wxCondition::Wait(
239 unsigned long lSec
240 , unsigned long lNsec)
241 {
242 return m_internal->Wait(lSec*1000 + lNsec/1000000);
243 }
244
245 void wxCondition::Signal()
246 {
247 ::DosPostEventSem(m_internal->m_vEvent);
248 }
249
250 void wxCondition::Broadcast()
251 {
252 int i;
253
254 for (i = 0; i < m_internal->m_nWaiters; i++)
255 {
256 if (::DosPostEventSem(m_internal->m_vEvent) != 0)
257 {
258 wxLogSysError(_("Couldn't change the state of event object."));
259 }
260 }
261 }
262
263 // ----------------------------------------------------------------------------
264 // wxCriticalSection implementation
265 // ----------------------------------------------------------------------------
266
267 wxCriticalSection::wxCriticalSection()
268 {
269 }
270
271 wxCriticalSection::~wxCriticalSection()
272 {
273 }
274
275 void wxCriticalSection::Enter()
276 {
277 ::DosEnterCritSec();
278 }
279
280 void wxCriticalSection::Leave()
281 {
282 ::DosExitCritSec();
283 }
284
285 // ----------------------------------------------------------------------------
286 // wxThread implementation
287 // ----------------------------------------------------------------------------
288
289 // wxThreadInternal class
290 // ----------------------
291
292 class wxThreadInternal
293 {
294 public:
295 inline wxThreadInternal()
296 {
297 m_hThread = 0;
298 m_eState = STATE_NEW;
299 m_nPriority = 0;
300 }
301
302 ~wxThreadInternal()
303 {
304 Free();
305 }
306
307 void Free()
308 {
309 if (m_hThread)
310 {
311 ::DosExit(0,0);
312 m_hThread = 0;
313 }
314 }
315
316 // create a new (suspended) thread (for the given thread object)
317 bool Create(wxThread* pThread);
318
319 // suspend/resume/terminate
320 bool Suspend();
321 bool Resume();
322 inline void Cancel() { m_eState = STATE_CANCELED; }
323
324 // thread state
325 inline void SetState(wxThreadState eState) { m_eState = eState; }
326 inline wxThreadState GetState() const { return m_eState; }
327
328 // thread priority
329 void SetPriority(unsigned int nPriority);
330 inline unsigned int GetPriority() const { return m_nPriority; }
331
332 // thread handle and id
333 inline TID GetHandle() const { return m_hThread; }
334 TID GetId() const { return m_hThread; }
335
336 // thread function
337 static DWORD OS2ThreadStart(wxThread *thread);
338
339 private:
340 // Threads in OS/2 have only an ID, so m_hThread is both it's handle and ID
341 // PM also has no real Tls mechanism to index pointers by so we'll just
342 // keep track of the wxWindows parent object here.
343 TID m_hThread; // handle and ID of the thread
344 wxThreadState m_eState; // state, see wxThreadState enum
345 unsigned int m_nPriority; // thread priority in "wx" units
346 };
347
348 ULONG wxThreadInternal::OS2ThreadStart(
349 wxThread* pThread
350 )
351 {
352 m_pThread = pThread;
353
354 DWORD dwRet = (DWORD)pThread->Entry();
355
356 // enter m_critsect before changing the thread state
357 pThread->m_critsect.Enter();
358
359 bool bWasCancelled = pThread->m_internal->GetState() == STATE_CANCELED;
360
361 pThread->m_internal->SetState(STATE_EXITED);
362 pThread->m_critsect.Leave();
363
364 pThread->OnExit();
365
366 // if the thread was cancelled (from Delete()), then it the handle is still
367 // needed there
368 if (pThread->IsDetached() && !bWasCancelled)
369 {
370 // auto delete
371 delete pThread;
372 }
373 //else: the joinable threads handle will be closed when Wait() is done
374 return dwRet;
375 }
376
377 void wxThreadInternal::SetPriority(
378 unsigned int nPriority
379 )
380 {
381 // translate wxWindows priority to the PM one
382 ULONG ulOS2_Priority;
383 ULONG ulrc;
384
385 m_nPriority = nPriority;
386
387 if (m_nPriority <= 20)
388 ulOS2_Priority = PRTYC_NOCHANGE;
389 else if (m_nPriority <= 40)
390 ulOS2_Priority = PRTYC_IDLETIME;
391 else if (m_nPriority <= 60)
392 ulOS2_Priority = PRTYC_REGULAR;
393 else if (m_nPriority <= 80)
394 ulOS2_Priority = PRTYC_TIMECRITICAL;
395 else if (m_nPriority <= 100)
396 ulOS2_Priority = PRTYC_FOREGROUNDSERVER;
397 else
398 {
399 wxFAIL_MSG(wxT("invalid value of thread priority parameter"));
400 ulOS2_Priority = PRTYC_REGULAR;
401 }
402 ulrc = ::DosSetPriority( PRTYS_THREAD
403 ,ulOS2_Priority
404 ,0
405 ,(ULONG)m_hThread
406 );
407 if (ulrc != 0)
408 {
409 wxLogSysError(_("Can't set thread priority"));
410 }
411 }
412
413 bool wxThreadInternal::Create(
414 wxThread* pThread
415 )
416 {
417 APIRET ulrc;
418
419 ulrc = ::DosCreateThread( &m_hThread
420 ,(PFNTHREAD)wxThreadInternal::OS2ThreadStart
421 ,(ULONG)pThread
422 ,CREATE_SUSPENDED | STACK_SPARSE
423 ,8192L
424 );
425 if(ulrc != 0)
426 {
427 wxLogSysError(_("Can't create thread"));
428
429 return FALSE;
430 }
431 if (m_nPriority != WXTHREAD_DEFAULT_PRIORITY)
432 {
433 SetPriority(m_nPriority);
434 }
435 return(TRUE);
436 }
437
438 bool wxThreadInternal::Suspend()
439 {
440 ULONG ulrc = ::DosSuspendThread(m_hThread);
441
442 if (ulrc != 0)
443 {
444 wxLogSysError(_("Can not suspend thread %lu"), m_hThread);
445 return FALSE;
446 }
447 m_eState = STATE_PAUSED;
448 return TRUE;
449 }
450
451 bool wxThreadInternal::Resume()
452 {
453 ULONG ulrc = ::DosResumeThread(m_hThread);
454
455 if (ulrc != 0)
456 {
457 wxLogSysError(_("Can not suspend thread %lu"), m_hThread);
458 return FALSE;
459 }
460 m_eState = STATE_PAUSED;
461 return TRUE;
462 }
463
464 // static functions
465 // ----------------
466
467 wxThread *wxThread::This()
468 {
469 wxThread* pThread = m_pThread;
470 return pThread;
471 }
472
473 bool wxThread::IsMain()
474 {
475 PTIB ptib;
476 PPIB ppib;
477
478 ::DosGetInfoBlocks(&ptib, &ppib);
479
480 if (ptib->tib_ptib2->tib2_ultid == s_ulIdMainThread)
481 return TRUE;
482 return FALSE;
483 }
484
485 #ifdef Yield
486 #undef Yield
487 #endif
488
489 void wxThread::Yield()
490 {
491 ::DosSleep(0);
492 }
493
494 void wxThread::Sleep(
495 unsigned long ulMilliseconds
496 )
497 {
498 ::DosSleep(ulMilliseconds);
499 }
500
501 // ctor and dtor
502 // -------------
503
504 wxThread::wxThread(wxThreadKind kind)
505 {
506 m_internal = new wxThreadInternal();
507
508 m_isDetached = kind == wxTHREAD_DETACHED;
509 }
510
511 wxThread::~wxThread()
512 {
513 delete m_internal;
514 }
515
516 // create/start thread
517 // -------------------
518
519 wxThreadError wxThread::Create()
520 {
521 if ( !m_internal->Create(this) )
522 return wxTHREAD_NO_RESOURCE;
523
524 return wxTHREAD_NO_ERROR;
525 }
526
527 wxThreadError wxThread::Run()
528 {
529 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
530
531 if ( m_internal->GetState() != STATE_NEW )
532 {
533 // actually, it may be almost any state at all, not only STATE_RUNNING
534 return wxTHREAD_RUNNING;
535 }
536 return Resume();
537 }
538
539 // suspend/resume thread
540 // ---------------------
541
542 wxThreadError wxThread::Pause()
543 {
544 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
545
546 return m_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
547 }
548
549 wxThreadError wxThread::Resume()
550 {
551 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
552
553 return m_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
554 }
555
556 // stopping thread
557 // ---------------
558
559 wxThread::ExitCode wxThread::Wait()
560 {
561 // although under Windows we can wait for any thread, it's an error to
562 // wait for a detached one in wxWin API
563 wxCHECK_MSG( !IsDetached(), (ExitCode)-1,
564 _T("can't wait for detached thread") );
565 ExitCode rc = (ExitCode)-1;
566 (void)Delete(&rc);
567 m_internal->Free();
568 return(rc);
569 }
570
571 wxThreadError wxThread::Delete(ExitCode *pRc)
572 {
573 ExitCode rc = 0;
574
575 // Delete() is always safe to call, so consider all possible states
576 if (IsPaused())
577 Resume();
578
579 TID hThread = m_internal->GetHandle();
580
581 if (IsRunning())
582 {
583 if (IsMain())
584 {
585 // set flag for wxIsWaitingForThread()
586 gs_bWaitingForThread = TRUE;
587
588 #if wxUSE_GUI
589 wxBeginBusyCursor();
590 #endif // wxUSE_GUI
591 }
592
593 // ask the thread to terminate
594 {
595 wxCriticalSectionLocker lock(m_critsect);
596 m_internal->Cancel();
597 }
598
599 #if wxUSE_GUI
600 // need a way to finish GUI processing before killing the thread
601 // until then we just exit
602
603 if ((gs_nWaitingForGui > 0) && wxGuiOwnedByMainThread())
604 {
605 wxMutexGuiLeave();
606 }
607 #else // !wxUSE_GUI
608
609 // can't wait for yourself to end under OS/2 so just quit
610
611 #endif // wxUSE_GUI/!wxUSE_GUI
612
613 if ( IsMain() )
614 {
615 gs_bWaitingForThread = FALSE;
616
617 #if wxUSE_GUI
618 wxEndBusyCursor();
619 #endif // wxUSE_GUI
620 }
621 }
622
623 ::DosExit(0, 0);
624 // probably won't get this far, but
625 if (IsDetached())
626 {
627 delete this;
628 }
629
630 if ( pRc )
631 *pRc = rc;
632
633 return rc == (ExitCode)-1 ? wxTHREAD_MISC_ERROR : wxTHREAD_NO_ERROR;
634 }
635
636 wxThreadError wxThread::Kill()
637 {
638 if (!IsRunning())
639 return wxTHREAD_NOT_RUNNING;
640
641 ::DosKillThread(m_internal->GetHandle());
642 m_internal->Free();
643 if (IsDetached())
644 {
645 delete this;
646 }
647 return wxTHREAD_NO_ERROR;
648 }
649
650 void wxThread::Exit(
651 ExitCode pStatus
652 )
653 {
654 m_internal->Free();
655 delete this;
656 ::DosExit(EXIT_THREAD, ULONG(pStatus));
657 wxFAIL_MSG(wxT("Couldn't return from DosExit()!"));
658 }
659
660 void wxThread::SetPriority(
661 unsigned int nPrio
662 )
663 {
664 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
665
666 m_internal->SetPriority(nPrio);
667 }
668
669 unsigned int wxThread::GetPriority() const
670 {
671 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
672
673 return m_internal->GetPriority();
674 }
675
676 unsigned long wxThread::GetId() const
677 {
678 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
679
680 return (unsigned long)m_internal->GetId();
681 }
682
683 bool wxThread::IsRunning() const
684 {
685 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
686
687 return(m_internal->GetState() == STATE_RUNNING);
688 }
689
690 bool wxThread::IsAlive() const
691 {
692 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
693
694 return (m_internal->GetState() == STATE_RUNNING) ||
695 (m_internal->GetState() == STATE_PAUSED);
696 }
697
698 bool wxThread::IsPaused() const
699 {
700 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
701
702 return (m_internal->GetState() == STATE_PAUSED);
703 }
704
705 bool wxThread::TestDestroy()
706 {
707 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
708
709 return m_internal->GetState() == STATE_CANCELED;
710 }
711
712 // ----------------------------------------------------------------------------
713 // Automatic initialization for thread module
714 // ----------------------------------------------------------------------------
715
716 class wxThreadModule : public wxModule
717 {
718 public:
719 virtual bool OnInit();
720 virtual void OnExit();
721
722 private:
723 DECLARE_DYNAMIC_CLASS(wxThreadModule)
724 };
725
726 IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
727
728 bool wxThreadModule::OnInit()
729 {
730 gs_pCritsectWaitingForGui = new wxCriticalSection();
731
732 gs_pCritsectGui = new wxCriticalSection();
733 gs_pCritsectGui->Enter();
734
735 PTIB ptib;
736 PPIB ppib;
737
738 ::DosGetInfoBlocks(&ptib, &ppib);
739
740 s_ulIdMainThread = ptib->tib_ptib2->tib2_ultid;
741 return TRUE;
742 }
743
744 void wxThreadModule::OnExit()
745 {
746 if (gs_pCritsectGui)
747 {
748 gs_pCritsectGui->Leave();
749 delete gs_pCritsectGui;
750 gs_pCritsectGui = NULL;
751 }
752
753 wxDELETE(gs_pCritsectWaitingForGui);
754 }
755
756 // ----------------------------------------------------------------------------
757 // Helper functions
758 // ----------------------------------------------------------------------------
759
760 // Does nothing under OS/2 [for now]
761 void WXDLLEXPORT wxWakeUpMainThread()
762 {
763 }
764
765 void WXDLLEXPORT wxMutexGuiLeave()
766 {
767 wxCriticalSectionLocker enter(*gs_pCritsectWaitingForGui);
768
769 if ( wxThread::IsMain() )
770 {
771 gs_bGuiOwnedByMainThread = FALSE;
772 }
773 else
774 {
775 // decrement the number of waiters now
776 wxASSERT_MSG(gs_nWaitingForGui > 0,
777 wxT("calling wxMutexGuiLeave() without entering it first?") );
778
779 gs_nWaitingForGui--;
780
781 wxWakeUpMainThread();
782 }
783
784 gs_pCritsectGui->Leave();
785 }
786
787 void WXDLLEXPORT wxMutexGuiLeaveOrEnter()
788 {
789 wxASSERT_MSG( wxThread::IsMain(),
790 wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") );
791
792 wxCriticalSectionLocker enter(*gs_pCritsectWaitingForGui);
793
794 if (gs_nWaitingForGui == 0)
795 {
796 // no threads are waiting for GUI - so we may acquire the lock without
797 // any danger (but only if we don't already have it)
798 if (!wxGuiOwnedByMainThread())
799 {
800 gs_pCritsectGui->Enter();
801
802 gs_bGuiOwnedByMainThread = TRUE;
803 }
804 //else: already have it, nothing to do
805 }
806 else
807 {
808 // some threads are waiting, release the GUI lock if we have it
809 if (wxGuiOwnedByMainThread())
810 {
811 wxMutexGuiLeave();
812 }
813 //else: some other worker thread is doing GUI
814 }
815 }
816
817 bool WXDLLEXPORT wxGuiOwnedByMainThread()
818 {
819 return gs_bGuiOwnedByMainThread;
820 }
821
822 bool WXDLLEXPORT wxIsWaitingForThread()
823 {
824 return gs_bWaitingForThread;
825 }
826
827 #endif
828 // wxUSE_THREADS