⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tlibmpthrd.cxx

📁 sloedgy open sip stack source code
💻 CXX
📖 第 1 页 / 共 2 页
字号:
/*
 * 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 + -