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