bool CEventQueue::getEvent(CEvent& event, double timeout) { CStopwatch timer(true); retry: // if no events are waiting then handle timers and then wait while (m_buffer->isEmpty()) { // handle timers first if (hasTimerExpired(event)) { return true; } // get time remaining in timeout double timeLeft = timeout - timer.getTime(); if (timeout >= 0.0 && timeLeft <= 0.0) { return false; } // get time until next timer expires. if there is a timer // and it'll expire before the client's timeout then use // that duration for our timeout instead. double timerTimeout = getNextTimerTimeout(); if (timeout < 0.0 || (timerTimeout >= 0.0 && timerTimeout < timeLeft)) { timeLeft = timerTimeout; } // wait for an event m_buffer->waitForEvent(timeLeft); } // get the event UInt32 dataID; IEventQueueBuffer::Type type = m_buffer->getEvent(event, dataID); switch (type) { case IEventQueueBuffer::kNone: if (timeout < 0.0 || timeout <= timer.getTime()) { // don't want to fail if client isn't expecting that // so if getEvent() fails with an infinite timeout // then just try getting another event. goto retry; } return false; case IEventQueueBuffer::kSystem: return true; case IEventQueueBuffer::kUser: { CArchMutexLock lock(m_mutex); event = removeEvent(dataID); return true; } default: assert(0 && "invalid event type"); return false; } }
bool EventQueue::isEmpty() const { return (m_buffer->isEmpty() && getNextTimerTimeout() != 0.0); }
bool SchedulerBase::Run() { uint32_t iter=0; TimeSpec timeout; TimeSpec immediate; bool gotEvents; if (!LogVerify(IsMainThread())) return false; m_isStarted = true; // Start with a quick event check. timeout = immediate; while (true) { iter++; if (m_wantsShutdown) break; // // Get event, or timeout. // gLog.Optional(Log::TimerDetail, "checking events (%u)", iter); gotEvents = waitForEvents(timeout); // By default the next event check is immediately. timeout = immediate; // // High priority timers, if any, get handled now // while (!m_wantsShutdown && expireTimer(Timer::Priority::Hi)) { //nothing } if (m_wantsShutdown) break; // // Handle any events. // if (gotEvents) { int socketId; gLog.Optional(Log::TimerDetail, "Handling events (%u)", iter); while (-1 != (socketId = getNextSocketEvent())) { // we have a socket event .. is it a socket or a signal? SocketItemHashMap::iterator foundSocket; SignalItemHashMap::iterator foundSignal; if (m_sockets.end() != (foundSocket = m_sockets.find(socketId))) { if (LogVerify(foundSocket->second.callback != NULL)) foundSocket->second.callback(socketId, foundSocket->second.userdata); } else if (m_signals.end() != (foundSignal = m_signals.find(socketId))) { if (LogVerify(foundSignal->second.callback != NULL)) { // 'Drain' the pipe. char drain[128]; int result; size_t reads = 0; while ( 0 < (result = ::read(socketId, drain, sizeof(drain)))) reads++; if (reads == 0 && result < 0) gLog.LogError("Failed to read from pipe %d: %s", socketId, strerror(errno)); else if (result == 0) gLog.LogError("Signaling pipe write end for %d closed", socketId); foundSignal->second.callback(foundSignal->second.fdWrite, foundSignal->second.userdata); } } else { gLog.Optional(Log::TimerDetail, "Socket (%d) signaled with no handler (%u).", socketId, iter); } if (m_wantsShutdown) break; } if (m_wantsShutdown) break; } // // Handle a low priority timer if there are no events. // TODO: starvation is a potential problem for low priority timers. // if (!gotEvents && !expireTimer(Timer::Priority::Low)) { // No events and no more timers, so we are ready to sleep again. timeout = getNextTimerTimeout(); } if (m_wantsShutdown) break; } // while true return true; }