/*! \internal Behaves as close to POSIX poll(2) as practical but may be implemented using select(2) where necessary. In that case, returns -1 and sets errno to EINVAL if passed any descriptor greater than or equal to FD_SETSIZE. */ int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts) { if (!timeout_ts) { // no timeout -> block forever int ret; EINTR_LOOP(ret, qt_ppoll(fds, nfds, Q_NULLPTR)); return ret; } timespec start = qt_gettime(); timespec timeout = *timeout_ts; // loop and recalculate the timeout as needed forever { const int ret = qt_ppoll(fds, nfds, &timeout); if (ret != -1 || errno != EINTR) return ret; // recalculate the timeout if (!time_update(&timeout, start, *timeout_ts)) { // timeout during update // or clock reset, fake timeout error return 0; } } }
int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept, const struct timeval *orig_timeout) { if (!orig_timeout) { // no timeout -> block forever register int ret; EINTR_LOOP(ret, select(nfds, fdread, fdwrite, fdexcept, 0)); return ret; } timeval start = qt_gettime(); timeval timeout = *orig_timeout; // loop and recalculate the timeout as needed int ret; forever { ret = ::select(nfds, fdread, fdwrite, fdexcept, &timeout); if (ret != -1 || errno != EINTR) return ret; // recalculate the timeout if (!time_update(&timeout, start, *orig_timeout)) { // timeout during update // or clock reset, fake timeout error return 0; } } }
QTimerInfoList::QTimerInfoList() { currentTime = qt_gettime(); #if (_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC) if (!qt_gettime_is_monotonic()) { // not using monotonic timers, initialize the timeChanged() machinery previousTime = currentTime; tms unused; previousTicks = times(&unused); ticksPerSecond = sysconf(_SC_CLK_TCK); msPerTick = 1000/ticksPerSecond; } else { // detected monotonic timers previousTime.tv_sec = previousTime.tv_usec = 0; previousTicks = 0; ticksPerSecond = 0; msPerTick = 0; } #endif firstTimerInfo = currentTimerInfo = 0; }
int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept, const struct timespec *orig_timeout) { if (!orig_timeout) { // no timeout -> block forever int ret; EINTR_LOOP(ret, select(nfds, fdread, fdwrite, fdexcept, 0)); return ret; } timespec start = qt_gettime(); timespec timeout = *orig_timeout; // loop and recalculate the timeout as needed int ret; forever { #ifndef Q_OS_QNX ret = ::pselect(nfds, fdread, fdwrite, fdexcept, &timeout, 0); #else timeval timeoutVal; timeoutVal.tv_sec = timeout.tv_sec; timeoutVal.tv_usec = timeout.tv_nsec / 1000; ret = ::select(nfds, fdread, fdwrite, fdexcept, &timeoutVal); #endif if (ret != -1 || errno != EINTR) return ret; // recalculate the timeout if (!time_update(&timeout, start, *orig_timeout)) { // timeout during update // or clock reset, fake timeout error return 0; } } }
static inline bool time_update(struct timespec *tv, const struct timespec &start, const struct timespec &timeout) { // clock source is (hopefully) monotonic, so we can recalculate how much timeout is left; // if it isn't monotonic, we'll simply hope that it hasn't jumped, because we have no alternative struct timespec now = qt_gettime(); *tv = timeout + start - now; return tv->tv_sec >= 0; }
QT_BEGIN_NAMESPACE static inline bool time_update(struct timeval *tv, const struct timeval &start, const struct timeval &timeout) { if (!QElapsedTimer::isMonotonic()) { // we cannot recalculate the timeout without a monotonic clock as the time may have changed return false; } // clock source is monotonic, so we can recalculate how much timeout is left struct timeval now = qt_gettime(); *tv = timeout + start - now; return tv->tv_sec >= 0; }
QT_BEGIN_NAMESPACE #if !defined(QT_HAVE_PPOLL) && defined(QT_HAVE_POLLTS) # define ppoll pollts # define QT_HAVE_PPOLL #endif static inline bool time_update(struct timespec *tv, const struct timespec &start, const struct timespec &timeout) { // clock source is (hopefully) monotonic, so we can recalculate how much timeout is left; // if it isn't monotonic, we'll simply hope that it hasn't jumped, because we have no alternative struct timespec now = qt_gettime(); *tv = timeout + start - now; return tv->tv_sec >= 0; }
void qt_abstime_for_timeout(timespec *ts, int timeout) { #ifdef Q_OS_MAC // on Mac, qt_gettime() (on qelapsedtimer_mac.cpp) returns ticks related to the Mach absolute time // that doesn't work with pthread // Mac also doesn't have clock_gettime struct timeval tv; gettimeofday(&tv, 0); ts->tv_sec = tv.tv_sec; ts->tv_nsec = tv.tv_usec * 1000; #else *ts = qt_gettime(); #endif ts->tv_sec += timeout / 1000; ts->tv_nsec += timeout % 1000 * Q_UINT64_C(1000) * 1000; normalizedTimespec(*ts); }
timespec QTimerInfoList::updateCurrentTime() { return (currentTime = qt_gettime()); }
int QEventDispatcherBlackberry::select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, timespec *timeout) { Q_UNUSED(nfds); Q_D(QEventDispatcherBlackberry); const BBScopedLoopLevelCounter bbLoopCounter(d); BpsChannelScopeSwitcher channelSwitcher(d->bps_channel); // prepare file sets for bps callback d->ioData->count = 0; d->ioData->readfds = readfds; d->ioData->writefds = writefds; d->ioData->exceptfds = exceptfds; // reset all file sets if (readfds) FD_ZERO(readfds); if (writefds) FD_ZERO(writefds); if (exceptfds) FD_ZERO(exceptfds); bps_event_t *event = 0; unsigned int eventCount = 0; // If an event handler called through filterEvent() starts a nested event loop by creating a // new QEventLoop, we will recursively enter this function again. However, each time // bps_get_event() is called, it destroys the last event it handed out before returning the // next event. We don't want it to destroy the event that triggered the nested event loop, // since there may still be more handlers that need to get that event, once the nested event // loop is done and control returns to the outer event loop. // // So we move an event to a holding channel, which takes ownership of the event. Putting // the event on our own channel allows us to manage when it is destroyed, keeping it alive // until we know we are done with it. Each recursive call of this function needs to have // it's own holding channel, since a channel is a queue, not a stack. // // However, a recursive call into this function happens very rarely compared to the many // times this function is called. We don't want to create a holding channel for each time // this function is called, only when it is called recursively. Thus we have the instance // variable d->holding_channel to use in the common case. We keep track of recursive calls // with d->loop_level. If we are in a recursive call, then we create a new holding channel // for this run. int holding_channel = d->holding_channel; if ((d->loop_level > 1) && Q_UNLIKELY(bps_channel_create(&holding_channel, 0) != BPS_SUCCESS)) { qWarning("QEventDispatcherBlackberry: bps_channel_create failed"); holding_channel = -1; } // Convert timeout to milliseconds int timeoutTotal = -1; if (timeout) timeoutTotal = timespecToMillisecs(*timeout); int timeoutLeft = timeoutTotal; timespec startTime = qt_gettime(); // This loop exists such that we can drain the bps event queue of all native events // more efficiently than if we were to return control to Qt after each event. This // is important for handling touch events which can come in rapidly. forever { // Only emit the awake() and aboutToBlock() signals in the second iteration. For the // first iteration, the UNIX event dispatcher will have taken care of that already. // Also native events are actually processed one loop iteration after they were // retrieved with bps_get_event(). // Filtering the native event should happen between the awake() and aboutToBlock() // signal emissions. The calls awake() - filterNativeEvent() - aboutToBlock() - // bps_get_event() need not to be interrupted by a break or return statement. if (eventCount > 0) { if (event) { emit awake(); filterNativeEvent(QByteArrayLiteral("bps_event_t"), static_cast<void*>(event), 0); emit aboutToBlock(); if (Q_LIKELY(holding_channel != -1)) { // We are now done with this BPS event. Destroy it. destroyHeldBpsEvent(holding_channel); } } // Update the timeout // Clock source is monotonic, so we can recalculate how much timeout is left if (timeoutTotal != -1) { timespec t2 = qt_gettime(); timeoutLeft = timeoutTotal - (timespecToMillisecs(t2) - timespecToMillisecs(startTime)); if (timeoutLeft < 0) timeoutLeft = 0; } timespec tnext; if (d->timerList.timerWait(tnext)) { int timeoutNext = timespecToMillisecs(tnext); if (timeoutNext < timeoutLeft || timeoutTotal == -1) { timeoutTotal = timeoutLeft = timeoutNext; startTime = qt_gettime(); } } } event = 0; { // We need to increase loop level in this scope, // because bps_get_event can also invoke callbacks QScopedLoopLevelCounter loopLevelCounter(d->threadData); // Wait for event or file to be ready const int result = bps_get_event(&event, timeoutLeft); if (Q_UNLIKELY(result != BPS_SUCCESS)) qWarning("QEventDispatcherBlackberry: bps_get_event failed"); } if (!event) // In case of !event, we break out of the loop to let Qt process the timers break; // (since timeout has expired) and socket notifiers that are now ready. if (bps_event_get_domain(event) == bpsUnblockDomain) { timeoutTotal = 0; // in order to immediately drain the event queue of native events event = 0; // (especially touch move events) we don't break out here } else { // Move the event to our holding channel so we can manage when it is destroyed. if (Q_LIKELY(holding_channel != 1) && Q_UNLIKELY(bps_channel_push_event(holding_channel, event) != BPS_SUCCESS)) { qWarning("QEventDispatcherBlackberry: bps_channel_push_event failed"); } } ++eventCount; // Make sure we are not trapped in this loop due to continuous native events // also we cannot recalculate the timeout without a monotonic clock as the time may have changed const unsigned int maximumEventCount = 12; if (Q_UNLIKELY((eventCount > maximumEventCount && timeoutLeft == 0) || !QElapsedTimer::isMonotonic())) { if (event) { filterNativeEvent(QByteArrayLiteral("bps_event_t"), static_cast<void*>(event), 0); if (Q_LIKELY(holding_channel != -1)) { // We are now done with this BPS event. Destroy it. destroyHeldBpsEvent(holding_channel); } } break; } } // If this was a recursive call into this function, a new holding channel was created for // this run, so destroy it now. if ((holding_channel != d->holding_channel) && Q_LIKELY(holding_channel != -1) && Q_UNLIKELY(bps_channel_destroy(holding_channel) != BPS_SUCCESS)) { qWarning("QEventDispatcherBlackberry: bps_channel_destroy failed"); } // the number of bits set in the file sets return d->ioData->count; }