/* * * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2014-2017 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file * This file implements Layer using select(). */ #include #include #include #include #include #include #include #include // Choose an approximation of PTHREAD_NULL if pthread.h doesn't define one. #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING && !defined(PTHREAD_NULL) #define PTHREAD_NULL 0 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING && !defined(PTHREAD_NULL) #if CHIP_SYSTEM_CONFIG_USE_LIBEV // older libev do not yet have ev_io_modify #ifndef ev_io_modify #define ev_io_modify(ev, events_) \ do \ { \ (ev)->events = ((ev)->events & EV__IOFDSET) | (events_); \ } while (0) #endif // ev_io_modify #endif // CHIP_SYSTEM_CONFIG_USE_LIBEV namespace chip { namespace System { constexpr Clock::Seconds64 kDefaultMinSleepPeriod = Clock::Seconds64(60 * 60 * 24 * 30); // Month [sec] CHIP_ERROR LayerImplSelect::Init() { VerifyOrReturnError(mLayerState.SetInitializing(), CHIP_ERROR_INCORRECT_STATE); RegisterPOSIXErrorFormatter(); for (auto & w : mSocketWatchPool) { w.Clear(); } #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING mHandleSelectThread = PTHREAD_NULL; #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING #if !CHIP_SYSTEM_CONFIG_USE_LIBEV // Create an event to allow an arbitrary thread to wake the thread in the select loop. ReturnErrorOnFailure(mWakeEvent.Open(*this)); #endif // !CHIP_SYSTEM_CONFIG_USE_LIBEV VerifyOrReturnError(mLayerState.SetInitialized(), CHIP_ERROR_INCORRECT_STATE); return CHIP_NO_ERROR; } void LayerImplSelect::Shutdown() { VerifyOrReturn(mLayerState.SetShuttingDown()); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH TimerList::Node * timer; while ((timer = mTimerList.PopEarliest()) != nullptr) { if (timer->mTimerSource != nullptr) { dispatch_source_cancel(timer->mTimerSource); dispatch_release(timer->mTimerSource); } } mTimerPool.ReleaseAll(); for (auto & w : mSocketWatchPool) { w.DisableAndClear(); } #elif CHIP_SYSTEM_CONFIG_USE_LIBEV TimerList::Node * timer; while ((timer = mTimerList.PopEarliest()) != nullptr) { if (ev_is_active(&timer->mLibEvTimer)) { ev_timer_stop(mLibEvLoopP, &timer->mLibEvTimer); } } mTimerPool.ReleaseAll(); for (auto & w : mSocketWatchPool) { w.DisableAndClear(); } #else mTimerList.Clear(); mTimerPool.ReleaseAll(); #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV #if !CHIP_SYSTEM_CONFIG_USE_LIBEV mWakeEvent.Close(*this); #endif // !CHIP_SYSTEM_CONFIG_USE_LIBEV mLayerState.ResetFromShuttingDown(); // Return to uninitialized state to permit re-initialization. } void LayerImplSelect::Signal() { #if CHIP_SYSTEM_CONFIG_USE_LIBEV ChipLogError(DeviceLayer, "Signal() should not be called in CHIP_SYSTEM_CONFIG_USE_LIBEV builds (might be ok in tests)"); #else /* * Wake up the I/O thread by writing a single byte to the wake pipe. * * If this is being called from within an I/O event callback, then writing to the wake pipe can be skipped, * since the I/O thread is already awake. * * Furthermore, we don't care if this write fails as the only reasonably likely failure is that the pipe is full, in which * case the select calling thread is going to wake up anyway. */ #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING if (pthread_equal(mHandleSelectThread, pthread_self())) { return; } #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING // Send notification to wake up the select call. CHIP_ERROR status = mWakeEvent.Notify(); if (status != CHIP_NO_ERROR) { ChipLogError(chipSystemLayer, "System wake event notify failed: %" CHIP_ERROR_FORMAT, status.Format()); } #endif // !CHIP_SYSTEM_CONFIG_USE_LIBEV } CHIP_ERROR LayerImplSelect::StartTimer(Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) { assertChipStackLockedByCurrentThread(); VerifyOrReturnError(mLayerState.IsInitialized(), CHIP_ERROR_INCORRECT_STATE); CHIP_SYSTEM_FAULT_INJECT(FaultInjection::kFault_TimeoutImmediate, delay = System::Clock::kZero); CancelTimer(onComplete, appState); TimerList::Node * timer = mTimerPool.Create(*this, SystemClock().GetMonotonicTimestamp() + delay, onComplete, appState); VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH dispatch_queue_t dispatchQueue = GetDispatchQueue(); if (dispatchQueue) { (void) mTimerList.Add(timer); dispatch_source_t timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, DISPATCH_TIMER_STRICT, dispatchQueue); VerifyOrDie(timerSource != nullptr); timer->mTimerSource = timerSource; dispatch_source_set_timer( timerSource, dispatch_walltime(nullptr, static_cast(Clock::Milliseconds64(delay).count() * NSEC_PER_MSEC)), DISPATCH_TIME_FOREVER, 2 * NSEC_PER_MSEC); dispatch_source_set_event_handler(timerSource, ^{ dispatch_source_cancel(timerSource); dispatch_release(timerSource); this->HandleTimerComplete(timer); }); dispatch_resume(timerSource); return CHIP_NO_ERROR; } #elif CHIP_SYSTEM_CONFIG_USE_LIBEV VerifyOrDie(mLibEvLoopP != nullptr); ev_timer_init(&timer->mLibEvTimer, &LayerImplSelect::HandleLibEvTimer, 1, 0); timer->mLibEvTimer.data = timer; auto t = Clock::Milliseconds64(delay).count(); // Note: libev uses the time when events started processing as the "now" reference for relative timers, // for efficiency reasons. This point in time is represented by ev_now(). // The real time is represented by ev_time(). // Without correction, this leads to timers firing a bit too early relative to the time StartTimer() // is called. So the relative value passed to ev_timer_set() is adjusted (increased) here. // Note: Still, slightly early (and of course, late) firing timers are something the caller MUST be prepared for, // because edge cases like system clock adjustments may cause them even with the correction applied here. ev_timer_set(&timer->mLibEvTimer, (static_cast(t) / 1E3) + ev_time() - ev_now(mLibEvLoopP), 0.); (void) mTimerList.Add(timer); ev_timer_start(mLibEvLoopP, &timer->mLibEvTimer); return CHIP_NO_ERROR; #endif #if !CHIP_SYSTEM_CONFIG_USE_LIBEV // Note: dispatch based implementation needs this as fallback, but not LIBEV (and dead code is not allowed with -Werror) if (mTimerList.Add(timer) == timer) { // The new timer is the earliest, so the time until the next event has probably changed. Signal(); } return CHIP_NO_ERROR; #endif // !CHIP_SYSTEM_CONFIG_USE_LIBEV } CHIP_ERROR LayerImplSelect::ExtendTimerTo(Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) { VerifyOrReturnError(delay.count() > 0, CHIP_ERROR_INVALID_ARGUMENT); assertChipStackLockedByCurrentThread(); Clock::Timeout remainingTime = mTimerList.GetRemainingTime(onComplete, appState); if (remainingTime.count() < delay.count()) { if (remainingTime == Clock::kZero) { // If remaining time is Clock::kZero, it might possible that our timer is in // the mExpiredTimers list and about to be fired. Remove it from that list, since we are extending it. mExpiredTimers.Remove(onComplete, appState); } return StartTimer(delay, onComplete, appState); } return CHIP_NO_ERROR; } bool LayerImplSelect::IsTimerActive(TimerCompleteCallback onComplete, void * appState) { bool timerIsActive = (mTimerList.GetRemainingTime(onComplete, appState) > Clock::kZero); if (!timerIsActive) { // check if the timer is in the mExpiredTimers list about to be fired. for (TimerList::Node * timer = mExpiredTimers.Earliest(); timer != nullptr; timer = timer->mNextTimer) { if (timer->GetCallback().GetOnComplete() == onComplete && timer->GetCallback().GetAppState() == appState) { return true; } } } return timerIsActive; } Clock::Timeout LayerImplSelect::GetRemainingTime(TimerCompleteCallback onComplete, void * appState) { return mTimerList.GetRemainingTime(onComplete, appState); } void LayerImplSelect::CancelTimer(TimerCompleteCallback onComplete, void * appState) { assertChipStackLockedByCurrentThread(); VerifyOrReturn(mLayerState.IsInitialized()); TimerList::Node * timer = mTimerList.Remove(onComplete, appState); if (timer == nullptr) { // The timer was not in our "will fire in the future" list, but it might // be in the "we're about to fire these" chunk we already grabbed from // that list. Check for it there too, and if found there we still want // to cancel it. timer = mExpiredTimers.Remove(onComplete, appState); } VerifyOrReturn(timer != nullptr); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH if (timer->mTimerSource != nullptr) { dispatch_source_cancel(timer->mTimerSource); dispatch_release(timer->mTimerSource); } #elif CHIP_SYSTEM_CONFIG_USE_LIBEV VerifyOrDie(mLibEvLoopP != nullptr); ev_timer_stop(mLibEvLoopP, &timer->mLibEvTimer); #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV mTimerPool.Release(timer); #if !CHIP_SYSTEM_CONFIG_USE_LIBEV // LIBEV has no I/O wakeup thread, so must not call Signal() Signal(); #endif } CHIP_ERROR LayerImplSelect::ScheduleWork(TimerCompleteCallback onComplete, void * appState) { assertChipStackLockedByCurrentThread(); VerifyOrReturnError(mLayerState.IsInitialized(), CHIP_ERROR_INCORRECT_STATE); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH dispatch_queue_t dispatchQueue = GetDispatchQueue(); if (dispatchQueue) { dispatch_async(dispatchQueue, ^{ onComplete(this, appState); }); return CHIP_NO_ERROR; } #elif CHIP_SYSTEM_CONFIG_USE_LIBEV // schedule as timer with no delay, but do NOT cancel previous timers with same onComplete/appState! TimerList::Node * timer = mTimerPool.Create(*this, SystemClock().GetMonotonicTimestamp(), onComplete, appState); VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY); VerifyOrDie(mLibEvLoopP != nullptr); ev_timer_init(&timer->mLibEvTimer, &LayerImplSelect::HandleLibEvTimer, 1, 0); timer->mLibEvTimer.data = timer; auto t = Clock::Milliseconds64(0).count(); ev_timer_set(&timer->mLibEvTimer, static_cast(t) / 1E3, 0.); (void) mTimerList.Add(timer); ev_timer_start(mLibEvLoopP, &timer->mLibEvTimer); return CHIP_NO_ERROR; #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV #if !CHIP_SYSTEM_CONFIG_USE_LIBEV // Note: dispatch based implementation needs this as fallback, but not LIBEV (and dead code is not allowed with -Werror) // Ideally we would not use a timer here at all, but if we try to just // ScheduleLambda the lambda needs to capture the following: // 1) onComplete // 2) appState // 3) The `this` pointer, because onComplete needs to be passed a pointer to // the System::Layer. // // On a 64-bit system that's 24 bytes, but lambdas passed to ScheduleLambda // are capped at CHIP_CONFIG_LAMBDA_EVENT_SIZE which is 16 bytes. // // So for now use a timer as a poor-man's closure that captures `this` and // onComplete and appState in a single pointer, so we fit inside the size // limit. // // TODO: We could do something here where we compile-time condition on the // sizes of things and use a direct ScheduleLambda if it would fit and this // setup otherwise. // // TODO: But also, unit tests seem to do SystemLayer::ScheduleWork without // actually running a useful event loop (in the PlatformManager sense), // which breaks if we use ScheduleLambda here, since that does rely on the // PlatformManager event loop. So for now, keep scheduling an expires-ASAP // timer, but just make sure we don't cancel existing timers with the same // callback and appState, so ScheduleWork invocations don't stomp on each // other. TimerList::Node * timer = mTimerPool.Create(*this, SystemClock().GetMonotonicTimestamp(), onComplete, appState); VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY); if (mTimerList.Add(timer) == timer) { // The new timer is the earliest, so the time until the next event has probably changed. Signal(); } return CHIP_NO_ERROR; #endif // !CHIP_SYSTEM_CONFIG_USE_LIBEV } CHIP_ERROR LayerImplSelect::StartWatchingSocket(int fd, SocketWatchToken * tokenOut) { // Find a free slot. SocketWatch * watch = nullptr; for (auto & w : mSocketWatchPool) { if (w.mFD == fd) { // Already registered, return the existing token *tokenOut = reinterpret_cast(&w); return CHIP_NO_ERROR; } if ((w.mFD == kInvalidFd) && (watch == nullptr)) { watch = &w; } } VerifyOrReturnError(watch != nullptr, CHIP_ERROR_ENDPOINT_POOL_FULL); watch->mFD = fd; #if CHIP_SYSTEM_CONFIG_USE_LIBEV ev_io_init(&watch->mIoWatcher, &LayerImplSelect::HandleLibEvIoWatcher, 0, 0); watch->mIoWatcher.data = watch; watch->mLayerImplSelectP = this; #endif *tokenOut = reinterpret_cast(watch); return CHIP_NO_ERROR; } CHIP_ERROR LayerImplSelect::SetCallback(SocketWatchToken token, SocketWatchCallback callback, intptr_t data) { SocketWatch * watch = reinterpret_cast(token); VerifyOrReturnError(watch != nullptr, CHIP_ERROR_INVALID_ARGUMENT); watch->mCallback = callback; watch->mCallbackData = data; return CHIP_NO_ERROR; } CHIP_ERROR LayerImplSelect::RequestCallbackOnPendingRead(SocketWatchToken token) { SocketWatch * watch = reinterpret_cast(token); VerifyOrReturnError(watch != nullptr, CHIP_ERROR_INVALID_ARGUMENT); watch->mPendingIO.Set(SocketEventFlags::kRead); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH if (watch->mRdSource == nullptr) { // First time requesting callback for read events: install a dispatch source dispatch_queue_t dispatchQueue = GetDispatchQueue(); if (dispatchQueue == nullptr) { // Note: if no dispatch queue is available, callbacks most probably will not work, // unless, as in some tests from a test-specific local loop, // the select based event handling (Prepare/WaitFor/HandleEvents) is invoked. ChipLogError(DeviceLayer, "RequestCallbackOnPendingRead with no dispatch queue: callback may not work (might be ok in tests)"); } else { watch->mRdSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, static_cast(watch->mFD), 0, dispatchQueue); VerifyOrReturnError(watch->mRdSource != nullptr, CHIP_ERROR_NO_MEMORY); dispatch_source_set_event_handler(watch->mRdSource, ^{ if (watch->mPendingIO.Has(SocketEventFlags::kRead) && watch->mCallback != nullptr) { SocketEvents events; events.Set(SocketEventFlags::kRead); watch->mCallback(events, watch->mCallbackData); } }); // only now we are sure the source exists and can become active dispatch_activate(watch->mRdSource); } } #elif CHIP_SYSTEM_CONFIG_USE_LIBEV VerifyOrDie(mLibEvLoopP != nullptr); int evs = (watch->mPendingIO.Has(SocketEventFlags::kRead) ? EV_READ : 0) | (watch->mPendingIO.Has(SocketEventFlags::kWrite) ? EV_WRITE : 0); if (!ev_is_active(&watch->mIoWatcher)) { // First time actually using that watch ev_io_set(&watch->mIoWatcher, watch->mFD, evs); ev_io_start(mLibEvLoopP, &watch->mIoWatcher); } else { // already active, just change flags // Note: changing flags only reliably works when the watcher is stopped ev_io_stop(mLibEvLoopP, &watch->mIoWatcher); ev_io_modify(&watch->mIoWatcher, evs); ev_io_start(mLibEvLoopP, &watch->mIoWatcher); } #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH return CHIP_NO_ERROR; } CHIP_ERROR LayerImplSelect::RequestCallbackOnPendingWrite(SocketWatchToken token) { SocketWatch * watch = reinterpret_cast(token); VerifyOrReturnError(watch != nullptr, CHIP_ERROR_INVALID_ARGUMENT); watch->mPendingIO.Set(SocketEventFlags::kWrite); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH if (watch->mWrSource == nullptr) { // First time requesting callback for read events: install a dispatch source dispatch_queue_t dispatchQueue = GetDispatchQueue(); if (dispatchQueue == nullptr) { // Note: if no dispatch queue is available, callbacks most probably will not work, // unless, as in some tests from a test-specific local loop, // the select based event handling (Prepare/WaitFor/HandleEvents) is invoked. ChipLogError(DeviceLayer, "RequestCallbackOnPendingWrite with no dispatch queue: callback may not work (might be ok in tests)"); } else { watch->mWrSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, static_cast(watch->mFD), 0, dispatchQueue); VerifyOrReturnError(watch->mWrSource != nullptr, CHIP_ERROR_NO_MEMORY); dispatch_source_set_event_handler(watch->mWrSource, ^{ if (watch->mPendingIO.Has(SocketEventFlags::kWrite) && watch->mCallback != nullptr) { SocketEvents events; events.Set(SocketEventFlags::kWrite); watch->mCallback(events, watch->mCallbackData); } }); // only now we are sure the source exists and can become active dispatch_activate(watch->mWrSource); } } #elif CHIP_SYSTEM_CONFIG_USE_LIBEV VerifyOrDie(mLibEvLoopP != nullptr); int evs = (watch->mPendingIO.Has(SocketEventFlags::kRead) ? EV_READ : 0) | (watch->mPendingIO.Has(SocketEventFlags::kWrite) ? EV_WRITE : 0); if (!ev_is_active(&watch->mIoWatcher)) { // First time actually using that watch ev_io_set(&watch->mIoWatcher, watch->mFD, evs); ev_io_start(mLibEvLoopP, &watch->mIoWatcher); } else { // already active, just change flags // Note: changing flags only reliably works when the watcher is stopped ev_io_stop(mLibEvLoopP, &watch->mIoWatcher); ev_io_modify(&watch->mIoWatcher, evs); ev_io_start(mLibEvLoopP, &watch->mIoWatcher); } #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH return CHIP_NO_ERROR; } CHIP_ERROR LayerImplSelect::ClearCallbackOnPendingRead(SocketWatchToken token) { SocketWatch * watch = reinterpret_cast(token); VerifyOrReturnError(watch != nullptr, CHIP_ERROR_INVALID_ARGUMENT); watch->mPendingIO.Clear(SocketEventFlags::kRead); #if CHIP_SYSTEM_CONFIG_USE_LIBEV if (ev_is_active(&watch->mIoWatcher) && watch->mPendingIO.Raw() == 0) { // all flags cleared now, stop watching ev_io_stop(mLibEvLoopP, &watch->mIoWatcher); } #endif return CHIP_NO_ERROR; } CHIP_ERROR LayerImplSelect::ClearCallbackOnPendingWrite(SocketWatchToken token) { SocketWatch * watch = reinterpret_cast(token); VerifyOrReturnError(watch != nullptr, CHIP_ERROR_INVALID_ARGUMENT); watch->mPendingIO.Clear(SocketEventFlags::kWrite); #if CHIP_SYSTEM_CONFIG_USE_LIBEV if (ev_is_active(&watch->mIoWatcher) && watch->mPendingIO.Raw() == 0) { // all flags cleared now, stop watching ev_io_stop(mLibEvLoopP, &watch->mIoWatcher); } #endif return CHIP_NO_ERROR; } CHIP_ERROR LayerImplSelect::StopWatchingSocket(SocketWatchToken * tokenInOut) { SocketWatch * watch = reinterpret_cast(*tokenInOut); *tokenInOut = InvalidSocketWatchToken(); VerifyOrReturnError(watch != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(watch->mFD >= 0, CHIP_ERROR_INCORRECT_STATE); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH || CHIP_SYSTEM_CONFIG_USE_LIBEV watch->DisableAndClear(); #else watch->Clear(); // Wake the thread calling select so that it stops selecting on the socket. Signal(); #endif return CHIP_NO_ERROR; } /** * Set the read, write or exception bit flags for the specified socket based on its status in * the corresponding file descriptor sets. * * @param[in] socket The file descriptor for which the bit flags are being set. * * @param[in] readfds A pointer to the set of readable file descriptors. * * @param[in] writefds A pointer to the set of writable file descriptors. * * @param[in] exceptfds A pointer to the set of file descriptors with errors. */ SocketEvents LayerImplSelect::SocketEventsFromFDs(int socket, const fd_set & readfds, const fd_set & writefds, const fd_set & exceptfds) { SocketEvents res; if (socket >= 0) { // POSIX does not define the fd_set parameter of FD_ISSET() as const, even though it isn't modified. if (FD_ISSET(socket, const_cast(&readfds))) res.Set(SocketEventFlags::kRead); if (FD_ISSET(socket, const_cast(&writefds))) res.Set(SocketEventFlags::kWrite); if (FD_ISSET(socket, const_cast(&exceptfds))) res.Set(SocketEventFlags::kExcept); } return res; } #if !CHIP_SYSTEM_CONFIG_USE_DISPATCH enum : intptr_t { kLoopHandlerInactive = 0, // default value for EventLoopHandler::mState kLoopHandlerPending, kLoopHandlerActive, }; void LayerImplSelect::AddLoopHandler(EventLoopHandler & handler) { // Add the handler as pending because this method can be called at any point // in a PrepareEvents() / WaitForEvents() / HandleEvents() sequence. // It will be marked active when we call PrepareEvents() on it for the first time. auto & state = LoopHandlerState(handler); VerifyOrDie(state == kLoopHandlerInactive); state = kLoopHandlerPending; mLoopHandlers.PushBack(&handler); } void LayerImplSelect::RemoveLoopHandler(EventLoopHandler & handler) { mLoopHandlers.Remove(&handler); LoopHandlerState(handler) = kLoopHandlerInactive; } #endif // !CHIP_SYSTEM_CONFIG_USE_DISPATCH void LayerImplSelect::PrepareEvents() { assertChipStackLockedByCurrentThread(); const Clock::Timestamp currentTime = SystemClock().GetMonotonicTimestamp(); Clock::Timestamp awakenTime = currentTime + kDefaultMinSleepPeriod; TimerList::Node * timer = mTimerList.Earliest(); if (timer) { awakenTime = std::min(awakenTime, timer->AwakenTime()); } #if !CHIP_SYSTEM_CONFIG_USE_DISPATCH // Activate added EventLoopHandlers and call PrepareEvents on active handlers. auto loopIter = mLoopHandlers.begin(); while (loopIter != mLoopHandlers.end()) { auto & loop = *loopIter++; // advance before calling out, in case a list modification clobbers the `next` pointer switch (auto & state = LoopHandlerState(loop)) { case kLoopHandlerPending: state = kLoopHandlerActive; [[fallthrough]]; case kLoopHandlerActive: awakenTime = std::min(awakenTime, loop.PrepareEvents(currentTime)); break; } } #endif // !CHIP_SYSTEM_CONFIG_USE_DISPATCH const Clock::Timestamp sleepTime = (awakenTime > currentTime) ? (awakenTime - currentTime) : Clock::kZero; Clock::ToTimeval(sleepTime, mNextTimeout); mMaxFd = -1; // NOLINTBEGIN(clang-analyzer-security.insecureAPI.bzero) // // NOTE: darwin uses bzero to clear out FD sets. This is not a security concern. FD_ZERO(&mSelected.mReadSet); FD_ZERO(&mSelected.mWriteSet); FD_ZERO(&mSelected.mErrorSet); // NOLINTEND(clang-analyzer-security.insecureAPI.bzero) for (auto & w : mSocketWatchPool) { if (w.mFD != kInvalidFd) { if (mMaxFd < w.mFD) { mMaxFd = w.mFD; } if (w.mPendingIO.Has(SocketEventFlags::kRead)) { FD_SET(w.mFD, &mSelected.mReadSet); } if (w.mPendingIO.Has(SocketEventFlags::kWrite)) { FD_SET(w.mFD, &mSelected.mWriteSet); } } } } void LayerImplSelect::WaitForEvents() { mSelectResult = select(mMaxFd + 1, &mSelected.mReadSet, &mSelected.mWriteSet, &mSelected.mErrorSet, &mNextTimeout); } void LayerImplSelect::HandleEvents() { assertChipStackLockedByCurrentThread(); if (!IsSelectResultValid()) { ChipLogError(DeviceLayer, "Select failed: %" CHIP_ERROR_FORMAT, CHIP_ERROR_POSIX(errno).Format()); return; } #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING mHandleSelectThread = pthread_self(); #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING // Obtain the list of currently expired timers. Any new timers added by timer callback are NOT handled on this pass, // since that could result in infinite handling of new timers blocking any other progress. VerifyOrDieWithMsg(mExpiredTimers.Empty(), DeviceLayer, "Re-entry into HandleEvents from a timer callback?"); mExpiredTimers = mTimerList.ExtractEarlier(Clock::Timeout(1) + SystemClock().GetMonotonicTimestamp()); TimerList::Node * timer = nullptr; while ((timer = mExpiredTimers.PopEarliest()) != nullptr) { mTimerPool.Invoke(timer); } // Process socket events, if any if (mSelectResult > 0) { for (auto & w : mSocketWatchPool) { if (w.mFD != kInvalidFd && w.mCallback != nullptr) { SocketEvents events = SocketEventsFromFDs(w.mFD, mSelected.mReadSet, mSelected.mWriteSet, mSelected.mErrorSet); if (events.HasAny()) { w.mCallback(events, w.mCallbackData); } } } } #if !CHIP_SYSTEM_CONFIG_USE_DISPATCH // Call HandleEvents for active loop handlers auto loopIter = mLoopHandlers.begin(); while (loopIter != mLoopHandlers.end()) { auto & loop = *loopIter++; // advance before calling out, in case a list modification clobbers the `next` pointer if (LoopHandlerState(loop) == kLoopHandlerActive) { loop.HandleEvents(); } } #endif // !CHIP_SYSTEM_CONFIG_USE_DISPATCH #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING mHandleSelectThread = PTHREAD_NULL; #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING } #if CHIP_SYSTEM_CONFIG_USE_DISPATCH void LayerImplSelect::HandleTimerComplete(TimerList::Node * timer) { mTimerList.Remove(timer); mTimerPool.Invoke(timer); } #elif CHIP_SYSTEM_CONFIG_USE_LIBEV void LayerImplSelect::HandleLibEvTimer(EV_P_ struct ev_timer * t, int revents) { TimerList::Node * timer = static_cast(t->data); VerifyOrDie(timer != nullptr); LayerImplSelect * layerP = dynamic_cast(timer->mCallback.mSystemLayer); VerifyOrDie(layerP != nullptr); layerP->mTimerList.Remove(timer); layerP->mTimerPool.Invoke(timer); } void LayerImplSelect::HandleLibEvIoWatcher(EV_P_ struct ev_io * i, int revents) { SocketWatch * watch = static_cast(i->data); if (watch != nullptr && watch->mCallback != nullptr && watch->mLayerImplSelectP != nullptr) { SocketEvents events; if (revents & EV_READ) { events.Set(SocketEventFlags::kRead); } if (revents & EV_WRITE) { events.Set(SocketEventFlags::kWrite); } if (events.HasAny()) { watch->mCallback(events, watch->mCallbackData); } } } #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV void LayerImplSelect::SocketWatch::Clear() { mFD = kInvalidFd; mPendingIO.ClearAll(); mCallback = nullptr; mCallbackData = 0; #if CHIP_SYSTEM_CONFIG_USE_DISPATCH mRdSource = nullptr; mWrSource = nullptr; #elif CHIP_SYSTEM_CONFIG_USE_LIBEV mLayerImplSelectP = nullptr; #endif } #if CHIP_SYSTEM_CONFIG_USE_DISPATCH void LayerImplSelect::SocketWatch::DisableAndClear() { if (mRdSource) { dispatch_source_cancel(mRdSource); dispatch_release(mRdSource); } if (mWrSource) { dispatch_source_cancel(mWrSource); dispatch_release(mWrSource); } Clear(); } #elif CHIP_SYSTEM_CONFIG_USE_LIBEV void LayerImplSelect::SocketWatch::DisableAndClear() { if (mLayerImplSelectP != nullptr && mLayerImplSelectP->mLibEvLoopP != nullptr) { ev_io_stop(mLayerImplSelectP->mLibEvLoopP, &mIoWatcher); } Clear(); } #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV } // namespace System } // namespace chip