📄 tclunixnotfy.c
字号:
if (filePtr->mask & TCL_WRITABLE) { (tsdPtr->checkMasks+MASK_SIZE)[index] &= ~bit; } if (filePtr->mask & TCL_EXCEPTION) { (tsdPtr->checkMasks+2*(MASK_SIZE))[index] &= ~bit; } /* * Find current max fd. */ if (fd+1 == tsdPtr->numFdBits) { for (tsdPtr->numFdBits = 0; index >= 0; index--) { flags = tsdPtr->checkMasks[index] | (tsdPtr->checkMasks+MASK_SIZE)[index] | (tsdPtr->checkMasks+2*(MASK_SIZE))[index]; if (flags) { for (i = (NBBY*sizeof(fd_mask)); i > 0; i--) { if (flags & (((unsigned long)1) << (i-1))) { break; } } tsdPtr->numFdBits = index * (NBBY*sizeof(fd_mask)) + i; break; } } } /* * Clean up information in the callback record. */ if (prevPtr == NULL) { tsdPtr->firstFileHandlerPtr = filePtr->nextPtr; } else { prevPtr->nextPtr = filePtr->nextPtr; } ckfree((char *) filePtr);}/* *---------------------------------------------------------------------- * * FileHandlerEventProc -- * * This procedure is called by Tcl_ServiceEvent when a file event * reaches the front of the event queue. This procedure is * responsible for actually handling the event by invoking the * callback for the file handler. * * Results: * Returns 1 if the event was handled, meaning it should be removed * from the queue. Returns 0 if the event was not handled, meaning * it should stay on the queue. The only time the event isn't * handled is if the TCL_FILE_EVENTS flag bit isn't set. * * Side effects: * Whatever the file handler's callback procedure does. * *---------------------------------------------------------------------- */static intFileHandlerEventProc(evPtr, flags) Tcl_Event *evPtr; /* Event to service. */ int flags; /* Flags that indicate what events to * handle, such as TCL_FILE_EVENTS. */{ int mask; FileHandler *filePtr; FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr; ThreadSpecificData *tsdPtr; if (!(flags & TCL_FILE_EVENTS)) { return 0; } /* * Search through the file handlers to find the one whose handle matches * the event. We do this rather than keeping a pointer to the file * handler directly in the event, so that the handler can be deleted * while the event is queued without leaving a dangling pointer. */ tsdPtr = TCL_TSD_INIT(&dataKey); for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; filePtr = filePtr->nextPtr) { if (filePtr->fd != fileEvPtr->fd) { continue; } /* * The code is tricky for two reasons: * 1. The file handler's desired events could have changed * since the time when the event was queued, so AND the * ready mask with the desired mask. * 2. The file could have been closed and re-opened since * the time when the event was queued. This is why the * ready mask is stored in the file handler rather than * the queued event: it will be zeroed when a new * file handler is created for the newly opened file. */ mask = filePtr->readyMask & filePtr->mask; filePtr->readyMask = 0; if (mask != 0) { (*filePtr->proc)(filePtr->clientData, mask); } break; } return 1;}/* *---------------------------------------------------------------------- * * Tcl_WaitForEvent -- * * This function is called by Tcl_DoOneEvent to wait for new * events on the message queue. If the block time is 0, then * Tcl_WaitForEvent just polls without blocking. * * Results: * Returns -1 if the select would block forever, otherwise * returns 0. * * Side effects: * Queues file events that are detected by the select. * *---------------------------------------------------------------------- */intTcl_WaitForEvent(timePtr) Tcl_Time *timePtr; /* Maximum block time, or NULL. */{ FileHandler *filePtr; FileHandlerEvent *fileEvPtr; struct timeval timeout, *timeoutPtr; int bit, index, mask;#ifdef TCL_THREADS int waitForFiles;#else int numFound;#endif ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); if (tclStubs.tcl_WaitForEvent != Tcl_WaitForEvent) { return tclStubs.tcl_WaitForEvent(timePtr); } /* * Set up the timeout structure. Note that if there are no events to * check for, we return with a negative result rather than blocking * forever. */ if (timePtr) { timeout.tv_sec = timePtr->sec; timeout.tv_usec = timePtr->usec; timeoutPtr = &timeout;#ifndef TCL_THREADS } else if (tsdPtr->numFdBits == 0) { /* * If there are no threads, no timeout, and no fds registered, * then there are no events possible and we must avoid deadlock. * Note that this is not entirely correct because there might * be a signal that could interrupt the select call, but we * don't handle that case if we aren't using threads. */ return -1;#endif } else { timeoutPtr = NULL; }#ifdef TCL_THREADS /* * Place this thread on the list of interested threads, signal the * notifier thread, and wait for a response or a timeout. */ Tcl_MutexLock(¬ifierMutex); waitForFiles = (tsdPtr->numFdBits > 0); if (timePtr != NULL && timePtr->sec == 0 && timePtr->usec == 0) { /* * Cannot emulate a polling select with a polling condition variable. * Instead, pretend to wait for files and tell the notifier * thread what we are doing. The notifier thread makes sure * it goes through select with its select mask in the same state * as ours currently is. We block until that happens. */ waitForFiles = 1; tsdPtr->pollState = POLL_WANT; timePtr = NULL; } else { tsdPtr->pollState = 0; } if (waitForFiles) { /* * Add the ThreadSpecificData structure of this thread to the list * of ThreadSpecificData structures of all threads that are waiting * on file events. */ tsdPtr->nextPtr = waitingListPtr; if (waitingListPtr) { waitingListPtr->prevPtr = tsdPtr; } tsdPtr->prevPtr = 0; waitingListPtr = tsdPtr; tsdPtr->onList = 1; write(triggerPipe, "", 1); } memset((VOID *) tsdPtr->readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask)); if (!tsdPtr->eventReady) { Tcl_ConditionWait(&tsdPtr->waitCV, ¬ifierMutex, timePtr); } tsdPtr->eventReady = 0; if (waitForFiles && tsdPtr->onList) { /* * Remove the ThreadSpecificData structure of this thread from the * waiting list. Alert the notifier thread to recompute its select * masks - skipping this caused a hang when trying to close a pipe * which the notifier thread was still doing a select on. */ if (tsdPtr->prevPtr) { tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; } else { waitingListPtr = tsdPtr->nextPtr; } if (tsdPtr->nextPtr) { tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; } tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; tsdPtr->onList = 0; write(triggerPipe, "", 1); } #else memcpy((VOID *) tsdPtr->readyMasks, (VOID *) tsdPtr->checkMasks, 3*MASK_SIZE*sizeof(fd_mask)); numFound = select(tsdPtr->numFdBits, (SELECT_MASK *) &tsdPtr->readyMasks[0], (SELECT_MASK *) &tsdPtr->readyMasks[MASK_SIZE], (SELECT_MASK *) &tsdPtr->readyMasks[2*MASK_SIZE], timeoutPtr); /* * Some systems don't clear the masks after an error, so * we have to do it here. */ if (numFound == -1) { memset((VOID *) tsdPtr->readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask)); }#endif /* * Queue all detected file events before returning. */ for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL); filePtr = filePtr->nextPtr) { index = filePtr->fd / (NBBY*sizeof(fd_mask)); bit = 1 << (filePtr->fd % (NBBY*sizeof(fd_mask))); mask = 0; if (tsdPtr->readyMasks[index] & bit) { mask |= TCL_READABLE; } if ((tsdPtr->readyMasks+MASK_SIZE)[index] & bit) { mask |= TCL_WRITABLE; } if ((tsdPtr->readyMasks+2*(MASK_SIZE))[index] & bit) { mask |= TCL_EXCEPTION; } if (!mask) { continue; } /* * Don't bother to queue an event if the mask was previously * non-zero since an event must still be on the queue. */ if (filePtr->readyMask == 0) { fileEvPtr = (FileHandlerEvent *) ckalloc( sizeof(FileHandlerEvent)); fileEvPtr->header.proc = FileHandlerEventProc; fileEvPtr->fd = filePtr->fd; Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL); } filePtr->readyMask = mask; }#ifdef TCL_THREADS Tcl_MutexUnlock(¬ifierMutex);#endif return 0;}#ifdef TCL_THREADS/* *---------------------------------------------------------------------- * * NotifierThreadProc -- * * This routine is the initial (and only) function executed by the * special notifier thread. Its job is to wait for file descriptors * to become readable or writable or to have an exception condition * and then to notify other threads who are interested in this * information by signalling a condition variable. Other threads * can signal this notifier thread of a change in their interests * by writing a single byte to a special pipe that the notifier * thread is monitoring. * * Result: * None. Once started, this routine never exits. It dies with * the overall process. * * Side effects: * The trigger pipe used to signal the notifier thread is created * when the notifier thread first starts. * *---------------------------------------------------------------------- */static voidNotifierThreadProc(clientData) ClientData clientData; /* Not used. */{ ThreadSpecificData *tsdPtr; fd_mask masks[3*MASK_SIZE]; long *maskPtr = (long *)masks; /* masks[] cast to type long[] */ int fds[2]; int i, status, index, bit, numFdBits, found, receivePipe, word; struct timeval poll = {0., 0.}, *timePtr; int maskSize = 3 * ((MASK_SIZE) / sizeof(long)) * sizeof(fd_mask); char buf[2]; if (pipe(fds) != 0) { panic("NotifierThreadProc: could not create trigger pipe."); } receivePipe = fds[0];#ifndef USE_FIONBIO status = fcntl(receivePipe, F_GETFL); status |= O_NONBLOCK; if (fcntl(receivePipe, F_SETFL, status) < 0) { panic("NotifierThreadProc: could not make receive pipe non blocking."); } status = fcntl(fds[1], F_GETFL); status |= O_NONBLOCK; if (fcntl(fds[1], F_SETFL, status) < 0) { panic("NotifierThreadProc: could not make trigger pipe non blocking."); }#else if (ioctl(receivePipe, (int) FIONBIO, &status) < 0) { panic("NotifierThreadProc: could not make receive pipe non blocking."); } if (ioctl(fds[1], (int) FIONBIO, &status) < 0) { panic("NotifierThreadProc: could not make trigger pipe non blocking."); }#endif /* * Install the write end of the pipe into the global variable. */ Tcl_MutexLock(¬ifierMutex); triggerPipe = fds[1]; /* * Signal any threads that are waiting. */ Tcl_ConditionNotify(¬ifierCV); Tcl_MutexUnlock(¬ifierMutex); /* * Look for file events and report them to interested threads. */ while (1) { /* * Set up the select mask to include the receive pipe. */ memset((VOID *)masks, 0, 3*MASK_SIZE*sizeof(fd_mask)); numFdBits = receivePipe + 1; index = receivePipe / (NBBY*sizeof(fd_mask)); bit = 1 << (receivePipe % (NBBY*sizeof(fd_mask))); masks[index] |= bit; /* * Add in the check masks from all of the waiting notifiers. */ Tcl_MutexLock(¬ifierMutex); timePtr = NULL; for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { for (i = 0; i < maskSize; i++) { maskPtr[i] |= ((long*)tsdPtr->checkMasks)[i]; } if (tsdPtr->numFdBits > numFdBits) { numFdBits = tsdPtr->numFdBits; } if (tsdPtr->pollState & POLL_WANT) { /* * Here we make sure we go through select() with the same * mask bits that were present when the thread tried to poll. */ tsdPtr->pollState |= POLL_DONE; timePtr = &poll; } } Tcl_MutexUnlock(¬ifierMutex); maskSize = 3 * ((MASK_SIZE) / sizeof(long)) * sizeof(fd_mask); if (select(numFdBits, (SELECT_MASK *) &masks[0], (SELECT_MASK *) &masks[MASK_SIZE], (SELECT_MASK *) &masks[2*MASK_SIZE], timePtr) == -1) { /* * Try again immediately on an error. */ continue; } /* * Alert any threads that are waiting on a ready file descriptor. */ Tcl_MutexLock(¬ifierMutex); for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { found = 0; for (i = 0; i < maskSize; i++) { word = maskPtr[i] & ((long*)tsdPtr->checkMasks)[i]; found |= word; (((long*)(tsdPtr->readyMasks))[i]) = word; } if (found || (tsdPtr->pollState & POLL_DONE)) { tsdPtr->eventReady = 1; if (tsdPtr->onList) { /* * Remove the ThreadSpecificData structure of this * thread from the waiting list. This prevents us from * continuously spining on select until the other * threads runs and services the file event. */ if (tsdPtr->prevPtr) { tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; } else { waitingListPtr = tsdPtr->nextPtr; } if (tsdPtr->nextPtr) { tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; } tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; tsdPtr->onList = 0; tsdPtr->pollState = 0; } Tcl_ConditionNotify(&tsdPtr->waitCV); } } Tcl_MutexUnlock(¬ifierMutex); /* * Consume the next byte from the notifier pipe if the pipe was * readable. Note that there may be multiple bytes pending, but * to avoid a race condition we only read one at a time. */ if (masks[index] & bit) { i = read(receivePipe, buf, 1); if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) { /* * Someone closed the write end of the pipe or sent us a * Quit message [Bug: 4139] and then closed the write end * of the pipe so we need to shut down the notifier thread. */ break; } } } /* * Clean up the read end of the pipe and signal any threads waiting on * termination of the notifier thread. */ close(receivePipe); Tcl_MutexLock(¬ifierMutex); triggerPipe = -1; Tcl_ConditionNotify(¬ifierCV); Tcl_MutexUnlock(¬ifierMutex);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -