4 * Thread library implementation for BeOS
6 * Portable Windows Library
8 * The contents of this file are subject to the Mozilla Public License
9 * Version 1.0 (the "License"); you may not use this file except in
10 * compliance with the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
13 * Software distributed under the License is distributed on an "AS IS"
14 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
15 * the License for the specific language governing rights and limitations
18 * The Original Code is Portable Windows Library.
20 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
22 * Portions are Copyright (c) 1993-1998 Equivalence Pty. Ltd.
24 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
25 * All Rights Reserved.
27 * Contributor(s): Yuri Kiryanov, ykiryanov at users.sourceforge.net
30 * Revision 1.21 2004/04/02 03:17:19 ykiryanov
31 * New version, improved
33 * Revision 1.20 2004/02/23 23:40:42 ykiryanov
34 * Added missing constructor for PMutex
36 * Revision 1.19 2004/02/23 21:23:09 ykiryanov
37 * Removed assert line to enable semaphore constructor
39 * Revision 1.18 2004/02/23 20:37:17 ykiryanov
40 * Changed function definition PXBlockIO to prototype one
42 * Revision 1.17 2004/02/23 18:10:39 ykiryanov
43 * Added a parameter to semaphore constructor to avoid ambiguity
45 * Revision 1.16 2004/02/23 00:02:20 ykiryanov
46 * Changed my e-mail to ykiryanov at users.sourceforge.net. Just in case someone wants to collaborate
48 * Revision 1.15 2004/02/22 23:59:28 ykiryanov
49 * Added missing functions: PProcess::SetMaxHandles(), PThread::GetCurrentThreadId(),
50 * PThread::PXAbortBlock(), PSyncPoint::Signal(), ::Wait(), ::Wait(timeout), ::WillBlock()
52 * Revision 1.14 2004/02/22 04:35:04 ykiryanov
53 * Removed PMutex desctructor
55 * Revision 1.13 2003/02/26 01:13:18 robertj
56 * Fixed race condition where thread can terminatebefore an IsSuspeded() call
57 * occurs and cause an assert, thanks Sebastian Meyer
59 * Revision 1.12 2001/06/30 06:59:07 yurik
60 * Jac Goudsmit from Be submit these changes 6/28. Implemented by Yuri Kiryanov
62 * Revision 1.11 2001/03/07 06:57:32 yurik
63 * Changed email to current one
65 * Revision 1.10 2001/01/16 12:32:06 rogerh
66 * Remove duplicate SetAutoDelete() function. Submitted by
67 * Jac Goudsmit <jac_goudsmit@yahoo.com>
77 //#define DEBUG_SEMAPHORES
78 //#define DEBUG_THREADS
80 ///////////////////////////////////////////////////////////////////////////////
83 static int const priorities
[] = {
84 1, // Lowest priority is 1. 0 is not
88 B_URGENT_DISPLAY_PRIORITY
,
91 int32
PThread::ThreadFunction(void * threadPtr
)
93 PThread
* thread
= (PThread
*)PAssertNULL(threadPtr
);
95 PProcess
& process
= PProcess::Current();
97 process
.activeThreadMutex
.Wait();
98 process
.activeThreads
.SetAt(thread
->threadId
, thread
);
99 process
.activeThreadMutex
.Signal();
101 process
.SignalTimerChange();
109 : threadId(B_BAD_THREAD_ID
),
110 priority(B_NORMAL_PRIORITY
),
115 PThread::PThread(PINDEX stackSize
,
116 AutoDeleteFlag deletion
,
117 Priority priorityLevel
,
118 const PString
& name
)
121 PAssert(stackSize
> 0, PInvalidParameter
);
122 autoDelete
= deletion
== AutoDeleteThread
;
123 originalStackSize
= stackSize
;
125 priority
= priorities
[priorityLevel
];
130 PError
<< "::spawn_thread(" << str
<< "), priority:" << priority
<< endl
;
133 threadId
= ::spawn_thread(ThreadFunction
, // Function
134 (const char*) str
, // Name
135 priority
, // Priority
136 (void *) this); // Pass this as cookie
138 PAssertOS(threadId
>= B_NO_ERROR
);
141 PError
<< ", id: " << threadId
<< endl
;
148 PProcess
& process
= PProcess::Current();
149 process
.deleteThreadMutex
.Wait();
150 process
.autoDeleteThreads
.Append(this);
151 process
.deleteThreadMutex
.Signal();
158 if (originalStackSize
<= 0)
161 PProcess
& process
= PProcess::Current();
162 process
.activeThreadMutex
.Wait();
163 process
.activeThreads
.SetAt(threadId
, NULL
);
164 process
.activeThreadMutex
.Signal();
169 ::close(unblockPipe
[0]);
170 ::close(unblockPipe
[1]);
174 void PThread::Restart()
176 PAssert(IsTerminated(), "Cannot restart running thread");
181 PError
<< "::spawn_thread(" << str
<< "), priority:" << priority
<< endl
;
184 threadId
= ::spawn_thread(ThreadFunction
, // Function
185 (const char*) str
, // Name
187 (void *) this); // Pass this as cookie
190 PError
<< ", id: " << threadId
<< endl
;
193 PAssertOS(threadId
>= B_NO_ERROR
);
197 void PThread::Terminate()
199 PAssert(!IsTerminated(), "Operation on terminated thread");
200 PAssert(originalStackSize
> 0, PLogicError
);
202 if (Current() == this)
204 sem_id semId
= ::create_sem( 1, "PWST" );
205 if ( ::acquire_sem(semId
) == B_NO_ERROR
)
207 // Invalidate the thread
208 threadId
= B_BAD_THREAD_ID
;
209 ::release_sem(semId
);
213 PError
<< "::exit_thread(0), id:" << threadId
<< endl
;
220 sem_id semId
= ::create_sem( 1, "PWTS" );
221 if ( ::acquire_sem(semId
) == B_NO_ERROR
)
226 // Invalidate the thread
227 threadId
= B_BAD_THREAD_ID
;
230 if (idToKill
!= B_BAD_THREAD_ID
)
232 ::release_sem(semId
);
236 PError
<< "::kill_thread(" << idToKill
<< ")" << endl
;
238 ::kill_thread(idToKill
);
242 PAssert(threadId
== B_BAD_THREAD_ID
, "Can't acquire semaphore to terminate thread");
246 BOOL
PThread::IsTerminated() const
248 return threadId
== B_BAD_THREAD_ID
;
252 void PThread::WaitForTermination() const
254 WaitForTermination(PMaxTimeInterval
);
258 BOOL
PThread::WaitForTermination(const PTimeInterval
& /*maxWait*/) const // Fix timeout
260 status_t result
= B_NO_ERROR
;
261 status_t exit_value
= B_NO_ERROR
;
264 PError
<< "::wait_for_thread(" << threadId
<< "), result:";
267 result
= ::wait_for_thread(threadId
, &exit_value
);
268 if ( result
== B_INTERRUPTED
) { // thread was killed.
270 PError
<< "B_INTERRUPTED" << endl
;
275 if ( result
== B_OK
) { // thread is dead
277 PError
<< "B_OK" << endl
;
282 if ( result
== B_BAD_THREAD_ID
) { // thread has invalid id
284 PError
<< "B_BAD_THREAD_ID" << endl
;
293 void PThread::Suspend(BOOL susp
)
295 PAssert(!IsTerminated(), "Operation on terminated thread");
298 status_t result
= ::suspend_thread(threadId
);
300 PAssert(result
== B_OK
, "Thread don't want to be suspended");
307 void PThread::Resume()
309 PAssert(!IsTerminated(), "Operation on terminated thread");
310 status_t result
= ::resume_thread(threadId
);
312 PAssert(result
== B_NO_ERROR
, "Thread doesn't want to resume");
316 BOOL
PThread::IsSuspended() const
319 status_t result
= ::get_thread_info(threadId
, &info
);
321 PAssert(result
== B_OK
&& threadId
== info
.thread
, "Thread info inaccessible");
322 return info
.state
== B_THREAD_SUSPENDED
;
325 void PThread::SetAutoDelete(AutoDeleteFlag deletion
)
327 PAssert(deletion
!= AutoDeleteThread
|| this != &PProcess::Current(), PLogicError
);
328 autoDelete
= deletion
== AutoDeleteThread
;
331 void PThread::SetPriority(Priority priorityLevel
)
333 PAssert(!IsTerminated(), "Operation on terminated thread");
335 priority
= priorities
[priorityLevel
];
336 status_t result
= ::set_thread_priority(threadId
, priority
);
338 PAssert(result
== B_OK
, "Thread priority change error");
342 PThread::Priority
PThread::GetPriority() const
344 PAssert(!IsTerminated(), "Operation on terminated thread");
348 return LowestPriority
;
349 case B_LOW_PRIORITY
:
351 case B_NORMAL_PRIORITY
:
352 return NormalPriority
;
353 case B_DISPLAY_PRIORITY
:
355 case B_URGENT_DISPLAY_PRIORITY
:
356 return HighestPriority
;
358 PAssertAlways(POperatingSystemError
);
359 return LowestPriority
;
362 void PThread::Yield()
364 // we just sleep for long enough to cause a reschedule (100 microsec)
368 void PThread::Sleep( const PTimeInterval
& delay
) // Time interval to sleep for.
370 bigtime_t microseconds
=
371 delay
== PMaxTimeInterval
? B_INFINITE_TIMEOUT
: (delay
.GetMilliSeconds() * 1000 );
373 status_t result
= ::snooze( microseconds
) ; // delay in ms, snooze in microsec
374 PAssert(result
== B_OK
, "Thread has insomnia");
377 void PThread::InitialiseProcessThread()
379 originalStackSize
= 0;
384 threadId
= ::find_thread(NULL
);
385 PAssertOS(threadId
>= B_NO_ERROR
);
387 ((PProcess
*)this)->activeThreads
.DisallowDeleteObjects();
388 ((PProcess
*)this)->activeThreads
.SetAt(threadId
, this);
392 PThread
* PThread::Current()
394 PProcess
& process
= PProcess::Current();
395 process
.activeThreadMutex
.Wait();
397 thread_id tId
= ::find_thread(NULL
);
398 PAssertOS(tId
>= B_NO_ERROR
);
400 PThread
* thread
= process
.activeThreads
.GetAt( tId
);
402 process
.activeThreadMutex
.Signal();
406 int PThread::PXBlockOnChildTerminate(int pid
, const PTimeInterval
& /*timeout*/) // Fix timeout
408 status_t result
= B_NO_ERROR
;
409 status_t exit_value
= B_NO_ERROR
;
412 PError
<< "::wait_for_thread(" << pid
<< "), result:";
415 result
= ::wait_for_thread(pid
, &exit_value
);
416 if ( result
== B_INTERRUPTED
)
418 // thread was killed.
420 PError
<< "B_INTERRUPTED" << endl
;
425 if ( result
== B_OK
)
429 PError
<< "B_OK" << endl
;
434 if ( result
== B_BAD_THREAD_ID
)
436 // thread has invalid id
438 PError
<< "B_BAD_THREAD_ID" << endl
;
446 PThreadIdentifier
PThread::GetCurrentThreadId(void)
448 return ::find_thread(NULL
);
451 int PThread::PXBlockOnIO(int handle
, int type
, const PTimeInterval
& timeout
)
453 // make sure we flush the buffer before doing a write
454 fd_set tmp_rfd
, tmp_wfd
, tmp_efd
;
455 fd_set
* read_fds
= &tmp_rfd
;
456 fd_set
* write_fds
= &tmp_wfd
;
457 fd_set
* exception_fds
= &tmp_efd
;
459 struct timeval
* tptr
= NULL
;
460 struct timeval timeout_val
;
461 if (timeout
!= PMaxTimeInterval
) { // Clean up for infinite timeout
462 static const PTimeInterval
oneDay(0, 0, 0, 0, 1);
463 if (timeout
< oneDay
) {
465 timeout_val
.tv_usec
= (timeout
.GetMilliSeconds() % 1000) * 1000;
466 timeout_val
.tv_sec
= timeout
.GetSeconds();
477 FD_ZERO (exception_fds
);
480 case PChannel::PXReadBlock
:
481 case PChannel::PXAcceptBlock
:
482 FD_SET (handle
, read_fds
);
484 case PChannel::PXWriteBlock
:
485 FD_SET (handle
, write_fds
);
487 case PChannel::PXConnectBlock
:
488 FD_SET (handle
, write_fds
);
489 FD_SET (handle
, exception_fds
);
492 PAssertAlways(PLogicError
);
497 // include the termination pipe into all blocking I/O functions
498 int width
= handle
+1;
499 FD_SET(unblockPipe
[0], read_fds
);
500 width
= PMAX(width
, unblockPipe
[0]+1);
502 retval
= ::select(width
, read_fds
, write_fds
, exception_fds
, tptr
);
504 if ((retval
>= 0) || (errno
!= EINTR
))
508 if ((retval
== 1) && FD_ISSET(unblockPipe
[0], read_fds
))
511 ::read(unblockPipe
[0], &ch
, 1);
514 //PTRACE(1,"Unblocked I/O");
520 int PThread::PXBlockOnIO(int maxHandles
,
523 fd_set
* exceptionBits
,
524 const PTimeInterval
& timeout
,
525 const PIntArray
& /*osHandles*/)
527 struct timeval
* tptr
= NULL
;
528 struct timeval timeout_val
;
529 if (timeout
!= PMaxTimeInterval
)
531 // Clean up for infinite timeout
532 static const PTimeInterval
oneDay(0, 0, 0, 0, 1);
533 if (timeout
< oneDay
) {
534 timeout_val
.tv_usec
= (timeout
.GetMilliSeconds() % 1000) * 1000;
535 timeout_val
.tv_sec
= timeout
.GetSeconds();
540 int retval
= ::select(maxHandles
, readBits
, writeBits
, exceptionBits
, tptr
);
541 PProcess::Current().PXCheckSignals();
545 void PThread::PXAbortBlock(void) const
548 ::write(unblockPipe
[1], &ch
, 1);
551 ///////////////////////////////////////////////////////////////////////////////
553 PDECLARE_CLASS(PHouseKeepingThread
, PThread
)
555 PHouseKeepingThread()
556 : PThread(1000, NoAutoDeleteThread
, NormalPriority
, "Housekeeper")
557 { closing
= FALSE
; Resume(); }
560 void SetClosing() { closing
= TRUE
; }
566 void PProcess::Construct()
568 ::pipe(timerChangePipe
);
570 // initialise the housekeeping thread
571 housekeepingThread
= NULL
;
576 void PHouseKeepingThread::Main()
578 debugger("Housekeeper");
580 PProcess
& process
= PProcess::Current();
583 PTimeInterval delay
= process
.timers
.Process();
585 int fd
= process
.timerChangePipe
[0];
588 fd_set
* read_fds
= &tmp_rfd
;
591 FD_SET(fd
, read_fds
);
593 static const PTimeInterval
oneDay(0, 0, 0, 0, 1);
594 struct timeval
* tptr
= NULL
;
596 if (delay
< oneDay
) {
597 tval
.tv_usec
= (delay
.GetMilliSeconds() % 1000) * 1000;
598 tval
.tv_sec
= delay
.GetSeconds();
601 if (::select(fd
+1, read_fds
, NULL
, NULL
, tptr
) == 1) {
606 process
.PXCheckSignals();
610 void PProcess::SignalTimerChange()
612 if (housekeepingThread
== NULL
) {
613 housekeepingThread
= new PHouseKeepingThread
;
617 write(timerChangePipe
[1], &ch
, 1);
620 BOOL
PProcess::SetMaxHandles(int)
625 PProcess::~PProcess()
627 // Don't wait for housekeeper to stop if Terminate() is called from it.
628 if (housekeepingThread
!= NULL
&& PThread::Current() != housekeepingThread
) {
629 housekeepingThread
->SetClosing();
631 housekeepingThread
->WaitForTermination();
632 delete housekeepingThread
;
637 ///////////////////////////////////////////////////////////////////////////////
640 PSemaphore::PSemaphore(unsigned initial
, unsigned maxCount
)
641 : mOwner(::find_thread(NULL
)), semId(::create_sem(initial
, "PWLS")), mCount(initial
)
643 PAssertOS(semId
>= B_NO_ERROR
);
644 PAssertOS(mOwner
!= B_BAD_THREAD_ID
);
646 #ifdef DEBUG_SEMAPHORES
648 get_sem_info(semId
, &info
);
649 PError
<< "::create_sem (PSemaphore(i,m)), id: " << semId
<< ", this: " << this << ", name: " << info
.name
<< ", count:" << info
.count
<< endl
;
654 PSemaphore::~PSemaphore()
656 status_t result
= B_NO_ERROR
;
657 PAssertOS(semId
>= B_NO_ERROR
);
659 // Transmit ownership of the semaphore to our thread
660 thread_id curThread
= ::find_thread(NULL
);
661 if(mOwner
!= curThread
)
664 ::get_thread_info(curThread
, &tinfo
);
665 ::set_sem_owner(semId
, tinfo
.team
);
669 #ifdef DEBUG_SEMAPHORES
671 get_sem_info(semId
, &info
);
672 PError
<< "::delete_sem, id: " << semId
<< ", this: " << this << ", name: " << info
.name
<< ", count:" << info
.count
;
675 // Deleting the semaphore id
676 result
= ::delete_sem(semId
);
678 #ifdef DEBUG_SEMAPHORES
679 if( result
!= B_NO_ERROR
)
680 PError
<< "...delete_sem failed, error: " << strerror(result
) << endl
;
684 void PSemaphore::Wait()
686 PAssertOS(semId
>= B_NO_ERROR
);
688 status_t result
= B_NO_ERROR
;
690 #ifdef DEBUG_SEMAPHORES
692 get_sem_info(semId
, &info
);
693 PError
<< "::acquire_sem_etc, id: " << semId
<< ", this: " << this << ", name: " << info
.name
<< ", count:" << info
.count
;
696 while ((B_BAD_THREAD_ID
!= mOwner
)
697 && ((result
= ::acquire_sem(semId
)) == B_INTERRUPTED
))
701 #ifdef DEBUG_SEMAPHORES
702 if( result
!= B_NO_ERROR
)
703 PError
<< "... failed, error: " << strerror(result
);
709 BOOL
PSemaphore::Wait(const PTimeInterval
& timeout
)
711 PAssertOS(semId
>= B_NO_ERROR
);
713 status_t result
= B_NO_ERROR
;
715 PInt64 ms
= timeout
.GetMilliSeconds();
716 bigtime_t microseconds
=
717 ms
? timeout
== PMaxTimeInterval
? B_INFINITE_TIMEOUT
: ( ms
* 1000 ) : 0;
719 #ifdef DEBUG_SEMAPHORES
721 get_sem_info(semId
, &info
);
722 PError
<< "::acquire_sem_etc " << semId
<< ",this: " << this << ", name: " << info
.name
<< ", count:" << info
.count
<< ", timeout:";
724 if( microseconds
== B_INFINITE_TIMEOUT
)
725 PError
<< "infinite";
727 PError
<< microseconds
<< " ms";
730 while((B_BAD_THREAD_ID
!= mOwner
)
731 && ((result
= ::acquire_sem_etc(semId
, 1,
732 B_RELATIVE_TIMEOUT
, microseconds
)) == B_INTERRUPTED
))
736 #ifdef DEBUG_SEMAPHORES
737 if( result
!= B_NO_ERROR
)
738 PError
<< " ... failed! error: " << strerror(result
);
740 PError
<< " " << endl
;
743 return result
== B_TIMED_OUT
;
746 void PSemaphore::Signal()
748 PAssertOS(semId
>= B_NO_ERROR
);
750 #ifdef DEBUG_SEMAPHORES
752 get_sem_info(semId
, &info
);
753 PError
<< "::release_sem " << semId
<< ", this: " << this << ", name: " << info
.name
<< ", count:" << info
.count
;
757 ::release_sem(semId
);
759 #ifdef DEBUG_SEMAPHORES
760 if( result
!= B_NO_ERROR
)
761 PError
<< "... failed, error: " << strerror(result
);
767 BOOL
PSemaphore::WillBlock() const
769 PAssertOS(semId
>= B_NO_ERROR
);
771 status_t result
= B_NO_ERROR
;
773 #ifdef DEBUG_SEMAPHORES
775 get_sem_info(semId
, &info
);
776 PError
<< "::acquire_sem_etc (WillBlock) " << semId
<< ", this: " << this << ", name: " << info
.name
<< ", count:" << info
.count
;
779 result
= ::acquire_sem_etc(semId
, 0, B_RELATIVE_TIMEOUT
, 0);
781 #ifdef DEBUG_SEMAPHORES
782 if( result
!= B_NO_ERROR
)
783 PError
<< "... failed, error: " << strerror(result
);
788 return result
== B_WOULD_BLOCK
;
791 ///////////////////////////////////////////////////////////////////////////////
797 PAssertOS(semId
>= B_NO_ERROR
);
798 #ifdef DEBUG_SEMAPHORES
800 get_sem_info(semId
, &info
);
801 PError
<< "::create_sem (PMutex()), id: " << semId
<< " " << ", this: " << this << ", " << info
.name
<< ", count:" << info
.count
<< endl
;
805 PMutex::PMutex(const PMutex
& m
)
808 PAssertOS(semId
>= B_NO_ERROR
);
809 #ifdef DEBUG_SEMAPHORES
811 get_sem_info(semId
, &info
);
812 PError
<< "::create_sem (PMutex(PMutex)), id: " << semId
<< " " << ", this: " << this << ", " << info
.name
<< ", count:" << info
.count
<< endl
;
821 BOOL
PMutex::Wait(const PTimeInterval
& timeout
)
823 return PSemaphore::Wait(timeout
);
826 void PMutex::Signal()
828 PSemaphore::Signal();
831 BOOL
PMutex::WillBlock() const
833 return PSemaphore::WillBlock();
836 ///////////////////////////////////////////////////////////////////////////////
839 PSyncPoint::PSyncPoint()
842 PAssertOS(semId
>= B_NO_ERROR
);
843 #ifdef DEBUG_SEMAPHORES
845 get_sem_info(semId
, &info
);
846 PError
<< "::create_sem (PSyncPoint()), id: " << semId
<< " " << ", this: " << this << info
.name
<< ", count:" << info
.count
<< endl
;
850 void PSyncPoint::Signal()
852 PSemaphore::Signal();
855 void PSyncPoint::Wait()
860 BOOL
PSyncPoint::Wait(const PTimeInterval
& timeout
)
862 return PSemaphore::Wait(timeout
);
865 BOOL
PSyncPoint::WillBlock() const
867 return PSemaphore::WillBlock();
870 //////////////////////////////////////////////////////////////////////////////
871 // Extra functionality not found in BeOS
873 int seteuid(uid_t uid
) { return 0; }
874 int setegid(gid_t gid
) { return 0; }
876 // End Of File ///////////////////////////////////////////////////////////////