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

📄 tclunixnotfy.c

📁 tcl是工具命令语言
💻 C
📖 第 1 页 / 共 2 页
字号:
    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(&notifierMutex);    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, &notifierMutex, 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(&notifierMutex);#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(&notifierMutex);    triggerPipe = fds[1];    /*     * Signal any threads that are waiting.     */    Tcl_ConditionNotify(&notifierCV);    Tcl_MutexUnlock(&notifierMutex);    /*     * 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(&notifierMutex);	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(&notifierMutex);	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(&notifierMutex);        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(&notifierMutex);		/*	 * 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(&notifierMutex);    triggerPipe = -1;    Tcl_ConditionNotify(&notifierCV);    Tcl_MutexUnlock(&notifierMutex);}#endif

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -