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