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