]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/os2/thread.cpp
fix for discrepancies between wxNotebookEvent and wxNotebook GetSelection() results
[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/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)
34enum 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()
49static ULONG s_ulIdMainThread = 0;
50wxMutex* p_wxMainMutex;
51
52// OS2 substitute for Tls pointer the current parent thread object
53wxThread* m_pThread; // pointer to the wxWindows thread object
54
55// if it's FALSE, some secondary thread is holding the GUI lock
56static 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
61static wxCriticalSection *gs_pCritsectGui = NULL;
62
63// critical section which protects s_nWaitingForGui variable
64static wxCriticalSection *gs_pCritsectWaitingForGui = NULL;
65
66// number of threads waiting for GUI in wxMutexGuiEnter()
67static size_t gs_nWaitingForGui = 0;
68
69// are we waiting for a thread termination?
70static bool gs_bWaitingForThread = FALSE;
71
72// ============================================================================
73// OS/2 implementation of thread classes
74// ============================================================================
75
76// ----------------------------------------------------------------------------
77// wxMutex implementation
78// ----------------------------------------------------------------------------
79class wxMutexInternal
80{
81public:
82 HMTX m_vMutex;
83};
84
85wxMutex::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
98wxMutex::~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
106wxMutexError 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
135wxMutexError 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
147wxMutexError 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
167class wxConditionInternal
168{
169public:
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
210wxCondition::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
226wxCondition::~wxCondition()
227{
228 ::DosCloseEventSem(m_internal->m_vEvent);
229 delete m_internal;
230 m_internal = NULL;
231}
232
233void wxCondition::Wait()
234{
235 (void)m_internal->Wait(SEM_INDEFINITE_WAIT);
236}
237
238bool wxCondition::Wait(
239 unsigned long lSec
240, unsigned long lNsec)
241{
242 return m_internal->Wait(lSec*1000 + lNsec/1000000);
243}
244
245void wxCondition::Signal()
246{
247 ::DosPostEventSem(m_internal->m_vEvent);
248}
249
250void 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
267wxCriticalSection::wxCriticalSection()
268{
269}
270
271wxCriticalSection::~wxCriticalSection()
272{
273}
274
275void wxCriticalSection::Enter()
276{
277 ::DosEnterCritSec();
278}
279
280void wxCriticalSection::Leave()
281{
282 ::DosExitCritSec();
283}
284
285// ----------------------------------------------------------------------------
286// wxThread implementation
287// ----------------------------------------------------------------------------
288
289// wxThreadInternal class
290// ----------------------
291
292class wxThreadInternal
293{
294public:
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
339private:
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
348ULONG 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
377void 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
413bool 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
438bool 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
451bool 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
467wxThread *wxThread::This()
468{
469 wxThread* pThread = m_pThread;
470 return pThread;
471}
472
473bool 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
489void wxThread::Yield()
490{
491 ::DosSleep(0);
492}
493
494void wxThread::Sleep(
495 unsigned long ulMilliseconds
496)
497{
498 ::DosSleep(ulMilliseconds);
499}
500
501// ctor and dtor
502// -------------
503
504wxThread::wxThread(wxThreadKind kind)
505{
506 m_internal = new wxThreadInternal();
507
508 m_isDetached = kind == wxTHREAD_DETACHED;
509}
510
511wxThread::~wxThread()
512{
513 delete m_internal;
514}
515
516// create/start thread
517// -------------------
518
519wxThreadError wxThread::Create()
520{
521 if ( !m_internal->Create(this) )
522 return wxTHREAD_NO_RESOURCE;
523
524 return wxTHREAD_NO_ERROR;
525}
526
527wxThreadError 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
542wxThreadError wxThread::Pause()
543{
544 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
545
546 return m_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
547}
548
549wxThreadError 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
559wxThread::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
571wxThreadError 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
636wxThreadError 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
650void 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
660void wxThread::SetPriority(
661 unsigned int nPrio
662)
663{
664 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
665
666 m_internal->SetPriority(nPrio);
667}
668
669unsigned int wxThread::GetPriority() const
670{
671 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
672
673 return m_internal->GetPriority();
674}
675
676unsigned long wxThread::GetId() const
677{
678 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
679
680 return (unsigned long)m_internal->GetId();
681}
682
683bool wxThread::IsRunning() const
684{
685 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
686
687 return(m_internal->GetState() == STATE_RUNNING);
688}
689
690bool 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
698bool wxThread::IsPaused() const
699{
700 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
701
702 return (m_internal->GetState() == STATE_PAUSED);
703}
704
705bool 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
716class wxThreadModule : public wxModule
717{
718public:
719 virtual bool OnInit();
720 virtual void OnExit();
721
722private:
723 DECLARE_DYNAMIC_CLASS(wxThreadModule)
724};
725
726IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
727
728bool 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
744void wxThreadModule::OnExit()
745{
746 if (gs_pCritsectGui)
747 {
748 gs_pCritsectGui->Leave();
749#if (!(defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 )))
750 delete gs_pCritsectGui;
751#endif
752 gs_pCritsectGui = NULL;
753 }
754
755#if (!(defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 )))
756 wxDELETE(gs_pCritsectWaitingForGui);
757#endif
758}
759
760// ----------------------------------------------------------------------------
761// Helper functions
762// ----------------------------------------------------------------------------
763
764// Does nothing under OS/2 [for now]
765void WXDLLEXPORT wxWakeUpMainThread()
766{
767}
768
769void WXDLLEXPORT wxMutexGuiLeave()
770{
771 wxCriticalSectionLocker enter(*gs_pCritsectWaitingForGui);
772
773 if ( wxThread::IsMain() )
774 {
775 gs_bGuiOwnedByMainThread = FALSE;
776 }
777 else
778 {
779 // decrement the number of waiters now
780 wxASSERT_MSG(gs_nWaitingForGui > 0,
781 wxT("calling wxMutexGuiLeave() without entering it first?") );
782
783 gs_nWaitingForGui--;
784
785 wxWakeUpMainThread();
786 }
787
788 gs_pCritsectGui->Leave();
789}
790
791void WXDLLEXPORT wxMutexGuiLeaveOrEnter()
792{
793 wxASSERT_MSG( wxThread::IsMain(),
794 wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") );
795
796 wxCriticalSectionLocker enter(*gs_pCritsectWaitingForGui);
797
798 if (gs_nWaitingForGui == 0)
799 {
800 // no threads are waiting for GUI - so we may acquire the lock without
801 // any danger (but only if we don't already have it)
802 if (!wxGuiOwnedByMainThread())
803 {
804 gs_pCritsectGui->Enter();
805
806 gs_bGuiOwnedByMainThread = TRUE;
807 }
808 //else: already have it, nothing to do
809 }
810 else
811 {
812 // some threads are waiting, release the GUI lock if we have it
813 if (wxGuiOwnedByMainThread())
814 {
815 wxMutexGuiLeave();
816 }
817 //else: some other worker thread is doing GUI
818 }
819}
820
821bool WXDLLEXPORT wxGuiOwnedByMainThread()
822{
823 return gs_bGuiOwnedByMainThread;
824}
825
826bool WXDLLEXPORT wxIsWaitingForThread()
827{
828 return gs_bWaitingForThread;
829}
830
831#endif
832 // wxUSE_THREADS