📄 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.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_THREADSextern int debug_mpthreads;#endifPDECLARE_CLASS(PHouseKeepingThread, PThread) public: PHouseKeepingThread() : PThread(1000, NoAutoDeleteThread, NormalPriority, "Housekeeper") { closing = FALSE; Resume(); } void Main(); void SetClosing() { closing = TRUE; } protected: BOOL closing;};#define new PNEWint 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" (void *)this, // param 2 == "PThread to clean up"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -