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.17 2004/02/23 18:10:39 ykiryanov
31 * Added a parameter to semaphore constructor to avoid ambiguity
33 * Revision 1.16 2004/02/23 00:02:20 ykiryanov
34 * Changed my e-mail to ykiryanov at users.sourceforge.net. Just in case someone wants to collaborate
36 * Revision 1.15 2004/02/22 23:59:28 ykiryanov
37 * Added missing functions: PProcess::SetMaxHandles(), PThread::GetCurrentThreadId(),
38 * PThread::PXAbortBlock(), PSyncPoint::Signal(), ::Wait(), ::Wait(timeout), ::WillBlock()
40 * Revision 1.14 2004/02/22 04:35:04 ykiryanov
41 * Removed PMutex desctructor
43 * Revision 1.13 2003/02/26 01:13:18 robertj
44 * Fixed race condition where thread can terminatebefore an IsSuspeded() call
45 * occurs and cause an assert, thanks Sebastian Meyer
47 * Revision 1.12 2001/06/30 06:59:07 yurik
48 * Jac Goudsmit from Be submit these changes 6/28. Implemented by Yuri Kiryanov
50 * Revision 1.11 2001/03/07 06:57:32 yurik
51 * Changed email to current one
53 * Revision 1.10 2001/01/16 12:32:06 rogerh
54 * Remove duplicate SetAutoDelete() function. Submitted by
55 * Jac Goudsmit <jac_goudsmit@yahoo.com>
65 ///////////////////////////////////////////////////////////////////////////////
67 //#define DEBUG_THREADS
69 static int const priorities
[] = {
70 1, // Lowest priority is 1. 0 is not
74 B_URGENT_DISPLAY_PRIORITY
,
77 int32
PThread::ThreadFunction(void * threadPtr
)
79 PThread
* thread
= (PThread
*)PAssertNULL(threadPtr
);
81 PProcess
& process
= PProcess::Current();
83 process
.activeThreadMutex
.Wait();
84 process
.activeThreads
.SetAt(thread
->threadId
, thread
);
85 process
.activeThreadMutex
.Signal();
87 process
.SignalTimerChange();
95 : threadId(B_BAD_THREAD_ID
),
96 priority(B_NORMAL_PRIORITY
),
101 PThread::PThread(PINDEX stackSize
,
102 AutoDeleteFlag deletion
,
103 Priority priorityLevel
,
104 const PString
& name
)
107 PAssert(stackSize
> 0, PInvalidParameter
);
108 autoDelete
= deletion
== AutoDeleteThread
;
109 originalStackSize
= stackSize
;
111 priority
= priorities
[priorityLevel
];
113 PString
str("PWLT ");
116 PError
<< "::spawn_thread(" << str
<< "), priority:" << priority
<< endl
;
119 threadId
= ::spawn_thread(ThreadFunction
, // Function
120 (const char*) str
, // Name
121 priority
, // Priority
122 (void *) this); // Pass this as cookie
124 PAssertOS(threadId
>= B_NO_ERROR
);
127 PError
<< ", id: " << threadId
<< endl
;
131 PProcess
& process
= PProcess::Current();
132 process
.deleteThreadMutex
.Wait();
133 process
.autoDeleteThreads
.Append(this);
134 process
.deleteThreadMutex
.Signal();
141 if (originalStackSize
<= 0)
144 PProcess
& process
= PProcess::Current();
145 process
.activeThreadMutex
.Wait();
146 process
.activeThreads
.SetAt(threadId
, NULL
);
147 process
.activeThreadMutex
.Signal();
154 void PThread::Restart()
156 PAssert(IsTerminated(), "Cannot restart running thread");
161 PError
<< "::spawn_thread(" << str
<< "), priority:" << priority
<< endl
;
164 threadId
= ::spawn_thread(ThreadFunction
, // Function
165 (const char*) str
, // Name
167 (void *) this); // Pass this as cookie
170 PError
<< ", id: " << threadId
<< endl
;
173 PAssertOS(threadId
>= B_NO_ERROR
);
177 void PThread::Terminate()
179 PAssert(!IsTerminated(), "Operation on terminated thread");
180 PAssert(originalStackSize
> 0, PLogicError
);
182 if (Current() == this)
184 sem_id semId
= ::create_sem( 1, "PWST" );
185 if ( ::acquire_sem(semId
) == B_NO_ERROR
)
187 // Invalidate the thread
188 threadId
= B_BAD_THREAD_ID
;
189 ::release_sem(semId
);
193 PError
<< "::exit_thread(0), id:" << threadId
<< endl
;
200 sem_id semId
= ::create_sem( 1, "PWTS" );
201 if ( ::acquire_sem(semId
) == B_NO_ERROR
)
206 // Invalidate the thread
207 threadId
= B_BAD_THREAD_ID
;
210 if (idToKill
!= B_BAD_THREAD_ID
)
212 ::release_sem(semId
);
216 PError
<< "::kill_thread(" << idToKill
<< ")" << endl
;
218 ::kill_thread(idToKill
);
223 PAssert(threadId
== B_BAD_THREAD_ID
, "Can't acquire semaphore to terminate thread");
227 BOOL
PThread::IsTerminated() const
229 return threadId
== B_BAD_THREAD_ID
;
233 void PThread::WaitForTermination() const
235 WaitForTermination(PMaxTimeInterval
);
239 BOOL
PThread::WaitForTermination(const PTimeInterval
& /*maxWait*/) const // Fix timeout
241 status_t result
= B_NO_ERROR
;
242 status_t exit_value
= B_NO_ERROR
;
245 PError
<< "::wait_for_thread(" << threadId
<< "), result:";
248 result
= ::wait_for_thread(threadId
, &exit_value
);
249 if ( result
== B_INTERRUPTED
) { // thread was killed.
251 PError
<< "B_INTERRUPTED" << endl
;
256 if ( result
== B_OK
) { // thread is dead
258 PError
<< "B_OK" << endl
;
263 if ( result
== B_BAD_THREAD_ID
) { // thread has invalid id
265 PError
<< "B_BAD_THREAD_ID" << endl
;
274 void PThread::Suspend(BOOL susp
)
276 PAssert(!IsTerminated(), "Operation on terminated thread");
279 status_t result
= ::suspend_thread(threadId
);
281 PAssert(result
== B_OK
, "Thread don't want to be suspended");
288 void PThread::Resume()
290 PAssert(!IsTerminated(), "Operation on terminated thread");
291 status_t result
= ::resume_thread(threadId
);
293 PAssert(result
== B_NO_ERROR
, "Thread doesn't want to resume");
297 BOOL
PThread::IsSuspended() const
300 status_t result
= ::get_thread_info(threadId
, &info
);
302 PAssert(result
== B_OK
&& threadId
== info
.thread
, "Thread info inaccessible");
303 return info
.state
== B_THREAD_SUSPENDED
;
306 void PThread::SetAutoDelete(AutoDeleteFlag deletion
)
308 PAssert(deletion
!= AutoDeleteThread
|| this != &PProcess::Current(), PLogicError
);
309 autoDelete
= deletion
== AutoDeleteThread
;
312 void PThread::SetPriority(Priority priorityLevel
)
314 PAssert(!IsTerminated(), "Operation on terminated thread");
316 priority
= priorities
[priorityLevel
];
317 status_t result
= ::set_thread_priority(threadId
, priority
);
319 PAssert(result
== B_OK
, "Thread priority change error");
323 PThread::Priority
PThread::GetPriority() const
325 PAssert(!IsTerminated(), "Operation on terminated thread");
329 return LowestPriority
;
330 case B_LOW_PRIORITY
:
332 case B_NORMAL_PRIORITY
:
333 return NormalPriority
;
334 case B_DISPLAY_PRIORITY
:
336 case B_URGENT_DISPLAY_PRIORITY
:
337 return HighestPriority
;
339 PAssertAlways(POperatingSystemError
);
340 return LowestPriority
;
343 void PThread::Yield()
345 // we just sleep for long enough to cause a reschedule (100 microsec)
349 void PThread::Sleep( const PTimeInterval
& delay
) // Time interval to sleep for.
351 bigtime_t microseconds
=
352 delay
== PMaxTimeInterval
? B_INFINITE_TIMEOUT
: (delay
.GetMilliSeconds() * 1000 );
354 status_t result
= ::snooze( microseconds
) ; // delay in ms, snooze in microsec
355 PAssert(result
== B_OK
, "Thread has insomnia");
358 void PThread::InitialiseProcessThread()
360 originalStackSize
= 0;
363 threadId
= ::find_thread(NULL
);
364 PAssertOS(threadId
>= B_NO_ERROR
);
366 ((PProcess
*)this)->activeThreads
.DisallowDeleteObjects();
367 ((PProcess
*)this)->activeThreads
.SetAt(threadId
, this);
371 PThread
* PThread::Current()
373 PProcess
& process
= PProcess::Current();
374 process
.activeThreadMutex
.Wait();
376 thread_id tId
= ::find_thread(NULL
);
377 PAssertOS(tId
>= B_NO_ERROR
);
379 PThread
* thread
= process
.activeThreads
.GetAt( tId
);
381 process
.activeThreadMutex
.Signal();
385 int PThread::PXBlockOnChildTerminate(int pid
, const PTimeInterval
& /*timeout*/) // Fix timeout
387 status_t result
= B_NO_ERROR
;
388 status_t exit_value
= B_NO_ERROR
;
391 PError
<< "::wait_for_thread(" << pid
<< "), result:";
394 result
= ::wait_for_thread(pid
, &exit_value
);
395 if ( result
== B_INTERRUPTED
) { // thread was killed.
397 PError
<< "B_INTERRUPTED" << endl
;
402 if ( result
== B_OK
) { // thread is dead
404 PError
<< "B_OK" << endl
;
409 if ( result
== B_BAD_THREAD_ID
) { // thread has invalid id
411 PError
<< "B_BAD_THREAD_ID" << endl
;
419 PThreadIdentifier
PThread::GetCurrentThreadId(void)
421 return ::find_thread(NULL
);
424 int PThread::PXBlockOnIO(int handle
, int type
, const PTimeInterval
& timeout
)
426 // make sure we flush the buffer before doing a write
427 fd_set tmp_rfd
, tmp_wfd
, tmp_efd
;
428 fd_set
* read_fds
= &tmp_rfd
;
429 fd_set
* write_fds
= &tmp_wfd
;
430 fd_set
* exception_fds
= &tmp_efd
;
434 FD_ZERO (exception_fds
);
437 case PChannel::PXReadBlock
:
438 case PChannel::PXAcceptBlock
:
439 FD_SET (handle
, read_fds
);
441 case PChannel::PXWriteBlock
:
442 FD_SET (handle
, write_fds
);
444 case PChannel::PXConnectBlock
:
445 FD_SET (handle
, write_fds
);
446 FD_SET (handle
, exception_fds
);
449 PAssertAlways(PLogicError
);
453 struct timeval
* tptr
= NULL
;
454 struct timeval timeout_val
;
455 if (timeout
!= PMaxTimeInterval
) { // Clean up for infinite timeout
456 static const PTimeInterval
oneDay(0, 0, 0, 0, 1);
457 if (timeout
< oneDay
) {
459 timeout_val
.tv_usec
= (timeout
.GetMilliSeconds() % 1000) * 1000;
460 timeout_val
.tv_sec
= timeout
.GetSeconds();
465 int retval
= ::select(handle
+1, read_fds
, write_fds
, exception_fds
, tptr
);
466 PProcess::Current().PXCheckSignals();
471 int PThread::PXBlockOnIO(int maxHandles
,
474 fd_set
& exceptionBits
,
475 const PTimeInterval
& timeout
,
476 const PIntArray
& /*osHandles*/)
478 // make sure we flush the buffer before doing a write
479 fd_set
* read_fds
= &readBits
;
480 fd_set
* write_fds
= &writeBits
;
481 fd_set
* exception_fds
= &exceptionBits
;
483 struct timeval
* tptr
= NULL
;
484 struct timeval timeout_val
;
485 if (timeout
!= PMaxTimeInterval
) { // Clean up for infinite timeout
486 static const PTimeInterval
oneDay(0, 0, 0, 0, 1);
487 if (timeout
< oneDay
) {
488 timeout_val
.tv_usec
= (timeout
.GetMilliSeconds() % 1000) * 1000;
489 timeout_val
.tv_sec
= timeout
.GetSeconds();
494 int retval
= ::select(maxHandles
, read_fds
, write_fds
, exception_fds
, tptr
);
495 PProcess::Current().PXCheckSignals();
499 void PThread::PXAbortBlock(void) const
503 ///////////////////////////////////////////////////////////////////////////////
506 void PProcess::Construct()
510 CreateConfigFilesDictionary();
515 PProcess::HouseKeepingThread::HouseKeepingThread()
516 : PThread(256*1024 , NoAutoDeleteThread
, LowPriority
, "HouseKeepingThread")
521 void PProcess::HouseKeepingThread::Main()
523 PProcess
& process
= PProcess::Current();
527 process
.deleteThreadMutex
.Wait();
529 for (PINDEX i
= 0; i
< process
.autoDeleteThreads
.GetSize(); i
++)
531 PThread
* pThread
= (PThread
*)
532 process
.autoDeleteThreads
.GetAt(i
);
533 if( pThread
->IsTerminated() )
535 process
.autoDeleteThreads
.RemoveAt(i
--);
539 process
.deleteThreadMutex
.Signal();
541 PTimeInterval nextTimer
= process
.timers
.Process();
542 if (nextTimer
!= PMaxTimeInterval
)
544 if ( nextTimer
.GetInterval() > 10000 )
550 breakBlock
.Wait( nextTimer
);
554 void PProcess::SignalTimerChange()
557 houseKeeper
= new HouseKeepingThread
;
559 houseKeeper
->breakBlock
.Signal();
562 BOOL
PProcess::SetMaxHandles(int)
567 PProcess::~PProcess()
569 Sleep(100); // Give threads time to die a natural death
574 // OK, if there are any left we get really insistent...
575 activeThreadMutex
.Wait();
576 for (PINDEX i
= 0; i
< activeThreads
.GetSize(); i
++) {
577 PThread
* pThread
= activeThreads
.GetAt(i
);
578 if (pThread
&& (this != pThread
) && !pThread
->IsTerminated())
579 pThread
->Terminate(); // With extreme prejudice
581 activeThreadMutex
.Signal();
583 deleteThreadMutex
.Wait();
584 autoDeleteThreads
.RemoveAll();
585 deleteThreadMutex
.Signal();
590 ///////////////////////////////////////////////////////////////////////////////
592 //#define DEBUG_SEMAPHORES
593 #define USE_BENAPHORES // Comment this line if you don't want benaphores
595 PSemaphore::PSemaphore(sem_id anId
, int32 initialBenaphore
, int32 param
)
596 : semId( anId
), benaphoreCount(initialBenaphore
)
598 #ifdef DEBUG_SEMAPHORES
599 PAssertOS(semId
!= 0);
603 PSemaphore::PSemaphore(unsigned initial
, unsigned maxCount
)
604 : semId(0), benaphoreCount(0)
606 PAssertOS(FALSE
); // This constructor is never called
609 PSemaphore::~PSemaphore()
611 status_t result
= B_NO_ERROR
;
613 #ifdef DEBUG_SEMAPHORES
614 PAssertOS( semId
>= B_NO_ERROR
);
617 #ifdef DEBUG_SEMAPHORES
619 get_sem_count(semId
, &semCnt
);
620 PError
<< "::delete_sem " << semId
<< ", count:" << semCnt
<< endl
;
625 result
= ::delete_sem(semId
);
628 #ifdef DEBUG_SEMAPHORES
629 if( result
!= B_NO_ERROR
)
630 PError
<< "::Error: " << strerror(result
) << endl
;
634 void PSemaphore::Wait()
636 status_t result
= B_NO_ERROR
;
638 #ifdef DEBUG_SEMAPHORES
639 PAssertOS( semId
>= B_NO_ERROR
);
642 #ifdef DEBUG_SEMAPHORES
643 PError
<< "PSemaphore::Wait, benaphore: " << benaphoreCount
<< endl
;
646 #ifdef USE_BENAPHORES
647 if( atomic_add( &benaphoreCount
, 1 ) > 0 ) {
648 #endif // USE_BENAPHORES
650 #ifdef DEBUG_SEMAPHORES
652 get_sem_info(semId
, &info
);
653 PError
<< "::acquire_sem_etc, id: " << semId
<< " (" << info
.name
<< "), count:" << info
.count
<< endl
;
656 while ((result
= ::acquire_sem(semId
)) == B_INTERRUPTED
)
658 #ifdef DEBUG_SEMAPHORES
659 PError
<< "::acquire_sem_etc " << semId
<< ", interrupted!" << endl
;
663 #ifdef DEBUG_SEMAPHORES
664 if( result
!= B_NO_ERROR
)
665 PError
<< "::Error: " << strerror(result
) << endl
;
668 #ifdef USE_BENAPHORES
669 atomic_add(&benaphoreCount
, -1);
671 #endif // USE_BENAPHORES
675 BOOL
PSemaphore::Wait(const PTimeInterval
& timeout
)
677 status_t result
= B_NO_ERROR
;
679 #ifdef DEBUG_SEMAPHORES
680 PAssertOS( semId
>= B_NO_ERROR
);
683 #ifdef DEBUG_SEMAPHORES
684 PError
<< "PSemaphore::Wait(timeout), benaphore: " << benaphoreCount
<< endl
;
687 #ifdef USE_BENAPHORES
688 if( atomic_add( &benaphoreCount
, 1 ) > 0 ) {
689 #endif // USE_BENAPHORES
691 PInt64 ms
= timeout
.GetMilliSeconds();
692 bigtime_t microseconds
=
693 ms
? timeout
== PMaxTimeInterval
? B_INFINITE_TIMEOUT
: ( ms
* 1000 ) : 0;
695 #ifdef DEBUG_SEMAPHORES
697 get_sem_count(semId
, &semCnt
);
698 PError
<< "::acquire_sem_etc " << semId
<< ", count:" << semCnt
<< ", timeout:";
700 if( microseconds
== B_INFINITE_TIMEOUT
)
701 PError
<< "infinite" << endl
;
703 PError
<< microseconds
<< endl
;
706 result
= ::acquire_sem_etc(semId
, 1,
707 B_RELATIVE_TIMEOUT
, microseconds
);
709 #ifdef DEBUG_SEMAPHORES
710 if( result
!= B_NO_ERROR
)
712 PError
<< "::acquire_sem_etc " << semId
<< " with ";
713 if( microseconds
== B_INFINITE_TIMEOUT
)
714 PError
<< "infinite";
716 PError
<< microseconds
;
717 PError
<< " timeout failed, Error: " << strerror(result
) << endl
;
721 #ifdef USE_BENAPHORES
722 atomic_add(&benaphoreCount
, -1);
724 #endif // USE_BENAPHORES
726 return result
== B_TIMED_OUT
;
730 void PSemaphore::Signal()
732 #ifdef DEBUG_SEMAPHORES
733 PAssertOS( semId
>= B_NO_ERROR
);
736 #ifdef DEBUG_SEMAPHORES
737 PError
<< "PSemaphore::Wait(timeout), benaphore: " << benaphoreCount
<< endl
;
740 #ifdef USE_BENAPHORES
741 if( atomic_add( &benaphoreCount
, -1 ) > 1 )
743 #endif // USE_BENAPHORES
745 #ifdef DEBUG_SEMAPHORES
748 ::release_sem_etc(semId
, 1, 0);
750 #ifdef DEBUG_SEMAPHORES
751 if( result
!= B_NO_ERROR
)
752 PError
<< "::Error:" << strerror(result
) << endl
;
755 #ifdef USE_BENAPHORES
757 #endif // USE_BENAPHORES
761 BOOL
PSemaphore::WillBlock() const
763 status_t result
= B_NO_ERROR
;
765 #ifdef DEBUG_SEMAPHORES
766 PAssertOS( semId
>= B_NO_ERROR
);
769 #ifdef USE_BENAPHORES
770 if( atomic_add( (volatile int32
*) &benaphoreCount
, -1 ) > 1 )
772 #endif // USE_BENAPHORES
774 #ifdef DEBUG_SEMAPHORES
776 get_sem_count(semId
, &semCnt
);
777 PError
<< "::acquire_sem_etc (WillBlock) " << semId
<< ", count:" << semCnt
<< endl
;
780 result
= ::acquire_sem_etc(semId
, 0, B_RELATIVE_TIMEOUT
, 0);
782 #ifdef DEBUG_SEMAPHORES
783 if( result
!= B_NO_ERROR
)
784 PError
<< "::Error:" << strerror(result
) << endl
;
787 #ifdef USE_BENAPHORES
789 #endif // USE_BENAPHORES
791 return result
== B_WOULD_BLOCK
;
794 ///////////////////////////////////////////////////////////////////////////////
798 : PSemaphore( ::create_sem(1, "PWLM" ) , 0 )
800 #ifdef DEBUG_SEMAPHORES
801 PError
<< "::create_sem(PMutex) " << semId
<< endl
;
802 PAssertOS( semId
>= B_NO_ERROR
);
811 BOOL
PMutex::Wait(const PTimeInterval
& timeout
)
813 return PSemaphore::Wait(timeout
);
816 void PMutex::Signal()
818 PSemaphore::Signal();
821 BOOL
PMutex::WillBlock() const
823 return PSemaphore::WillBlock();
826 ///////////////////////////////////////////////////////////////////////////////
829 PSyncPoint::PSyncPoint()
830 : PSemaphore( ::create_sem(0, "PWLP" ), 1 )
832 #ifdef DEBUG_SEMAPHORES
833 PError
<< "::create_sem(PSyncPoint) " << semId
<< endl
;
836 PAssertOS( semId
>= B_NO_ERROR
);
839 void PSyncPoint::Signal()
841 PSemaphore::Signal();
844 void PSyncPoint::Wait()
849 BOOL
PSyncPoint::Wait(const PTimeInterval
& timeout
)
851 return PSemaphore::Wait(timeout
);
854 BOOL
PSyncPoint::WillBlock() const
856 return PSemaphore::WillBlock();
859 //////////////////////////////////////////////////////////////////////////////
860 // Extra functionality not found in BeOS
862 int seteuid(uid_t uid
) { return 0; }
863 int setegid(gid_t gid
) { return 0; }
865 // End Of File ///////////////////////////////////////////////////////////////