]> git.saurik.com Git - wxWidgets.git/blob - src/os2/thread.cpp
[gtk] fixed bug that caused segfaults in wxYield when wxToolBar has non-button contr...
[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 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_INFINITE_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 = thread->m_internal->GetState() == STATE_CANCELED;
360
361 pThread->m_internal->SetState(STATE_EXITED);
362 thread->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 thread;
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
384 m_nPriority = nPriority;
385
386 if (m_nPriority <= 20)
387 ulOS2_Priority = PRTYC_NOCHANGE;
388 else if (m_nPriority <= 40)
389 ulOS2_Priority = PRTYC_IDLETIME;
390 else if (m_nPriority <= 60)
391 ulOS2_Priority = PRTYC_REGULAR;
392 else if (m_nPriority <= 80)
393 ulOS2_Priority = PRTYC_TIMECRITICAL;
394 else if (m_nPriority <= 100)
395 ulOS2_Priority = PRTYC_FOREGROUNDSERVER;
396 else
397 {
398 wxFAIL_MSG(wxT("invalid value of thread priority parameter"));
399 ulOS2_Priority = PRTYC_REGULAR;
400 }
401 ulrc = ::DosSetPriority( PRTYS_THREAD
402 ,ulOS2_Priority
403 ,0
404 ,(ULONG)m_hThread
405 );
406 if (ulrc != 0)
407 {
408 wxLogSysError(_("Can't set thread priority"));
409 }
410 }
411
412 bool wxThreadInternal::Create(
413 wxThread* pThread
414 )
415 {
416 APIRET ulrc;
417
418 ulrc = ::DosCreateThread( &m_hThread
419 ,(PFNTHREAD)wxThreadInternal::OS2ThreadStart
420 ,(ULONG)pThread
421 ,CREATE_SUSPENDED | STACK_SPARSE
422 ,8192L
423 );
424 if(ulrc != 0)
425 {
426 wxLogSysError(_("Can't create thread"));
427
428 return FALSE;
429 }
430 if (m_nPriority != WXTHREAD_DEFAULT_PRIORITY)
431 {
432 SetPriority(m_nPriority);
433 }
434 return(TRUE);
435 }
436
437 bool wxThreadInternal::Suspend()
438 {
439 ULONG ulrc = ::DosSuspendThread(m_hThread);
440
441 if (ulrc != 0)
442 {
443 wxLogSysError(_("Can not suspend thread %lu"), m_hThread);
444 return FALSE;
445 }
446 m_eState = STATE_PAUSED;
447 return TRUE;
448 }
449
450 bool wxThreadInternal::Resume()
451 {
452 ULONG ulrc = ::DosResumeThread(m_hThread);
453
454 if (ulrc != 0)
455 {
456 wxLogSysError(_("Can not suspend thread %lu"), m_hThread);
457 return FALSE;
458 }
459 m_eState = STATE_PAUSED;
460 return TRUE;
461 }
462
463 // static functions
464 // ----------------
465
466 wxThread *wxThread::This()
467 {
468 wxThread* pThread = m_pThread;
469 return pThread;
470 }
471
472 bool wxThread::IsMain()
473 {
474 PTIB ptib;
475 PPIB ppib;
476
477 ::DosGetInfoBlocks(&ptib, &ppib);
478
479 if (ptib->tib_ptib2->tib2_ultid == s_ulIdMainThread)
480 return TRUE;
481 return FALSE;
482 }
483
484 #ifdef Yield
485 #undef Yield
486 #endif
487
488 void wxThread::Yield()
489 {
490 ::DosSleep(0);
491 }
492
493 void wxThread::Sleep(
494 unsigned long ulMilliseconds
495 )
496 {
497 ::DosSleep(ulMilliseconds);
498 }
499
500 // ctor and dtor
501 // -------------
502
503 wxThread::wxThread(wxThreadKind kind)
504 {
505 m_internal = new wxThreadInternal();
506
507 m_isDetached = kind == wxTHREAD_DETACHED;
508 }
509
510 wxThread::~wxThread()
511 {
512 delete m_internal;
513 }
514
515 // create/start thread
516 // -------------------
517
518 wxThreadError wxThread::Create()
519 {
520 if ( !m_internal->Create(this) )
521 return wxTHREAD_NO_RESOURCE;
522
523 return wxTHREAD_NO_ERROR;
524 }
525
526 wxThreadError wxThread::Run()
527 {
528 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
529
530 if ( m_internal->GetState() != STATE_NEW )
531 {
532 // actually, it may be almost any state at all, not only STATE_RUNNING
533 return wxTHREAD_RUNNING;
534 }
535 return Resume();
536 }
537
538 // suspend/resume thread
539 // ---------------------
540
541 wxThreadError wxThread::Pause()
542 {
543 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
544
545 return m_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
546 }
547
548 wxThreadError wxThread::Resume()
549 {
550 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
551
552 return m_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
553 }
554
555 // stopping thread
556 // ---------------
557
558 wxThread::ExitCode wxThread::Wait()
559 {
560 // although under Windows we can wait for any thread, it's an error to
561 // wait for a detached one in wxWin API
562 wxCHECK_MSG( !IsDetached(), (ExitCode)-1,
563 _T("can't wait for detached thread") );
564 ExitCode rc = (ExitCode)-1;
565 (void)Delete(&rc);
566 m_internal->Free();
567 return(rc);
568 }
569
570 wxThreadError wxThread::Delete(ExitCode *pRc)
571 {
572 ExitCode rc = 0;
573
574 // Delete() is always safe to call, so consider all possible states
575 if (IsPaused())
576 Resume();
577
578 TID hThread = m_internal->GetHandle();
579
580 if (IsRunning())
581 {
582 if (IsMain())
583 {
584 // set flag for wxIsWaitingForThread()
585 gs_waitingForThread = TRUE;
586
587 #if wxUSE_GUI
588 wxBeginBusyCursor();
589 #endif // wxUSE_GUI
590 }
591
592 // ask the thread to terminate
593 {
594 wxCriticalSectionLocker lock(m_critsect);
595 m_internal->Cancel();
596 }
597
598 #if wxUSE_GUI
599 // need a way to finish GUI processing before killing the thread
600 // until then we just exit
601
602 if ((gs_nWaitingForGui > 0) && wxGuiOwnedByMainThread())
603 {
604 wxMutexGuiLeave();
605 }
606 #else // !wxUSE_GUI
607
608 // can't wait for yourself to end under OS/2 so just quit
609
610 #endif // wxUSE_GUI/!wxUSE_GUI
611
612 if ( IsMain() )
613 {
614 gs_waitingForThread = FALSE;
615
616 #if wxUSE_GUI
617 wxEndBusyCursor();
618 #endif // wxUSE_GUI
619 }
620 }
621
622 ::DosExit(0, 0);
623 // probably won't get this far, but
624 if (IsDetached())
625 {
626 delete this;
627 }
628
629 wxASSERT_MSG( (DWORD)rc != STILL_ACTIVE,
630 wxT("thread must be already terminated.") );
631
632 if ( pRc )
633 *pRc = rc;
634
635 return rc == (ExitCode)-1 ? wxTHREAD_MISC_ERROR : wxTHREAD_NO_ERROR;
636 }
637
638 wxThreadError wxThread::Kill()
639 {
640 if (!IsRunning())
641 return wxTHREAD_NOT_RUNNING;
642
643 ::DosKillThread(m_internal->GetHandle());
644 m_internal->Free();
645 if (IsDetached())
646 {
647 delete this;
648 }
649 return wxTHREAD_NO_ERROR;
650 }
651
652 void wxThread::Exit(
653 ExitCode pStatus
654 )
655 {
656 m_internal->Free();
657 delete this;
658 ::DosExit(EXIT_THREAD, ULONG(pStatus));
659 wxFAIL_MSG(wxT("Couldn't return from DosExit()!"));
660 }
661
662 void wxThread::SetPriority(
663 unsigned int nPrio
664 )
665 {
666 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
667
668 m_internal->SetPriority(nPrio);
669 }
670
671 unsigned int wxThread::GetPriority() const
672 {
673 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
674
675 return m_internal->GetPriority();
676 }
677
678 unsigned long wxThread::GetId() const
679 {
680 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
681
682 return (unsigned long)m_internal->GetId();
683 }
684
685 bool wxThread::IsRunning() const
686 {
687 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
688
689 return(m_internal->GetState() == STATE_RUNNING);
690 }
691
692 bool wxThread::IsAlive() const
693 {
694 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
695
696 return (m_internal->GetState() == STATE_RUNNING) ||
697 (m_internal->GetState() == STATE_PAUSED);
698 }
699
700 bool wxThread::IsPaused() const
701 {
702 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
703
704 return (m_internal->GetState() == STATE_PAUSED);
705 }
706
707 bool wxThread::TestDestroy()
708 {
709 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
710
711 return m_internal->GetState() == STATE_CANCELED;
712 }
713
714 // ----------------------------------------------------------------------------
715 // Automatic initialization for thread module
716 // ----------------------------------------------------------------------------
717
718 class wxThreadModule : public wxModule
719 {
720 public:
721 virtual bool OnInit();
722 virtual void OnExit();
723
724 private:
725 DECLARE_DYNAMIC_CLASS(wxThreadModule)
726 };
727
728 IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
729
730 bool wxThreadModule::OnInit()
731 {
732 s_pCritsectWaitingForGui = new wxCriticalSection();
733
734 s_pCritsectGui = new wxCriticalSection();
735 s_pCritsectGui->Enter();
736
737 PTIB ptib;
738 PPIB ppib;
739
740 ::DosGetInfoBlocks(&ptib, &ppib);
741
742 s_ulIdMainThread = ptib->tib_ptib2->tib2_ultid;
743 return TRUE;
744 }
745
746 void wxThreadModule::OnExit()
747 {
748 if (s_pCritsectGui)
749 {
750 s_pCritsectGui->Leave();
751 delete s_pCritsectGui;
752 s_pCritsectGui = NULL;
753 }
754
755 wxDELETE(s_pCritsectWaitingForGui);
756 }
757
758 // ----------------------------------------------------------------------------
759 // Helper functions
760 // ----------------------------------------------------------------------------
761
762 // Does nothing under OS/2 [for now]
763 void WXDLLEXPORT wxWakeUpMainThread()
764 {
765 }
766
767 void WXDLLEXPORT wxMutexGuiLeave()
768 {
769 wxCriticalSectionLocker enter(*s_pCritsectWaitingForGui);
770
771 if ( wxThread::IsMain() )
772 {
773 s_bGuiOwnedByMainThread = FALSE;
774 }
775 else
776 {
777 // decrement the number of waiters now
778 wxASSERT_MSG( s_nWaitingForGui > 0,
779 wxT("calling wxMutexGuiLeave() without entering it first?") );
780
781 s_nWaitingForGui--;
782
783 wxWakeUpMainThread();
784 }
785
786 s_pCritsectGui->Leave();
787 }
788
789 void WXDLLEXPORT wxMutexGuiLeaveOrEnter()
790 {
791 wxASSERT_MSG( wxThread::IsMain(),
792 wxT("only main thread may call wxMutexGuiLeaveOrEnter()!") );
793
794 wxCriticalSectionLocker enter(*s_pCritsectWaitingForGui);
795
796 if ( s_nWaitingForGui == 0 )
797 {
798 // no threads are waiting for GUI - so we may acquire the lock without
799 // any danger (but only if we don't already have it)
800 if (!wxGuiOwnedByMainThread())
801 {
802 s_pCritsectGui->Enter();
803
804 s_bGuiOwnedByMainThread = TRUE;
805 }
806 //else: already have it, nothing to do
807 }
808 else
809 {
810 // some threads are waiting, release the GUI lock if we have it
811 if (wxGuiOwnedByMainThread())
812 {
813 wxMutexGuiLeave();
814 }
815 //else: some other worker thread is doing GUI
816 }
817 }
818
819 bool WXDLLEXPORT wxGuiOwnedByMainThread()
820 {
821 return s_bGuiOwnedByMainThread;
822 }
823
824 bool WXDLLEXPORT wxIsWaitingForThread()
825 {
826 return s_bWaitingForThread;
827 }
828
829 #endif
830 // wxUSE_THREADS