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