//-------------------------------------------------------------------------------------------------- static void DeleteFdMonitor ( FdMonitor_t* fdMonitorPtr ///< [in] Pointer to the FD Monitor to be deleted. ) //-------------------------------------------------------------------------------------------------- { event_PerThreadRec_t* perThreadRecPtr = thread_GetEventRecPtr(); LE_ASSERT(perThreadRecPtr == fdMonitorPtr->threadRecPtr); // Remove the FD Monitor from the thread's FD Monitor List. le_dls_Remove(&perThreadRecPtr->fdMonitorList, &fdMonitorPtr->link); LOCK // Delete the Safe References used for the FD Monitor and any of its Handler objects. le_ref_DeleteRef(FdMonitorRefMap, fdMonitorPtr->safeRef); UNLOCK // Tell epoll(7) to stop monitoring this fd. StopMonitoringFd(fdMonitorPtr); // Release the object back to it's pool. le_mem_Release(fdMonitorPtr); }
//-------------------------------------------------------------------------------------------------- void le_fdMonitor_Enable ( le_fdMonitor_Ref_t monitorRef, ///< [in] Reference to the File Descriptor Monitor object. short events ///< [in] Bit map of events. ) //-------------------------------------------------------------------------------------------------- { // Look up the File Descriptor Monitor object using the safe reference provided. // Note that the safe reference map is shared by all threads in the process, so it // must be protected using the mutex. The File Descriptor Monitor objects, on the other // hand, are only allowed to be accessed by the one thread that created them, so it is // safe to unlock the mutex after doing the safe reference lookup. LOCK FdMonitor_t* monitorPtr = le_ref_Lookup(FdMonitorRefMap, monitorRef); UNLOCK LE_FATAL_IF(monitorPtr == NULL, "File Descriptor Monitor %p doesn't exist!", monitorRef); LE_FATAL_IF(thread_GetEventRecPtr() != monitorPtr->threadRecPtr, "FD Monitor '%s' (fd %d) is owned by another thread.", monitorPtr->name, monitorPtr->fd); short filteredEvents = events & (POLLIN | POLLOUT | POLLPRI); if (filteredEvents != events) { char textBuff[64]; LE_WARN("Attempt to enable events that can't be disabled (%s).", GetPollEventsText(textBuff, sizeof(textBuff), events & ~filteredEvents)); } uint32_t epollEvents = PollToEPoll(filteredEvents); // If the fd doesn't support epoll, we assume it is always ready for read and write. // As long as EPOLLIN or EPOLLOUT (or both) is enabled for one of these fds, DispatchToHandler() // keeps re-queueing itself to the thread's event queue. But it will stop doing that if // EPOLLIN and EPOLLOUT are both disabled. So, here is where we get things going again when // EPOLLIN or EPOLLOUT is enabled outside DispatchToHandler() for that fd. if ( (monitorPtr->isAlwaysReady) && (epollEvents & (EPOLLIN | EPOLLOUT)) && ((monitorPtr->epollEvents & (EPOLLIN | EPOLLOUT)) == 0) ) { // Fetch the pointer to the FD Monitor from thread-specific data. // This will be NULL if we are not inside an FD Monitor handler. FdMonitor_t* handlerMonitorPtr = pthread_getspecific(FDMonitorPtrKey); // If no handler is running or some other fd's handler is running, if ((handlerMonitorPtr == NULL) || (handlerMonitorPtr->safeRef == monitorRef)) { // Queue up DispatchToHandler() for this fd. fdMon_Report(monitorRef, epollEvents & (EPOLLIN | EPOLLOUT)); } } // Bit-wise OR the newly enabled event flags into the FD Monitor's epoll(7) flags set. monitorPtr->epollEvents |= epollEvents; UpdateEpollFd(monitorPtr); }
//-------------------------------------------------------------------------------------------------- void le_fdMonitor_SetDeferrable ( le_fdMonitor_Ref_t monitorRef, ///< [in] Reference to the File Descriptor Monitor object. bool isDeferrable ///< [in] true (deferrable) or false (urgent). ) //-------------------------------------------------------------------------------------------------- { // Look up the File Descriptor Monitor object using the safe reference provided. // Note that the safe reference map is shared by all threads in the process, so it // must be protected using the mutex. The File Descriptor Monitor objects, on the other // hand, are only allowed to be accessed by the one thread that created them, so it is // safe to unlock the mutex after doing the safe reference lookup. LOCK FdMonitor_t* monitorPtr = le_ref_Lookup(FdMonitorRefMap, monitorRef); UNLOCK LE_FATAL_IF(monitorPtr == NULL, "File Descriptor Monitor %p doesn't exist!", monitorRef); LE_FATAL_IF(thread_GetEventRecPtr() != monitorPtr->threadRecPtr, "FD Monitor '%s' (fd %d) is owned by another thread.", monitorPtr->name, monitorPtr->fd); // Set/clear the EPOLLWAKEUP flag in the FD Monitor's epoll(7) flags set. if (isDeferrable) { monitorPtr->epollEvents &= ~EPOLLWAKEUP; } else { monitorPtr->epollEvents |= EPOLLWAKEUP; } UpdateEpollFd(monitorPtr); }
//-------------------------------------------------------------------------------------------------- static void DeleteFdMonitor ( FdMonitor_t* fdMonitorPtr ///< [in] Pointer to the FD Monitor to be deleted. ) //-------------------------------------------------------------------------------------------------- { int i; event_PerThreadRec_t* perThreadRecPtr = thread_GetEventRecPtr(); LE_ASSERT(perThreadRecPtr == fdMonitorPtr->threadRecPtr); // Remove the FD Monitor from the thread's FD Monitor List. le_dls_Remove(&perThreadRecPtr->fdMonitorList, &fdMonitorPtr->link); LOCK // Delete the Safe References used for the FD Monitor and any of its Handler objects. le_ref_DeleteRef(FdMonitorRefMap, fdMonitorPtr->safeRef); for (i = 0; i < LE_EVENT_NUM_FD_EVENT_TYPES; i++) { void* safeRef = fdMonitorPtr->handlerArray[i].safeRef; if (safeRef != NULL) { le_ref_DeleteRef(HandlerRefMap, safeRef); } } UNLOCK // Tell epoll(7) to stop monitoring this fd. StopMonitoringFd(fdMonitorPtr); // Release the object back to it's pool. le_mem_Release(fdMonitorPtr); }
//-------------------------------------------------------------------------------------------------- int le_event_GetFd ( void ) { return CONTAINER_OF(thread_GetEventRecPtr(), event_LinuxPerThreadRec_t, portablePerThreadRec)->epollFd; }
//-------------------------------------------------------------------------------------------------- le_event_FdMonitorRef_t le_event_CreateFdMonitor ( const char* name, ///< [in] Name of the object (for diagnostics). int fd ///< [in] File descriptor to be monitored for events. ) //-------------------------------------------------------------------------------------------------- { // Get a pointer to the thread-specific event loop data record. event_PerThreadRec_t* perThreadRecPtr = thread_GetEventRecPtr(); // Allocate the object. FdMonitor_t* fdMonitorPtr = le_mem_ForceAlloc(FdMonitorPool); // Initialize the object. fdMonitorPtr->link = LE_DLS_LINK_INIT; fdMonitorPtr->fd = fd; fdMonitorPtr->threadRecPtr = perThreadRecPtr; memset(fdMonitorPtr->handlerArray, 0, sizeof(fdMonitorPtr->handlerArray)); // To start with, no events are in the set to be monitored. They will be added as handlers // are registered for them. (Although, EPOLLHUP and EPOLLERR will always be monitored // regardless of what flags we specify). We use epoll in "level-triggered mode". fdMonitorPtr->epollEvents = 0; // Assume that the event should wake up the system; can be changed later. fdMonitorPtr->wakeUp = true; // Copy the name into it. if (le_utf8_Copy(fdMonitorPtr->name, name, sizeof(fdMonitorPtr->name), NULL) == LE_OVERFLOW) { LE_WARN("FD Monitor object name '%s' truncated to '%s'.", name, fdMonitorPtr->name); } LOCK // Create a safe reference for the object. fdMonitorPtr->safeRef = le_ref_CreateRef(FdMonitorRefMap, fdMonitorPtr); // Add it to the thread's FD Monitor list. le_dls_Queue(&perThreadRecPtr->fdMonitorList, &fdMonitorPtr->link); // Tell epoll(7) to start monitoring this fd. struct epoll_event ev; ev.events = fdMonitorPtr->epollEvents; ev.data.ptr = fdMonitorPtr->safeRef; if (epoll_ctl(perThreadRecPtr->epollFd, EPOLL_CTL_ADD, fd, &ev) == -1) { LE_FATAL("epoll_ctl(ADD) failed for fd %d. errno = %d (%m)", fd, errno); } UNLOCK return fdMonitorPtr->safeRef; }
//-------------------------------------------------------------------------------------------------- static void DispatchToHandler ( void* param1Ptr, ///< FD Monitor safe reference. void* param2Ptr ///< FD event type. ) //-------------------------------------------------------------------------------------------------- { le_event_FdEventType_t eventType = (le_event_FdEventType_t)param2Ptr; event_PerThreadRec_t* perThreadRecPtr = thread_GetEventRecPtr(); 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) { LE_ASSERT(perThreadRecPtr == fdMonitorPtr->threadRecPtr); Handler_t* handlerPtr = &(fdMonitorPtr->handlerArray[eventType]); if (handlerPtr->handlerFunc != NULL) { // Set the thread's Context Pointer. event_SetCurrentContextPtr(handlerPtr->contextPtr); // Call the handler function. handlerPtr->handlerFunc(fdMonitorPtr->fd); } else { TRACE("Discarding event %s for FD Monitor %s (fd %d).", GetFdEventTypeName(eventType), fdMonitorPtr->name, fdMonitorPtr->fd); // If this is a write hang-up, then we need to tell epoll to stop monitoring // this fd, because otherwise we could end up wasting power and spamming the // log with debug messages while we detect and discard this event over and over. if (eventType == LE_EVENT_FD_WRITE_HANG_UP) { StopMonitoringFd(fdMonitorPtr); } } } else { TRACE("Discarding event %s for non-existent FD Monitor.", GetFdEventTypeName(eventType)); } }
//-------------------------------------------------------------------------------------------------- le_event_FdHandlerRef_t le_event_SetFdHandler ( le_event_FdMonitorRef_t monitorRef, ///< [in] Reference to the File Descriptor Monitor object. le_event_FdEventType_t eventType, ///< [in] The type of event to be reported to this handler. le_event_FdHandlerFunc_t handlerFunc ///< [in] The handler function. ) //-------------------------------------------------------------------------------------------------- { LE_ASSERT(handlerFunc != NULL); // Look up the File Descriptor Monitor object using the safe reference provided. // Note that the safe reference map is shared by all threads in the process, so it // must be protected using the mutex. The File Descriptor Monitor objects, on the other // hand, are only allowed to be accessed by the one thread that created them, so it is // safe to unlock the mutex after doing the safe reference lookup. LOCK FdMonitor_t* monitorPtr = le_ref_Lookup(FdMonitorRefMap, monitorRef); UNLOCK LE_FATAL_IF(monitorPtr == NULL, "File Descriptor Monitor %p doesn't exist!", monitorRef); LE_FATAL_IF(thread_GetEventRecPtr() != monitorPtr->threadRecPtr, "FD Monitor '%s' (fd %d) is owned by another thread.", monitorPtr->name, monitorPtr->fd); // Get a pointer to the Handler object in the appropriate spot for this type of event in the // FD Monitor's array of handlers. Handler_t* handlerPtr = &(monitorPtr->handlerArray[eventType]); // Double check that no one has tried setting this handler yet. LE_FATAL_IF(handlerPtr->handlerFunc != NULL, "FD handler already set for event '%s' on FD Monitor '%s' (fd %d).", GetFdEventTypeName(eventType), monitorPtr->name, monitorPtr->fd); // Initialize the Handler object. handlerPtr->handlerFunc = handlerFunc; handlerPtr->contextPtr = NULL; handlerPtr->monitorPtr = monitorPtr; LOCK handlerPtr->safeRef = le_ref_CreateRef(HandlerRefMap, handlerPtr); UNLOCK // Enable the monitoring of this event. EnableFdMonitoring(monitorPtr, eventType); return handlerPtr->safeRef; }
//-------------------------------------------------------------------------------------------------- void le_event_ClearFdHandlerByEventType ( le_event_FdMonitorRef_t monitorRef, ///< [in] Reference to the File Descriptor Monitor object. le_event_FdEventType_t eventType ///< [in] The type of event to clear the handler for. ) //-------------------------------------------------------------------------------------------------- { LE_ASSERT(eventType < LE_EVENT_NUM_FD_EVENT_TYPES); // Look up the File Descriptor Monitor object using the safe reference provided. // Note that the safe reference map is shared by all threads in the process, so it // must be protected using the mutex. The File Descriptor Monitor objects, on the other // hand, are only allowed to be accessed by the one thread that created them, so it is // safe to unlock the mutex after doing the safe reference lookup. LOCK FdMonitor_t* monitorPtr = le_ref_Lookup(FdMonitorRefMap, monitorRef); UNLOCK LE_FATAL_IF(monitorPtr == NULL, "File Descriptor Monitor %p doesn't exist!", monitorRef); LE_FATAL_IF(thread_GetEventRecPtr() != monitorPtr->threadRecPtr, "FD Monitor '%s' (fd %d) is owned by another thread.", monitorPtr->name, monitorPtr->fd); // Get a pointer to the Handler object in the appropriate spot for this type of event in the // FD Monitor's array of handlers. Handler_t* handlerPtr = &(monitorPtr->handlerArray[eventType]); LE_CRIT_IF(handlerPtr->handlerFunc == NULL, "Handler cleared when not set for FD Monitor '%s' (fd %d), event type %d.", monitorPtr->name, monitorPtr->fd, eventType); // Clear the Handler object. handlerPtr->handlerFunc = NULL; handlerPtr->contextPtr = NULL; LOCK le_ref_DeleteRef(HandlerRefMap, handlerPtr->safeRef); UNLOCK handlerPtr->safeRef = NULL; // Disable the monitoring of this event. DisableFdMonitoring(monitorPtr, eventType); }
//-------------------------------------------------------------------------------------------------- void le_fdMonitor_Delete ( le_fdMonitor_Ref_t monitorRef ///< [in] Reference to the File Descriptor Monitor object. ) //-------------------------------------------------------------------------------------------------- { LOCK FdMonitor_t* monitorPtr = le_ref_Lookup(FdMonitorRefMap, monitorRef); UNLOCK LE_FATAL_IF(monitorPtr == NULL, "FD Monitor object %p does not exist!", monitorRef); LE_FATAL_IF(monitorPtr->threadRecPtr != thread_GetEventRecPtr(), "FD Monitor '%s' (%d) is owned by another thread.", monitorPtr->name, monitorPtr->fd); DeleteFdMonitor(monitorPtr); }
//-------------------------------------------------------------------------------------------------- void le_event_ClearFdHandler ( le_event_FdHandlerRef_t handlerRef ///< [in] Reference to the handler. ) //-------------------------------------------------------------------------------------------------- { // Look up the Handler object using the safe reference provided. // Note that the safe reference map is shared by all threads in the process, so it // must be protected using the mutex. The Handler objects, on the other // hand, are only allowed to be accessed by the one thread that created them, so it is // safe to unlock the mutex after doing the safe reference lookup. LOCK Handler_t* handlerPtr = le_ref_Lookup(HandlerRefMap, handlerRef); UNLOCK LE_FATAL_IF(handlerPtr == NULL, "FD event handler %p doesn't exist!", handlerRef); FdMonitor_t* monitorPtr = handlerPtr->monitorPtr; LE_FATAL_IF(thread_GetEventRecPtr() != monitorPtr->threadRecPtr, "FD Monitor '%s' (fd %d) is owned by another thread.", monitorPtr->name, monitorPtr->fd); LE_ASSERT(handlerPtr->handlerFunc != NULL); le_event_FdEventType_t eventType = INDEX_OF_ARRAY_MEMBER(monitorPtr->handlerArray, handlerPtr); LE_ASSERT(eventType < LE_EVENT_NUM_FD_EVENT_TYPES); // Clear the Handler object. handlerPtr->handlerFunc = NULL; handlerPtr->contextPtr = NULL; LOCK le_ref_DeleteRef(HandlerRefMap, handlerPtr->safeRef); UNLOCK handlerPtr->safeRef = NULL; // Disable the monitoring of this event. DisableFdMonitoring(monitorPtr, eventType); }
//-------------------------------------------------------------------------------------------------- void le_fdMonitor_SetContextPtr ( le_fdMonitor_Ref_t monitorRef, ///< [in] Reference to the File Descriptor Monitor. void* contextPtr ///< [in] Opaque context pointer value. ) //-------------------------------------------------------------------------------------------------- { // Look up the File Descriptor Monitor object using the safe reference provided. // Note that the safe reference map is shared by all threads in the process, so it // must be protected using the mutex. The File Descriptor Monitor objects, on the other // hand, are only allowed to be accessed by the one thread that created them, so it is // safe to unlock the mutex after doing the safe reference lookup. LOCK FdMonitor_t* monitorPtr = le_ref_Lookup(FdMonitorRefMap, monitorRef); UNLOCK LE_FATAL_IF(monitorPtr == NULL, "File Descriptor Monitor %p doesn't exist!", monitorRef); LE_FATAL_IF(thread_GetEventRecPtr() != monitorPtr->threadRecPtr, "FD Monitor '%s' (fd %d) is owned by another thread.", monitorPtr->name, monitorPtr->fd); monitorPtr->contextPtr = contextPtr; }
//-------------------------------------------------------------------------------------------------- void le_event_WakeUp ( le_event_FdMonitorRef_t monitorRef, ///< [in] Reference to the File Descriptor Monitor object. bool wakeUp ///< [in] Optional EPOLLWAKEUP flag for epoll_wait(). ) //-------------------------------------------------------------------------------------------------- { // Look up the File Descriptor Monitor object using the safe reference provided. // Note that the safe reference map is shared by all threads in the process, so it // must be protected using the mutex. The File Descriptor Monitor objects, on the other // hand, are only allowed to be accessed by the one thread that created them, so it is // safe to unlock the mutex after doing the safe reference lookup. LOCK FdMonitor_t* monitorPtr = le_ref_Lookup(FdMonitorRefMap, monitorRef); UNLOCK LE_FATAL_IF(monitorPtr == NULL, "File Descriptor Monitor %p doesn't exist!", monitorRef); LE_FATAL_IF(thread_GetEventRecPtr() != monitorPtr->threadRecPtr, "FD Monitor '%s' (fd %d) is owned by another thread.", monitorPtr->name, monitorPtr->fd); monitorPtr->wakeUp = wakeUp; }
//-------------------------------------------------------------------------------------------------- void le_fdMonitor_Disable ( le_fdMonitor_Ref_t monitorRef, ///< [in] Reference to the File Descriptor Monitor object. short events ///< [in] Bit map of events. ) //-------------------------------------------------------------------------------------------------- { // Look up the File Descriptor Monitor object using the safe reference provided. // Note that the safe reference map is shared by all threads in the process, so it // must be protected using the mutex. The File Descriptor Monitor objects, on the other // hand, are only allowed to be accessed by the one thread that created them, so it is // safe to unlock the mutex after doing the safe reference lookup. LOCK FdMonitor_t* monitorPtr = le_ref_Lookup(FdMonitorRefMap, monitorRef); UNLOCK LE_FATAL_IF(monitorPtr == NULL, "File Descriptor Monitor %p doesn't exist!", monitorRef); LE_FATAL_IF(thread_GetEventRecPtr() != monitorPtr->threadRecPtr, "FD Monitor '%s' (fd %d) is owned by another thread.", monitorPtr->name, monitorPtr->fd); short filteredEvents = events & (POLLIN | POLLOUT | POLLPRI); LE_WARN_IF(filteredEvents != events, "Only POLLIN, POLLOUT, and POLLPRI events can be disabled. (fd monitor '%s')", monitorPtr->name); // Convert the events from POLLxx events to EPOLLxx events. uint32_t epollEvents = PollToEPoll(filteredEvents); // Remove them from the FD Monitor's epoll(7) flags set. monitorPtr->epollEvents &= (~epollEvents); UpdateEpollFd(monitorPtr); }
//-------------------------------------------------------------------------------------------------- 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; } }
//-------------------------------------------------------------------------------------------------- le_fdMonitor_Ref_t le_fdMonitor_Create ( const char* name, ///< [in] Name of the object (for diagnostics). int fd, ///< [in] File descriptor to be monitored for events. le_fdMonitor_HandlerFunc_t handlerFunc, ///< [in] Handler function. short events ///< [in] Initial set of events to be monitored. ) //-------------------------------------------------------------------------------------------------- { // Get a pointer to the thread-specific event loop data record. event_PerThreadRec_t* perThreadRecPtr = thread_GetEventRecPtr(); // Allocate the object. FdMonitor_t* fdMonitorPtr = le_mem_ForceAlloc(FdMonitorPool); // Initialize the object. fdMonitorPtr->link = LE_DLS_LINK_INIT; fdMonitorPtr->fd = fd; fdMonitorPtr->epollEvents = PollToEPoll(events) | EPOLLWAKEUP; // Non-deferrable by default. fdMonitorPtr->isAlwaysReady = false; fdMonitorPtr->threadRecPtr = perThreadRecPtr; fdMonitorPtr->handlerFunc = handlerFunc; fdMonitorPtr->contextPtr = NULL; // Copy the name into it. if (le_utf8_Copy(fdMonitorPtr->name, name, sizeof(fdMonitorPtr->name), NULL) == LE_OVERFLOW) { LE_WARN("FD Monitor object name '%s' truncated to '%s'.", name, fdMonitorPtr->name); } LOCK // Create a safe reference for the object. fdMonitorPtr->safeRef = le_ref_CreateRef(FdMonitorRefMap, fdMonitorPtr); // Add it to the thread's FD Monitor list. le_dls_Queue(&perThreadRecPtr->fdMonitorList, &fdMonitorPtr->link); // Tell epoll(7) to start monitoring this fd. struct epoll_event ev; memset(&ev, 0, sizeof(ev)); ev.events = fdMonitorPtr->epollEvents; ev.data.ptr = fdMonitorPtr->safeRef; if (epoll_ctl(perThreadRecPtr->epollFd, EPOLL_CTL_ADD, fd, &ev) == -1) { if (errno == EPERM) { LE_DEBUG("fd %d doesn't support epoll(), assuming always readable and writeable.", fd); fdMonitorPtr->isAlwaysReady = true; // If either EPOLLIN or EPOLLOUT are enabled, queue up the handler for this now. uint32_t epollEvents = fdMonitorPtr->epollEvents & (EPOLLIN | EPOLLOUT); if (epollEvents != 0) { fdMon_Report(fdMonitorPtr->safeRef, epollEvents); } } else { LE_FATAL("epoll_ctl(ADD) failed for fd %d. errno = %d (%m)", fd, errno); } } UNLOCK return fdMonitorPtr->safeRef; }
//-------------------------------------------------------------------------------------------------- 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!"); } } }