blob: 52354f99d82ff04e09a7a5e29bb2d41d3fa80656 [file] [log] [blame]
/*
*
* 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 <lib/support/CodeUtils.h>
#include <lib/support/TimeUtils.h>
#include <platform/LockTracker.h>
#include <system/SystemFaultInjection.h>
#include <system/SystemLayer.h>
#include <system/SystemLayerImplSelect.h>
#include <algorithm>
#include <errno.h>
// 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 && !CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
// 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 && !CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
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 && !CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
mWakeEvent.Close(*this);
#endif // !CHIP_SYSTEM_CONFIG_USE_LIBEV && !CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
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)");
#elif CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
ChipLogError(DeviceLayer, "Signal() should not be called in CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK builds");
#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_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
}
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<int64_t>(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;
}
#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
return CHIP_ERROR_INTERNAL;
#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
#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<double>(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 && !CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
// Note: The dispatch-based implementation (using sockets but not Network.framework) requires this as a fallback
// for testing purposes. However, it is not needed for LIBEV or when using Network.framework (which lacks a testing
// configuration). Since dead code is also not allowed with -Werror, we need to ifdef this code out
// in those configurations.
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_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
}
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 && !CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
// Neither LIBEV nor CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK builds include an 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;
}
#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
return CHIP_ERROR_INTERNAL;
#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
#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<double>(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 && !CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
// Note: The dispatch-based implementation (using sockets but not Network.framework) requires this as a fallback
// for testing purposes. However, it is not needed for LIBEV or when using Network.framework (which lacks a testing
// configuration). Since dead code is also not allowed with -Werror, we need to ifdef this code out
// in those configurations.
// 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_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
}
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<SocketWatchToken>(&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<SocketWatchToken>(watch);
return CHIP_NO_ERROR;
}
CHIP_ERROR LayerImplSelect::SetCallback(SocketWatchToken token, SocketWatchCallback callback, intptr_t data)
{
SocketWatch * watch = reinterpret_cast<SocketWatch *>(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<SocketWatch *>(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<uintptr_t>(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<SocketWatch *>(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<uintptr_t>(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<SocketWatch *>(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<SocketWatch *>(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<SocketWatch *>(*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<fd_set *>(&readfds)))
res.Set(SocketEventFlags::kRead);
if (FD_ISSET(socket, const_cast<fd_set *>(&writefds)))
res.Set(SocketEventFlags::kWrite);
if (FD_ISSET(socket, const_cast<fd_set *>(&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<TimerList::Node *>(t->data);
VerifyOrDie(timer != nullptr);
LayerImplSelect * layerP = dynamic_cast<LayerImplSelect *>(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<SocketWatch *>(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