📄 tlibcoop.cxx
字号:
/* * tlibcoop.cxx * * Routines for cooperative multi-tasking 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: tlibcoop.cxx,v $ * Revision 1.6 1998/11/03 11:37:45 robertj * Fixed big delay in waiting for child process termination * * Revision 1.5 1998/09/24 04:12:24 robertj * Added open software license. * */void PProcess::Construct(){ CommonConstruct(); ioBlocks[0].AllowDeleteObjects(FALSE); ioBlocks[1].AllowDeleteObjects(FALSE); ioBlocks[2].AllowDeleteObjects(FALSE);}PProcess::~PProcess(){ CommonDestruct();}void PProcess::PXAbortIOBlock(int fd) { for (PINDEX i = 0; i < 3 ; i++) { POrdinalKey key(fd); PThread * thread = ioBlocks[i].GetAt(key); if (thread != NULL) { // ensure the thread is in the current list of active threads for (PThread * start = this; start != thread; start = start->link) PAssert (start->link != this, "I/O abort for inactive thread"); // check for terminated thread PAssert (thread->status != Terminated, "I/O abort for terminated thread"); // zero out the thread entry ioBlocks[i].SetAt(key, NULL); thread->selectReturnVal = -1; thread->selectErrno = EINTR; FD_ZERO(thread->read_fds); FD_ZERO(thread->write_fds); FD_ZERO(thread->exception_fds); } }}/////////////////////////////////////////////////////////////////////////////////// PThreadPThread::PThread(){ hasIOTimer = FALSE; handleWidth = 0; waitPid = 0;}PThread::~PThread(){ if (this == (PThread *)&PProcess::Current()) return; // can never destruct ourselves, unless we are the system process PAssert(this != PThread::Current(), "Thread attempted suicide!"); // call the terminate function so overloads work properly if (!IsTerminated()) Terminate(); // now we can terminate FreeStack();}void PThread::ClearBlock(){ if (waitPid > 0) waitPid = 0; else handleWidth = 0;}static void Copy_Fd_Sets(fd_set & dr, fd_set & dw, fd_set & de, fd_set & sr, fd_set & sw, fd_set & se, int width){ unsigned long *srp = (unsigned long *)&sr; unsigned long *swp = (unsigned long *)&sw; unsigned long *sep = (unsigned long *)&se; unsigned long *drp = (unsigned long *)&dr; unsigned long *dwp = (unsigned long *)&dw; unsigned long *dep = (unsigned long *)&de; int orSize = (width + (8*sizeof (unsigned long)) - 1) / (8*sizeof (unsigned long)); for (int i = 0; i < orSize; i++) { *drp++ = *srp++; *dwp++ = *swp++; *dep++ = *sep++; }}BOOL PThread::IsNoLongerBlocked(){ // check signals PProcess & process = PProcess::Current(); process.PXCheckSignals(); // if are blocked on a child process, see if that child is still going if (waitPid > 0) return kill(waitPid, 0) != 0; // if the I/O block was terminated previously, perhaps by a close from // another thread, then return TRUE. Don't check for timeouts yet - // this means we return if data is available before we timeout because // it isn't there if (selectErrno != 0 || selectReturnVal != 0) return TRUE; // if we aren't blocked on a channel, assert PAssert (handleWidth > 0, "IsNoLongerBlocked called for thread not I/O blocked"); // do a zero time select call to see if we would block if we did the read/write struct timeval timeout = {0, 0}; fd_set rfds, wfds, efds; Copy_Fd_Sets(rfds, wfds, efds, *read_fds, *write_fds, *exception_fds, handleWidth); // check signals on the way in process.PXCheckSignals(); // do the select selectReturnVal = SELECT (handleWidth, read_fds, write_fds, exception_fds, &timeout); // check signals on the way out process.PXCheckSignals(); Copy_Fd_Sets(*read_fds, *write_fds, *exception_fds, rfds, wfds, efds, handleWidth); if (selectReturnVal != 0) { selectErrno = errno; return TRUE; } // if the channel has timed out, return TRUE return hasIOTimer && (ioTimer == 0);}void PProcess::OperatingSystemYield(){ PThread * current = &PProcess::Current(); // setup file descriptor tables for select call fd_set rfds, wfds, efds; FD_ZERO (&rfds); FD_ZERO (&wfds); FD_ZERO (&efds); int width = 0; int waitCount = 0; // process the timer list BEFORE checking timers PTimeInterval delay = GetTimerList()->Process(); // Set maximum delay of 10 seconds if we have waiting child proceses if (waitCount > 0) delay = 10000; // collect the handles across all threads PThread * thread = current; BOOL threadUnblocked = FALSE; do { if (thread->status == BlockedIO) { if (thread->waitPid > 0) waitCount++; else if ((thread->selectReturnVal != 0) || (thread->hasIOTimer && thread->ioTimer == 0)) { threadUnblocked = TRUE; } else if (thread->handleWidth > 0) { unsigned long *srp = (unsigned long *)thread->read_fds; unsigned long *swp = (unsigned long *)thread->write_fds; unsigned long *sep = (unsigned long *)thread->exception_fds; unsigned long *drp = (unsigned long *)&rfds; unsigned long *dwp = (unsigned long *)&wfds; unsigned long *dep = (unsigned long *)&efds; int orSize = (thread->handleWidth + (8*sizeof (unsigned long)) - 1) / (8*sizeof (unsigned long)); for (int i = 0; i < orSize; i++) { *drp++ |= *srp++; *dwp++ |= *swp++; *dep++ |= *sep++; } width = PMAX(width, thread->handleWidth); } } thread = thread->link; } while (thread != current); // // if we found a thread that was IO blocked, but has already found // input, then return now // if (threadUnblocked) return; // // find timeout until next timer event // struct timeval * timeout = NULL; struct timeval timeout_val; if (delay != PMaxTimeInterval) { if (delay.GetMilliSeconds() < 1000L*60L*60L*24L) { timeout_val.tv_usec = (delay.GetMilliSeconds() % 1000) * 1000; timeout_val.tv_sec = delay.GetSeconds(); timeout = &timeout_val; } } // wait for something to happen on an I/O channel, or for a timeout, or a SIGCLD int childPid; if ((width > 0) || (timeout != NULL)) { childPid = wait3(NULL, WNOHANG|WUNTRACED, NULL); if (childPid < 0) { SELECT (width, &rfds, &wfds, &efds, timeout); childPid = wait3(NULL, WNOHANG|WUNTRACED, NULL); } } else if (waitCount > 0) childPid = wait3(NULL, WUNTRACED, NULL); else { //PError << "OperatingSystemYield with no blocks! Sleeping....\n"; ::sleep(1); childPid = 0; } // finish off any child processes that exited if (childPid >= 0) { int retVal; waitpid(childPid, &retVal, WNOHANG); } // process the timer list GetTimerList()->Process();}void PThread::PXSetOSHandleBlock(int fd, int type){ POrdinalKey key(fd); PProcess & proc = PProcess::Current(); if (type & 1) { PAssert(!proc.ioBlocks[0].Contains(key), "Attempt to read block on handle which already has a pending read operation"); proc.ioBlocks[0].SetAt(key, this); } if (type & 2) { PAssert(!proc.ioBlocks[1].Contains(key), "Attempt to write block on handle which already has a pending write operation"); proc.ioBlocks[1].SetAt(key, this); } if (type & 4) { PAssert(!proc.ioBlocks[2].Contains(key), "Attempt to exception block on handle which already has a pending except operation"); proc.ioBlocks[2].SetAt(key, this); }}void PThread::PXClearOSHandleBlock(int fd, int type){ POrdinalKey key(fd); PProcess & proc = PProcess::Current(); if ((type & 1) && (proc.ioBlocks[0].GetAt(key) == this)) proc.ioBlocks[0].SetAt(key, NULL); if ((type & 2) && (proc.ioBlocks[1].GetAt(key) == this)) proc.ioBlocks[1].SetAt(key, NULL); if ((type & 4) && (proc.ioBlocks[2].GetAt(key) == this)) proc.ioBlocks[2].SetAt(key, NULL);}//////////////////////////////////////////////////////////////////////// PThread::PXBlockOnIO// This function performs the specified IO block// The return value is the value that the select call returned// which caused the block to return. errno is also set to the // value of errno at the time the select returned.//int PThread::PXBlockOnIO(int handle, int type, const PTimeInterval & timeout){ // make sure this thread is running PAssert(status == Running, "Attempt to I/O block a thread which is not running"); // make sure we flush the buffer before doing a write fd_set tmp_rfd, tmp_wfd, tmp_efd; read_fds = &tmp_rfd; write_fds = &tmp_wfd; exception_fds = &tmp_efd; FD_ZERO (read_fds); FD_ZERO (write_fds); FD_ZERO (exception_fds); int blockType; switch (type) { case PChannel::PXReadBlock: case PChannel::PXAcceptBlock: FD_SET (handle, read_fds); blockType = 1; break; case PChannel::PXWriteBlock: FD_SET (handle, write_fds); blockType = 2; break; case PChannel::PXConnectBlock: FD_SET (handle, write_fds); FD_SET (handle, exception_fds); blockType = 2+4; break; default: PAssertAlways(PLogicError); return 0; } // make sure no other thread is blocked on this os_handle PXSetOSHandleBlock(handle, blockType); hasIOTimer = timeout != PMaxTimeInterval; if (hasIOTimer) ioTimer = timeout; selectErrno = selectReturnVal = 0; handleWidth = handle+1; waitPid = 0; status = BlockedIO; Yield(); PXClearOSHandleBlock(handle, blockType); ioTimer.Stop(); handleWidth = 0; errno = selectErrno; return selectReturnVal;}int PThread::PXBlockOnIO(int maxHandles, fd_set & readBits, fd_set & writeBits, fd_set & exceptionBits, const PTimeInterval & timeout, const PIntArray & osHandles){ PAssert(status == Running, "Attempt to I/O block a thread which is not running"); for (PINDEX i = 0; i < osHandles.GetSize(); i+=2) PXSetOSHandleBlock(osHandles[i], osHandles[i+1]); read_fds = &readBits; write_fds = &writeBits; exception_fds = &exceptionBits; handleWidth = maxHandles; hasIOTimer = timeout != PMaxTimeInterval; if (hasIOTimer) ioTimer = timeout; selectErrno = selectReturnVal = 0; waitPid = 0; status = BlockedIO; Yield(); for (PINDEX i = 0; i < osHandles.GetSize(); i+=2) PXClearOSHandleBlock(osHandles[i], osHandles[i+1]); ioTimer.Stop(); handleWidth = 0; errno = selectErrno; return selectReturnVal;}int PThread::PXBlockOnChildTerminate(int pid, const PTimeInterval & timeout){ waitPid = pid; hasIOTimer = timeout != PMaxTimeInterval; if (hasIOTimer) ioTimer = timeout; status = BlockedIO; Yield(); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -