📄 tlibmpthrd.cxx
字号:
/*
* tlibmpthrd.cxx
*
* Routines for Macintosh pre-emptive threading system
*
* Portable Windows Library
*
* Copyright (c) 1993-1998 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* $Log: tlibmpthrd.cxx,v $
* Revision 1.2 2007/03/11 03:58:47 joegenbaclor
* Disabled PMEMORY_CHECK for release builds
*
* Revision 1.1 2006/06/29 04:18:41 joegenbaclor
* *** empty log message ***
*
* Revision 1.4 2002/06/27 06:38:58 robertj
* Changes to remove memory leak display for things that aren't memory leaks.
*
* Revision 1.3 2002/02/19 07:40:59 rogerh
* Remove PMutex destructor for Carbon.
*
* Revision 1.2 2002/02/19 07:28:02 rogerh
* PXAbortIO -> PXAbortBlock. Submitted by Peter Johnson <paj@mac.com>
*
* Revision 1.1 2001/08/11 15:38:43 rogerh
* Add Mac OS Carbon changes from John Woods <jfw@jfwhome.funhouse.com>
*
*/
#include <sys/resource.h>
#include <new> // just because I want to throw std::bad_alloc...
#ifndef NDEBUG
#define DEBUG_THREADS
extern int debug_mpthreads;
#endif
PDECLARE_CLASS(PHouseKeepingThread, PThread)
public:
PHouseKeepingThread()
: PThread(1000, NoAutoDeleteThread, NormalPriority, "Housekeeper")
{ closing = FALSE; Resume(); }
void Main();
void SetClosing() { closing = TRUE; }
protected:
BOOL closing;
};
#define new PNEW
int PThread::PXBlockOnIO(int handle, int type, const PTimeInterval & timeout)
{
//PTRACE(1,"PThread::PXBlockOnIO(" << handle << ',' << type << ')');
// make sure we flush the buffer before doing a write
fd_set tmp_rfd, tmp_wfd, tmp_efd;
fd_set * read_fds = &tmp_rfd;
fd_set * write_fds = &tmp_wfd;
fd_set * exception_fds = &tmp_efd;
struct timeval * tptr = NULL;
struct timeval timeout_val;
if (timeout != PMaxTimeInterval) {
static const PTimeInterval oneDay(0, 0, 0, 0, 1);
if (timeout < oneDay) {
timeout_val.tv_usec = (timeout.GetMilliSeconds() % 1000) * 1000;
timeout_val.tv_sec = timeout.GetSeconds();
tptr = &timeout_val;
}
}
int retval;
for (;;) {
FD_ZERO (read_fds);
FD_ZERO (write_fds);
FD_ZERO (exception_fds);
switch (type) {
case PChannel::PXReadBlock:
case PChannel::PXAcceptBlock:
FD_SET (handle, read_fds);
break;
case PChannel::PXWriteBlock:
FD_SET (handle, write_fds);
break;
case PChannel::PXConnectBlock:
FD_SET (handle, write_fds);
FD_SET (handle, exception_fds);
break;
default:
PAssertAlways(PLogicError);
return 0;
}
// include the termination pipe into all blocking I/O functions
int width = handle+1;
FD_SET(unblockPipe[0], read_fds);
width = PMAX(width, unblockPipe[0]+1);
retval = ::select(width, read_fds, write_fds, exception_fds, tptr);
if ((retval >= 0) || (errno != EINTR))
break;
}
if ((retval == 1) && FD_ISSET(unblockPipe[0], read_fds)) {
BYTE ch;
::read(unblockPipe[0], &ch, 1);
errno = EINTR;
retval = -1;
//PTRACE(1,"Unblocked I/O");
}
return retval;
}
void PThread::PXAbortBlock() const
{
BYTE ch;
::write(unblockPipe[1], &ch, 1);
}
// For Mac OS, the housekeeping thread has two jobs:
// First, poll for synchronous signals (as is traditional), and
// second, to poll the MPThread termination notification queue and clean up
// deceased PThreads.
// There is an ickiness here which depends on a current restriction of
// Mac OS X: synchronous signals (i.e. not signals resulting from
// exceptions) are only delivered to the main thread. I assume that
// it is therefore safe for the main thread to call MPNotifyQueue from
// a signal handler if and only if the main thread never calls MPNotifyQueue
// on the termination notification queue from its main code. This ought to
// be acceptable if notifying a queue is single-threaded per queue; if
// MPNotifyQueue has a global critical section, this will work very badly.
static MPQueueID terminationNotificationQueue = 0;
// This bites. Threads don't know what process they come from (even though
// there can be only one), yet when we're winding down the process thread
// kills the housekeeper before it can clean up all the other threads.
// So the process thread has to poll the termination queue, but it does so
// from PThread context, so it can't know there's no housekeeper. yuck.
static BOOL noHousekeeper = 0;
static void SetUpTermQueue()
{
OSStatus err;
// Let us PLEASE try not to get into any "create/delete" loops here.
// Create it and be DONE with it.
while (!terminationNotificationQueue) {
MPQueueID tempQueue;
err = MPCreateQueue(&tempQueue);
PAssert(err == noErr, "MPCreateQueue failed");
// When Motorola finally finishes the 620, there's a lot of Mac code
// gonna need to be rewritten.
// HAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA!!!
// "finishes the 620". hee hee hee!
if (!OTCompareAndSwap32(0, (UInt32)tempQueue,
(UInt32*)&terminationNotificationQueue)) {
// then someone else snuck in and initialized it.
MPDeleteQueue(tempQueue);
} else {
#ifdef DEBUG_THREADS
if (debug_mpthreads)
fprintf(stderr,"set up notification queue\n");
#endif
// XXX MPNotifyQueue is perfectly willing to allocate memory
// XXX for the items dropped in the queue. However, if it can't,
// XXX then life just goes on -- and we miss a thread exit message.
// XXX If we reserve queue space, however, then we guarantee two
// XXX things: 1, we absolutely will be able to receive N
// XXX notifications, and 2, we absolutely will drop the N+1st
// XXX on the floor, spare memory or no. The target applications
// XXX for this library do not appear (currently) to generate
// XXX absurd numbers of threads, so I'll reserve an absurd number
// XXX of messages, and pretend that nothing can go wrong.
// XXX n go wrong.n go wrong.n go wrong.n go wrong.n go wrong.
// XXX If the following fails, it's probably for lack of memory,
// XXX in which case the queue will just try dynamic allocation.
(void)MPSetQueueReserve(terminationNotificationQueue, 128);
}
}
}
static BOOL PollNotificationQueue(Duration timeout)
{
OSStatus err = noErr;
void *parm1, *parm2, *parm3;
err = MPWaitOnQueue(terminationNotificationQueue,
&parm1, &parm2, &parm3,
timeout);
if (err == noErr) {
// then we got a notification
if ((int)parm1 == 1) {
// then it was a thread death notification, parm2 is
// the PThread pointer
#ifdef DEBUG_THREADS
if (debug_mpthreads)
fprintf(stderr,"notified of %p death\n", parm2);
#endif
PThread::PX_ThreadEnd(parm2);
} // else parm1 == 0 and it's just a wakeup notice
}
return err == noErr;
}
void PHouseKeepingThread::Main()
{
PProcess & process = PProcess::Current();
SetUpTermQueue();
while (!closing) {
PTimeInterval waitTime = process.timers.Process();
Duration timeout;
if (waitTime == PMaxTimeInterval)
timeout = kDurationForever;
else {
// "Values of type Duration are 32 bits long. They are intepreted
// in a manner consistend with the Time Manager -- positive values
// are in units of milliseconds, negative values are in units of
// microseconds."
// Fortunately, PMaxTimeInterval is limited to a positive 32-bit
// number of milliseconds.
timeout = (long)waitTime.GetMilliSeconds();
}
// Block on the notification queue
(void)PollNotificationQueue(timeout);
// whether we timed out or got notified, check the signals.
process.PXCheckSignals();
}
noHousekeeper = 1;
#ifdef DEBUG_THREADS
if (debug_mpthreads)
fprintf(stderr,"housekeeper exiting\n");
#endif
}
void PProcess::Construct()
{
// set the file descriptor limit to something sensible
struct rlimit rl;
PAssertOS(getrlimit(RLIMIT_NOFILE, &rl) == 0);
rl.rlim_cur = rl.rlim_max;
PAssertOS(setrlimit(RLIMIT_NOFILE, &rl) == 0);
SetUpTermQueue();
// initialise the housekeeping thread
housekeepingThread = NULL;
CommonConstruct();
}
PProcess::~PProcess()
{
// Don't wait for housekeeper to stop if Terminate() is called from it.
if (housekeepingThread != NULL && PThread::Current() != housekeepingThread) {
housekeepingThread->SetClosing();
SignalTimerChange();
housekeepingThread->WaitForTermination();
delete housekeepingThread;
housekeepingThread = 0;
}
// XXX try to gracefully handle shutdown transient where the housekeeping
// XXX thread hasn't managed to clean up all the threads
while (PollNotificationQueue(kDurationImmediate)) ;
CommonDestruct();
}
PThread::PThread()
{
// see InitialiseProcessThread()
}
void PThread::InitialiseProcessThread()
{
OSStatus err = 0;
PX_origStackSize = 0;
autoDelete = FALSE;
PX_threadId = MPCurrentTaskID();
PX_suspendCount = 0;
::pipe(unblockPipe);
// Sadly, Mac OS MPThreads can't just initialize a block of memory into
// an MPSemaphore (XXX ought to be a CriticalRegion, but they're broken
// in Mac OS X 10.0.x!)
PX_suspendMutex = 0;
if ((err = MPCreateSemaphore(1,1,&PX_suspendMutex))
!= 0) {
PAssertOS(err == 0);
throw std::bad_alloc();
}
((PProcess *)this)->activeThreads.DisallowDeleteObjects();
((PProcess *)this)->activeThreads.SetAt((unsigned)PX_threadId, this);
}
PThread::PThread(PINDEX stackSize,
AutoDeleteFlag deletion,
Priority /*priorityLevel*/,
const PString & name)
: threadName(name), PX_signature(kMPThreadSig)
{
OSStatus err = 0;
PAssert(stackSize > 0, PInvalidParameter);
PX_origStackSize = stackSize;
autoDelete = (deletion == AutoDeleteThread);
// Sadly, Mac OS MPThreads can't just initialize a block of memory into
// an MPSemaphore (XXX ought to be a CriticalRegion, but they're broken
// in Mac OS X 10.0.x!)
PX_suspendMutex = 0;
if ((err = MPCreateSemaphore(1,1,&PX_suspendMutex)) != 0) {
PAssert(err == 0, "MPCreateSemaphore failed");
throw std::bad_alloc();
}
::pipe(unblockPipe);
// throw the new thread
PX_NewThread(TRUE);
}
PThread::~PThread()
{
if (!IsTerminated())
Terminate();
::close(unblockPipe[0]);
::close(unblockPipe[1]);
if (PX_suspendMutex)
MPDeleteSemaphore(PX_suspendMutex);
#ifdef DEBUG_THREADS
if (debug_mpthreads)
fprintf(stderr,"thread %p destructing\n", this);
#endif
PX_signature = kMPDeadSig;
}
void PThread::PX_NewThread(BOOL startSuspended)
{
OSErr err;
// initialise suspend counter and create mutex
PX_suspendCount = startSuspended ? 1 : 0;
// initialise Suspend/Resume semaphore (for Mac OS X)
// XXX The MPThread manager allows for starting tasks "suspended", but I
// XXX suspect that only works if you have a debugger registered.
suspend_semaphore = new PSemaphore(0,1);
// throw the thread
SetUpTermQueue();
// create the task.
#ifdef DEBUG_THREADS
if (debug_mpthreads)
fprintf(stderr,"thread %p being started\n", (void *)this);
#endif
err = MPCreateTask( (TaskProc)PX_ThreadStart, (void*)this,
65536, // stacksize
terminationNotificationQueue,
(void *)1, // param 1 == "death"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -