//-------------------------------------------------------------------------------------------------- le_result_t le_event_ServiceLoop ( void ) { event_PerThreadRec_t* perThreadRecPtr = thread_GetEventRecPtr(); int epollFd = CONTAINER_OF(perThreadRecPtr, event_LinuxPerThreadRec_t, portablePerThreadRec)->epollFd; struct epoll_event epollEventList[MAX_EPOLL_EVENTS]; LE_DEBUG("perThreadRecPtr->liveEventCount is" "%" PRIu64, perThreadRecPtr->liveEventCount); // If there are still live events remaining in the queue, process a single event, then return if (perThreadRecPtr->liveEventCount > 0) { perThreadRecPtr->liveEventCount--; // This function assumes the mutex is NOT locked. event_ProcessOneEventReport(perThreadRecPtr); return LE_OK; } int result; do { // If no events on the queue, try to refill the event queue. // Ask epoll what, if anything, has happened on any of the file descriptors that we are // monitoring using our epoll fd. (NOTE: This is non-blocking.) result = epoll_wait(epollFd, epollEventList, NUM_ARRAY_MEMBERS(epollEventList), 0); if ((result < 0) && (EINTR == errno)) { // If epoll was interrupted, // Check if someone has cancelled the thread and terminate the thread now, if so. pthread_testcancel(); } } while ((result < 0) && (EINTR == errno)); // If something happened on one or more of the monitored file descriptors, if (result > 0) { int i; // Check if someone has cancelled the thread and terminate the thread now, if so. pthread_testcancel(); // For each fd event reported by epoll_wait(), if it is any file descriptor other // than the eventfd (which is used to indicate that there is something on the // Event Queue), queue an Event Report to the Event Queue for that fd. for (i = 0; i < result; i++) { // Get the pointer that we registered with epoll_ctl(2) along with this fd. // The value of this pointer will either be NULL or a Safe Reference for an // FD Monitor object. If it is NULL, then the Event Queue's eventfd is the // fd that experienced the event, which we will deal with later in this function. void* safeRef = epollEventList[i].data.ptr; if (safeRef != NULL) { fdMon_Report(safeRef, EPollToPoll(epollEventList[i].events)); } } } // Otherwise, check if an epoll_wait() reported an error. // Interruptions are tested above, so this is always a fatal error. else if (result < 0) { LE_FATAL("epoll_wait() failed. errno = %d.", errno); } // Otherwise, if epoll_wait() returned zero, then either this function was called without // waiting for the eventfd to be readable, or the eventfd was readable momentarily, but // something changed between the time the application code detected the readable condition // and now that made the eventfd not readable anymore. else { LE_DEBUG("epoll_wait() returned zero."); return LE_WOULD_BLOCK; } // Read the eventfd to reset it to zero so epoll stops telling us about it until more // are added. perThreadRecPtr->liveEventCount = fa_event_WaitForEvent(perThreadRecPtr); LE_DEBUG("perThreadRecPtr->liveEventCount is" "%" PRIu64, perThreadRecPtr->liveEventCount); // If events were read, process the top event if (perThreadRecPtr->liveEventCount > 0) { perThreadRecPtr->liveEventCount--; event_ProcessOneEventReport(perThreadRecPtr); return LE_OK; } else { return LE_WOULD_BLOCK; } }
//-------------------------------------------------------------------------------------------------- static void DispatchToHandler ( void* param1Ptr, ///< FD Monitor safe reference. void* param2Ptr ///< epoll() event flags. ) //-------------------------------------------------------------------------------------------------- { uint32_t epollEventFlags = (uint32_t)(size_t)param2Ptr; LOCK // Get a pointer to the FD Monitor object for this fd. FdMonitor_t* fdMonitorPtr = le_ref_Lookup(FdMonitorRefMap, param1Ptr); UNLOCK // If the FD Monitor object has been deleted, we can just ignore this. if (fdMonitorPtr == NULL) { TRACE("Discarding events for non-existent FD Monitor %p.", param1Ptr); return; } // Sanity check: The FD monitor must belong to the current thread. LE_ASSERT(thread_GetEventRecPtr() == fdMonitorPtr->threadRecPtr); // Mask out any events that have been disabled since epoll_wait() reported these events to us. epollEventFlags &= (fdMonitorPtr->epollEvents | EPOLLERR | EPOLLHUP | EPOLLRDHUP); // If there's nothing left to report to the handler, don't call it. if (epollEventFlags == 0) { // Note: if the fd is always ready to read or write (is not supported by epoll()), then // we will only end up in here if both POLLIN and POLLOUT are disabled, in which case // returning now will prevent re-queuing of DispatchToHandler(), which is what we // want. When either POLLIN or POLLOUT are re-enabled, le_fdMonitor_Enable() will // call fdMon_Report() to get things going again. return; } // Translate the epoll() events into poll() events. short pollEvents = EPollToPoll(epollEventFlags); if (IS_TRACE_ENABLED()) { char eventsTextBuff[128]; TRACE("Calling event handler for FD Monitor %s (fd %d, events %s).", fdMonitorPtr->name, fdMonitorPtr->fd, GetPollEventsText(eventsTextBuff, sizeof(eventsTextBuff), pollEvents)); } // Increment the reference count on the Monitor object in case the handler deletes it. le_mem_AddRef(fdMonitorPtr); // Store a pointer to the FD Monitor as thread-specific data so le_fdMonitor_GetMonitor() // and le_fdMonitor_GetContextPtr() can find it. LE_ASSERT(pthread_setspecific(FDMonitorPtrKey, fdMonitorPtr) == 0); // Set the thread's event loop Context Pointer. event_SetCurrentContextPtr(fdMonitorPtr->contextPtr); // Call the handler function. fdMonitorPtr->handlerFunc(fdMonitorPtr->fd, pollEvents); // Clear the thread-specific pointer to the FD Monitor. LE_ASSERT(pthread_setspecific(FDMonitorPtrKey, NULL) == 0); // If this fd is always ready (is not supported by epoll) and either POLLIN or POLLOUT // are enabled, then queue up another dispatcher for this FD Monitor. // If neither are enabled, then le_fdMonitor_Enable() will queue the dispatcher // when one of them is re-enabled. if ((fdMonitorPtr->isAlwaysReady) && (fdMonitorPtr->epollEvents & (EPOLLIN | EPOLLOUT))) { fdMon_Report(fdMonitorPtr->safeRef, fdMonitorPtr->epollEvents & (EPOLLIN | EPOLLOUT)); } // Release our reference. We don't need the Monitor object anymore. le_mem_Release(fdMonitorPtr); }
//-------------------------------------------------------------------------------------------------- void fa_event_RunLoop ( void ) { event_PerThreadRec_t* perThreadRecPtr = thread_GetEventRecPtr(); int epollFd = CONTAINER_OF(perThreadRecPtr, event_LinuxPerThreadRec_t, portablePerThreadRec)->epollFd; struct epoll_event epollEventList[MAX_EPOLL_EVENTS]; // Make sure nobody calls this function more than once in the same thread. LE_ASSERT(perThreadRecPtr->state == LE_EVENT_LOOP_INITIALIZED); // Update the state of the Event Loop. perThreadRecPtr->state = LE_EVENT_LOOP_RUNNING; // Enter the infinite loop itself. for (;;) { // Wait for something to happen on one of the file descriptors that we are monitoring // using our epoll fd. int result = epoll_wait(epollFd, epollEventList, NUM_ARRAY_MEMBERS(epollEventList), -1); // If something happened on one or more of the monitored file descriptors, if (result > 0) { int i; // Check if someone has cancelled the thread and terminate the thread now, if so. pthread_testcancel(); // For each fd event reported by epoll_wait(), if it is any file descriptor other // than the eventfd (which is used to indicate that there is something on the // Event Queue), queue an Event Report to the Event Queue for that fd. for (i = 0; i < result; i++) { // Get the pointer that we registered with epoll_ctl(2) along with this fd. // The value of this pointer will either be NULL or a Safe Reference for an // FD Monitor object. If it is NULL, then the Event Queue's eventfd is the // fd that experienced the event. void* safeRef = epollEventList[i].data.ptr; if (safeRef != NULL) { fdMon_Report(safeRef, EPollToPoll(epollEventList[i].events)); } } // Process all the Event Reports on the Event Queue. event_ProcessEventReports(perThreadRecPtr); } // Otherwise, if an epoll_wait() reported an error, hopefully it's just an interruption // by a signal (EINTR). Anything else is a fatal error. else if (result < 0) { if (errno != EINTR) { LE_FATAL("epoll_wait() failed. errno = %d.", errno); } // It was just EINTR, so we are okay to go back to sleep. But first, // check if someone has cancelled the thread and terminate the thread now, if so. pthread_testcancel(); } // Otherwise, if epoll_wait() returned zero, something has gone horribly wrong, because // it should never return zero. else { LE_FATAL("epoll_wait() returned zero!"); } } }