From 45b23ba1fb3d16fcf2809820a6f5ea894e80dad3 Mon Sep 17 00:00:00 2001 From: Kevin Schoedel Date: Wed, 21 Jul 2021 10:21:16 -0400 Subject: [PATCH] Revert #8456 Move WakeIOThread() functionality to WatchableEventManager #### Problem TestPlatformMgr reports errors, ``` CHIP:CSL: System wake event notify failed: OS Error 0x02000009: Bad file descriptor ``` (although it still passes, which obscured the problem). This might be behind intermittent core dumps in CI. #### Change overview Revert #8456 Move WakeIOThread() functionality to WatchableEventManager #### Testing No error reported by TestPlatformMgr after reverting. --- src/controller/CHIPDeviceController.cpp | 6 +- .../java/CHIPDeviceController-JNI.cpp | 2 +- .../GenericPlatformManagerImpl_POSIX.cpp | 6 +- .../GenericPlatformManagerImpl_Zephyr.cpp | 2 +- src/inet/EndPointBasis.h | 2 +- src/inet/RawEndPoint.cpp | 2 +- src/inet/TCPEndPoint.cpp | 6 +- src/platform/Linux/MdnsImpl.h | 2 +- src/platform/mbed/PlatformManagerImpl.cpp | 7 +- src/system/BUILD.gn | 9 +- src/system/SystemConfig.h | 8 + src/system/SystemLayer.cpp | 45 +++ src/system/SystemLayer.h | 12 +- src/system/SystemTimer.cpp | 8 +- src/system/WakeEvent.cpp | 186 ------------ src/system/WakeEvent.h | 69 ----- src/system/WatchableEventManager.h | 72 ----- src/system/WatchableEventManagerLibevent.cpp | 196 ------------ src/system/WatchableEventManagerLibevent.h | 73 ----- src/system/WatchableEventManagerSelect.cpp | 233 -------------- src/system/WatchableEventManagerSelect.h | 92 ------ src/system/WatchableSocket.h | 285 ------------------ src/system/WatchableSocketLibevent.cpp | 148 ++++++++- src/system/WatchableSocketLibevent.h | 42 ++- src/system/WatchableSocketSelect.cpp | 205 +++++++++++-- src/system/WatchableSocketSelect.h | 69 ++++- src/system/tests/TestSystemWakeEvent.cpp | 27 +- 27 files changed, 506 insertions(+), 1308 deletions(-) delete mode 100644 src/system/WakeEvent.cpp delete mode 100644 src/system/WakeEvent.h delete mode 100644 src/system/WatchableEventManager.h delete mode 100644 src/system/WatchableEventManagerLibevent.cpp delete mode 100644 src/system/WatchableEventManagerLibevent.h delete mode 100644 src/system/WatchableEventManagerSelect.cpp delete mode 100644 src/system/WatchableEventManagerSelect.h delete mode 100644 src/system/WatchableSocket.h diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index a41dec0c091a9e..282c8e1887276e 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -549,11 +549,11 @@ CHIP_ERROR DeviceController::ServiceEventSignal() { VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE); -#if CONFIG_DEVICE_LAYER && (CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK) - DeviceLayer::SystemLayer.WatchableEvents().Signal(); +#if CONFIG_DEVICE_LAYER && CHIP_SYSTEM_CONFIG_USE_IO_THREAD + DeviceLayer::SystemLayer.WakeIOThread(); #else ReturnErrorOnFailure(CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); -#endif // CONFIG_DEVICE_LAYER && (CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK) +#endif // CONFIG_DEVICE_LAYER && CHIP_SYSTEM_CONFIG_USE_IO_THREAD return CHIP_NO_ERROR; } diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp index c1baa070b74639..262d7402705886 100644 --- a/src/controller/java/CHIPDeviceController-JNI.cpp +++ b/src/controller/java/CHIPDeviceController-JNI.cpp @@ -206,7 +206,7 @@ void JNI_OnUnload(JavaVM * jvm, void * reserved) if (sIOThread != PTHREAD_NULL) { sShutdown = true; - sSystemLayer.WatchableEvents().Signal(); + sSystemLayer.WakeIOThread(); StackUnlockGuard unlockGuard(JniReferences::GetInstance().GetStackLock()); pthread_join(sIOThread, NULL); diff --git a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.cpp b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.cpp index 44200995b8a569..c48eb5e3e39eca 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.cpp +++ b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.cpp @@ -127,7 +127,9 @@ void GenericPlatformManagerImpl_POSIX::_PostEvent(const ChipDeviceEve { mChipEventQueue.Push(*event); - SystemLayer.WatchableEvents().Signal(); // Trigger wake select on CHIP thread +#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD + SystemLayer.WakeIOThread(); // Trigger wake select on CHIP thread +#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD } template @@ -257,7 +259,7 @@ CHIP_ERROR GenericPlatformManagerImpl_POSIX::_StopEventLoopTask() // SystemLayer. // Impl()->LockChipStack(); - SystemLayer.WatchableEvents().Signal(); + SystemLayer.WakeIOThread(); Impl()->UnlockChipStack(); pthread_mutex_lock(&mStateLock); diff --git a/src/include/platform/internal/GenericPlatformManagerImpl_Zephyr.cpp b/src/include/platform/internal/GenericPlatformManagerImpl_Zephyr.cpp index 749ece712f3e0c..fb67c1a44fdb24 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl_Zephyr.cpp +++ b/src/include/platform/internal/GenericPlatformManagerImpl_Zephyr.cpp @@ -109,7 +109,7 @@ void GenericPlatformManagerImpl_Zephyr::_PostEvent(const ChipDeviceEv // k_msgq_put takes `void*` instead of `const void*`. Nonetheless, it should be safe to // const_cast here and there are components in Zephyr itself which do the same. if (k_msgq_put(&mChipEventQueue, const_cast(event), K_NO_WAIT) == 0) - SystemLayer.WatchableEvents().Signal(); // Trigger wake on CHIP thread + SystemLayer.WakeIOThread(); // Trigger wake on CHIP thread else ChipLogError(DeviceLayer, "Failed to post event to CHIP Platform event queue"); } diff --git a/src/inet/EndPointBasis.h b/src/inet/EndPointBasis.h index 2d7411351e7c42..0baa1a8be6260c 100644 --- a/src/inet/EndPointBasis.h +++ b/src/inet/EndPointBasis.h @@ -35,7 +35,7 @@ #include #if CHIP_SYSTEM_CONFIG_USE_SOCKETS -#include +#include #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS #if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK diff --git a/src/inet/RawEndPoint.cpp b/src/inet/RawEndPoint.cpp index c1df86b95721ed..16d461d7675d6f 100644 --- a/src/inet/RawEndPoint.cpp +++ b/src/inet/RawEndPoint.cpp @@ -48,7 +48,7 @@ #endif // CHIP_SYSTEM_CONFIG_USE_LWIP #if CHIP_SYSTEM_CONFIG_USE_SOCKETS -#include +#include #if HAVE_SYS_SOCKET_H #include #endif // HAVE_SYS_SOCKET_H diff --git a/src/inet/TCPEndPoint.cpp b/src/inet/TCPEndPoint.cpp index 1a24f17e39937c..5d488352b4063c 100644 --- a/src/inet/TCPEndPoint.cpp +++ b/src/inet/TCPEndPoint.cpp @@ -833,10 +833,10 @@ void TCPEndPoint::EnableReceive() DriveReceiving(); -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD // Wake the thread waiting for I/O so that it can include the socket. - SystemLayer().WatchableEvents().Signal(); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + SystemLayer().WakeIOThread(); +#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD } /** diff --git a/src/platform/Linux/MdnsImpl.h b/src/platform/Linux/MdnsImpl.h index 7360c148b6ed4b..042aa214626877 100644 --- a/src/platform/Linux/MdnsImpl.h +++ b/src/platform/Linux/MdnsImpl.h @@ -34,7 +34,7 @@ #include #include "lib/mdns/platform/Mdns.h" -#include "system/WatchableSocket.h" +#include "system/SystemSockets.h" struct AvahiWatch { diff --git a/src/platform/mbed/PlatformManagerImpl.cpp b/src/platform/mbed/PlatformManagerImpl.cpp index cfe4b7c8d55351..2b0384772d1134 100644 --- a/src/platform/mbed/PlatformManagerImpl.cpp +++ b/src/platform/mbed/PlatformManagerImpl.cpp @@ -46,9 +46,8 @@ CHIP_ERROR PlatformManagerImpl::_InitChipStack(void) mQueue.~EventQueue(); new (&mQueue) events::EventQueue(event_size * CHIP_DEVICE_CONFIG_MAX_EVENT_QUEUE_SIZE); - mQueue.background([&](int t) { - MbedEventTimeout::AttachTimeout([&] { SystemLayer.WatchableEvents().Signal(); }, std::chrono::milliseconds{ t }); - }); + mQueue.background( + [&](int t) { MbedEventTimeout::AttachTimeout([&] { SystemLayer.WakeIOThread(); }, std::chrono::milliseconds{ t }); }); // Reinitialize the Mutexes mThisStateMutex.~Mutex(); @@ -212,7 +211,7 @@ CHIP_ERROR PlatformManagerImpl::_StopEventLoopTask() // Wake from select so it unblocks processing LockChipStack(); - SystemLayer.WatchableEvents().Signal(); + SystemLayer.WakeIOThread(); UnlockChipStack(); osStatus err = osOK; diff --git a/src/system/BUILD.gn b/src/system/BUILD.gn index 387ea3c89465aa..3695867d85c400 100644 --- a/src/system/BUILD.gn +++ b/src/system/BUILD.gn @@ -96,7 +96,6 @@ buildconfig_header("system_buildconfig") { } if (chip_system_config_use_sockets) { - defines += [ "CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE=" ] defines += [ "CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE=" ] } } @@ -143,6 +142,8 @@ static_library("system") { "SystemObject.h", "SystemPacketBuffer.cpp", "SystemPacketBuffer.h", + "SystemSockets.cpp", + "SystemSockets.h", "SystemStats.cpp", "SystemStats.h", "SystemTimer.cpp", @@ -150,10 +151,6 @@ static_library("system") { "TLVPacketBufferBackingStore.cpp", "TLVPacketBufferBackingStore.h", "TimeSource.h", - "WakeEvent.cpp", - "WakeEvent.h", - "WatchableEventManager.h", - "WatchableSocket.h", ] cflags = [ "-Wconversion" ] @@ -168,8 +165,6 @@ static_library("system") { if (chip_system_config_use_sockets) { sources += [ - "WatchableEventManager${chip_system_config_sockets_event_loop}.cpp", - "WatchableEventManager${chip_system_config_sockets_event_loop}.h", "WatchableSocket${chip_system_config_sockets_event_loop}.cpp", "WatchableSocket${chip_system_config_sockets_event_loop}.h", ] diff --git a/src/system/SystemConfig.h b/src/system/SystemConfig.h index 35573ec09ba594..2ad29e073af22c 100644 --- a/src/system/SystemConfig.h +++ b/src/system/SystemConfig.h @@ -111,6 +111,14 @@ // clang-format off +#ifndef CHIP_SYSTEM_CONFIG_USE_IO_THREAD +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#define CHIP_SYSTEM_CONFIG_USE_IO_THREAD 1 +#else +#define CHIP_SYSTEM_CONFIG_USE_IO_THREAD 0 +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD + /** * @def CHIP_SYSTEM_CONFIG_TRANSFER_INETLAYER_PROJECT_CONFIGURATION * diff --git a/src/system/SystemLayer.cpp b/src/system/SystemLayer.cpp index a527234dd23918..681e8288db1d19 100644 --- a/src/system/SystemLayer.cpp +++ b/src/system/SystemLayer.cpp @@ -133,6 +133,11 @@ CHIP_ERROR Layer::Init() this->AddEventHandlerDelegate(sSystemEventHandlerDelegate); #endif // CHIP_SYSTEM_CONFIG_USE_LWIP +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + // Create an event to allow an arbitrary thread to wake the thread in the select loop. + ReturnErrorOnFailure(this->mWakeEvent.Open(mWatchableEvents)); +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + this->mLayerState = kLayerState_Initialized; return CHIP_NO_ERROR; @@ -143,6 +148,10 @@ CHIP_ERROR Layer::Shutdown() if (this->mLayerState == kLayerState_NotInitialized) return CHIP_ERROR_INCORRECT_STATE; +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + ReturnErrorOnFailure(mWakeEvent.Close()); +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + for (size_t i = 0; i < Timer::sPool.Size(); ++i) { Timer * lTimer = Timer::sPool.Get(*this, i); @@ -446,6 +455,42 @@ void Layer::HandleTimeout() #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD + +/** + * Wake up the I/O thread by writing a single byte to the wake pipe. + * + * @note + * If @p WakeIOThread() 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. + */ +void Layer::WakeIOThread() +{ + CHIP_ERROR lReturn; + + if (this->State() != kLayerState_Initialized) + return; + +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING + if (pthread_equal(this->mHandleSelectThread, pthread_self())) + { + return; + } +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING + + // Send notification to wake up the select call. + lReturn = this->mWakeEvent.Notify(); + if (lReturn != CHIP_NO_ERROR) + { + ChipLogError(chipSystemLayer, "System wake event notify failed: %s", ErrorStr(lReturn)); + } +} + +#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD + #if CHIP_SYSTEM_CONFIG_USE_LWIP LwIPEventHandlerDelegate Layer::sSystemEventHandlerDelegate; diff --git a/src/system/SystemLayer.h b/src/system/SystemLayer.h index 7ab9ae174d5fd7..e2eba02ed60e94 100644 --- a/src/system/SystemLayer.h +++ b/src/system/SystemLayer.h @@ -39,8 +39,7 @@ // Include dependent headers #if CHIP_SYSTEM_CONFIG_USE_SOCKETS -#include -#include +#include #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING @@ -137,11 +136,14 @@ class DLL_EXPORT Layer Clock & GetClock() { return mClock; } -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS WatchableEventManager & WatchableEvents() { return mWatchableEvents; } bool GetTimeout(struct timeval & aSleepTime); // TODO(#5556): Integrate timer platform details with WatchableEventManager. void HandleTimeout(); // TODO(#5556): Integrate timer platform details with WatchableEventManager. -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD + void WakeIOThread(); +#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD #if CHIP_SYSTEM_CONFIG_USE_LWIP typedef CHIP_ERROR (*EventHandler)(Object & aTarget, EventType aEventType, uintptr_t aArgument); @@ -170,8 +172,8 @@ class DLL_EXPORT Layer #if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK WatchableEventManager mWatchableEvents; + WakeEvent mWakeEvent; #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING - friend class WatchableEventManager; std::atomic mHandleSelectThread; #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK diff --git a/src/system/SystemTimer.cpp b/src/system/SystemTimer.cpp index 1191f6135626b7..e55c659f70f333 100644 --- a/src/system/SystemTimer.cpp +++ b/src/system/SystemTimer.cpp @@ -184,9 +184,9 @@ CHIP_ERROR Timer::Start(uint32_t aDelayMilliseconds, OnCompleteFunct aOnComplete } #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - lLayer.WatchableEvents().Signal(); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD + lLayer.WakeIOThread(); +#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK return CHIP_NO_ERROR; @@ -220,7 +220,7 @@ CHIP_ERROR Timer::ScheduleWork(OnCompleteFunct aOnComplete, void * aAppState) else { #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH - lLayer.WatchableEvents().Signal(); + lLayer.WakeIOThread(); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH } #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH diff --git a/src/system/WakeEvent.cpp b/src/system/WakeEvent.cpp deleted file mode 100644 index e0e387a7c273ca..00000000000000 --- a/src/system/WakeEvent.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * - * Copyright (c) 2020-2021 Project CHIP Authors - * - * 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 declares the abstraction of one-directional, anonymous - * data stream built on top of two file descriptors. - */ - -#include - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - -#include - -// Include additional CHIP headers -#include -#include - -// Include system and language headers -#include -#include -#include - -#if !CHIP_SYSTEM_CONFIG_USE_POSIX_PIPE -#include -#endif - -namespace chip { -namespace System { - -#if CHIP_SYSTEM_CONFIG_USE_POSIX_PIPE - -namespace { -inline int SetNonBlockingMode(int fd) -{ - int flags = ::fcntl(fd, F_GETFL, 0); - return ::fcntl(fd, F_SETFL, flags | O_NONBLOCK); -} -} // anonymous namespace - -CHIP_ERROR WakeEvent::Open(WatchableEventManager & watchState) -{ - enum - { - FD_READ = 0, - FD_WRITE = 1 - }; - int fds[2]; - - if (::pipe(fds) < 0) - return chip::System::MapErrorPOSIX(errno); - - if (SetNonBlockingMode(fds[FD_READ]) < 0) - return chip::System::MapErrorPOSIX(errno); - - if (SetNonBlockingMode(fds[FD_WRITE]) < 0) - return chip::System::MapErrorPOSIX(errno); - - mFD.Init(watchState); - mFD.Attach(fds[FD_READ]); - mFD.SetCallback(Confirm, reinterpret_cast(this)); - mFD.RequestCallbackOnPendingRead(); - - mWriteFD = fds[FD_WRITE]; - - return CHIP_NO_ERROR; -} - -CHIP_ERROR WakeEvent::Close() -{ - int res = 0; - - res |= mFD.Close(); - res |= ::close(mWriteFD); - mWriteFD = -1; - - if (res < 0) - { - return chip::System::MapErrorPOSIX(errno); - } - - return CHIP_NO_ERROR; -} - -void WakeEvent::Confirm() -{ - uint8_t buffer[128]; - ssize_t res; - - do - { - res = ::read(mFD.GetFD(), buffer, sizeof(buffer)); - if (res < 0 && errno != EAGAIN && errno != EWOULDBLOCK) - { - ChipLogError(chipSystemLayer, "System wake event confirm failed: %s", ErrorStr(chip::System::MapErrorPOSIX(errno))); - return; - } - } while (res == sizeof(buffer)); -} - -CHIP_ERROR WakeEvent::Notify() -{ - char byte = 1; - - if (::write(mWriteFD, &byte, 1) < 0 && errno != EAGAIN && errno != EWOULDBLOCK) - { - return chip::System::MapErrorPOSIX(errno); - } - - return CHIP_NO_ERROR; -} - -#else // CHIP_SYSTEM_CONFIG_USE_POSIX_PIPE - -CHIP_ERROR WakeEvent::Open(WatchableEventManager & watchState) -{ - mFD.Init(watchState); - - const int fd = ::eventfd(0, 0); - if (fd == -1) - { - return chip::System::MapErrorPOSIX(errno); - } - - mFD.Attach(fd); - mFD.SetCallback(Confirm, reinterpret_cast(this)); - mFD.RequestCallbackOnPendingRead(); - - return CHIP_NO_ERROR; -} - -CHIP_ERROR WakeEvent::Close() -{ - int res = mFD.Close(); - - if (res < 0) - { - return chip::System::MapErrorPOSIX(errno); - } - - return CHIP_NO_ERROR; -} - -void WakeEvent::Confirm() -{ - uint64_t value; - - if (::read(mFD.GetFD(), &value, sizeof(value)) < 0 && errno != EAGAIN && errno != EWOULDBLOCK) - { - ChipLogError(chipSystemLayer, "System wake event confirm failed: %s", ErrorStr(chip::System::MapErrorPOSIX(errno))); - } -} - -CHIP_ERROR WakeEvent::Notify() -{ - uint64_t value = 1; - - if (::write(mFD.GetFD(), &value, sizeof(value)) < 0 && errno != EAGAIN && errno != EWOULDBLOCK) - { - return chip::System::MapErrorPOSIX(errno); - } - - return CHIP_NO_ERROR; -} - -#endif // CHIP_SYSTEM_CONFIG_USE_POSIX_PIPE - -} // namespace System -} // namespace chip - -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS diff --git a/src/system/WakeEvent.h b/src/system/WakeEvent.h deleted file mode 100644 index 54465ede3bf3ce..00000000000000 --- a/src/system/WakeEvent.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * - * Copyright (c) 2020-2021 Project CHIP Authors - * - * 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 declares the abstraction of socket (file descriptor) events. - */ - -#pragma once - -// Include configuration headers -#include - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - -#include -#include - -namespace chip { -namespace System { - -class WakeEventTest; -class WatchableEventManager; - -/** - * @class WakeEvent - * - * An instance of this type can be used by a WatchableEventManager to allow other threads - * to wake its event loop thread via WatchableEventManager::Signal(). - */ -class WakeEvent -{ -public: - CHIP_ERROR Open(WatchableEventManager & watchState); /**< Initialize the pipeline */ - CHIP_ERROR Close(); /**< Close both ends of the pipeline. */ - - CHIP_ERROR Notify(); /**< Set the event. */ - void Confirm(); /**< Clear the event. */ - -private: - friend class WakeEventTest; - - int GetReadFD() const { return mFD.GetFD(); } - static void Confirm(WatchableSocket & socket) { reinterpret_cast(socket.GetCallbackData())->Confirm(); } - -#if CHIP_SYSTEM_CONFIG_USE_POSIX_PIPE - int mWriteFD; -#endif - WatchableSocket mFD; -}; - -} // namespace System -} // namespace chip - -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS diff --git a/src/system/WatchableEventManager.h b/src/system/WatchableEventManager.h deleted file mode 100644 index bf226453974cd2..00000000000000 --- a/src/system/WatchableEventManager.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * Copyright (c) 2020-2021 Project CHIP Authors - * - * 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 declares the abstraction for managing a collection of socket (file descriptor) events. - */ - -#pragma once - -// Include configuration headers -#include - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - -namespace chip { - -namespace System { - -class Layer; - -/** - * @class WatchableEventManager - * - * An instance of this type is contained in System::Layer. Its purpose is to hold socket-event system state - * or methods available to every associated instance of WatchableSocket. - * - * It MUST provide at least three methods: - * - * - void Init(System::Layer & systemLayer) -- called from System::Layer::Init() - * - void Shutdown() -- called from System::Layer::Shutdown() - * - void Signal() -- called to indicate that event monitoring may need to be refreshed or resumed. - * - * Other contents depend on the contract between socket-event implementation and platform layer implementation. - * For POSIX-like platforms, WatchableEventManager provides a set of functions called from the event loop: - * - * - void EventLoopBegins() -- Called before the first iterations of the event loop. - * - void PrepareEvents() -- Called at the start of each iteration of the event loop. - * - void WaitForEvents() -- Called on each iteration of the event loop, between PrepareEvents() and HandleEvents(). - * Uniquely, this method gets called with the CHIP stack NOT locked, so it can block. - * For example, the select()-based implementation calls select() here. - * - void HandleEvents() -- Called at the end of each iteration of the event loop. - * - void EventLoopEnds() -- Called after the last iteration of the event loop. - */ -class WatchableEventManager; - -} // namespace System -} // namespace chip - -#define INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE 1 -#ifdef CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE -#include CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE -#else // CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE -#include -#endif // CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE -#undef INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE - -#endif // CHIP_SYSTEM_CONFIG_USE_EVENT_MANAGERS diff --git a/src/system/WatchableEventManagerLibevent.cpp b/src/system/WatchableEventManagerLibevent.cpp deleted file mode 100644 index 8517b290a54c27..00000000000000 --- a/src/system/WatchableEventManagerLibevent.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - * - * Copyright (c) 2021 Project CHIP Authors - * - * 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 WatchableEventManager using libevent. - */ - -#include -#include -#include -#include -#include - -#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ - -namespace chip { -namespace Mdns { -void GetMdnsTimeout(timeval & timeout); -void HandleMdnsTimeout(); -} // namespace Mdns -} // namespace chip - -#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ - -#ifndef CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS -#define CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS 1 // TODO(#5556): default to off -#endif - -namespace chip { -namespace System { - -namespace { - -System::SocketEvents SocketEventsFromLibeventFlags(short eventFlags) -{ - return System::SocketEvents() - .Set(SocketEventFlags::kRead, eventFlags & EV_READ) - .Set(SocketEventFlags::kWrite, eventFlags & EV_WRITE); -} - -void TimeoutCallbackHandler(evutil_socket_t fd, short eventFlags, void * data) -{ - event * const ev = reinterpret_cast(data); - evtimer_del(ev); -} - -} // anonymous namespace - -void WatchableEventManager::Init(System::Layer & systemLayer) -{ -#if CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS - static bool enabled_event_debug_mode = false; - if (!enabled_event_debug_mode) - { - enabled_event_debug_mode = true; - event_enable_debug_mode(); - } -#endif // CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS - - mEventBase = event_base_new(); - mTimeoutEvent = evtimer_new(mEventBase, TimeoutCallbackHandler, event_self_cbarg()); - mActiveSockets = nullptr; - mSystemLayer = &systemLayer; -} - -void WatchableEventManager::PrepareEvents() -{ - // TODO(#5556): Integrate timer platform details with WatchableEventManager. - timeval nextTimeout = { 0, 0 }; - PrepareEventsWithTimeout(nextTimeout); -} - -void WatchableEventManager::PrepareEventsWithTimeout(struct timeval & nextTimeout) -{ - // TODO(#5556): Integrate timer platform details with WatchableEventManager. - mSystemLayer->GetTimeout(nextTimeout); - if (nextTimeout.tv_sec || nextTimeout.tv_usec) - { - evtimer_add(mTimeoutEvent, &nextTimeout); - } -} - -void WatchableEventManager::WaitForEvents() -{ - VerifyOrDie(mEventBase != nullptr); - event_base_loop(mEventBase, EVLOOP_ONCE); -} - -void WatchableEventManager::HandleEvents() -{ - mSystemLayer->HandleTimeout(); - -#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ - chip::Mdns::HandleMdnsTimeout(); -#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ - - while (mActiveSockets != nullptr) - { - WatchableSocket * const watcher = mActiveSockets; - mActiveSockets = watcher->mActiveNext; - watcher->InvokeCallback(); - } -} - -void WatchableEventManager::Shutdown() -{ - event_base_loopbreak(mEventBase); - event_free(mTimeoutEvent); - mTimeoutEvent = nullptr; - event_base_free(mEventBase); - mEventBase = nullptr; -} - -void WatchableEventManager::Signal() -{ - /* - * Wake up the I/O thread by writing a single byte to the wake pipe. - * - * If p WakeIOThread() 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(mSystemLayer->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, ChipError::FormatError(status)); - } -} - -// static -void WatchableEventManager::LibeventCallbackHandler(evutil_socket_t fd, short eventFlags, void * data) -{ - WatchableSocket * const watcher = reinterpret_cast(data); - VerifyOrDie(watcher != nullptr); - VerifyOrDie(watcher->mFD == fd); - - watcher->mPendingIO = SocketEventsFromLibeventFlags(eventFlags); - - // Add to active list. - WatchableSocket ** pp = &watcher->mSharedState->mActiveSockets; - while (*pp != nullptr) - { - if (*pp == watcher) - { - return; - } - pp = &(*pp)->mActiveNext; - } - *pp = watcher; - watcher->mActiveNext = nullptr; -} - -void WatchableEventManager::RemoveFromQueueIfPresent(WatchableSocket * watcher) -{ - VerifyOrDie(watcher != nullptr); - VerifyOrDie(watcher->mSharedState == this); - - WatchableSocket ** pp = &mActiveSockets; - while (*pp != nullptr) - { - if (*pp == watcher) - { - *pp = watcher->mActiveNext; - return; - } - pp = &(*pp)->mActiveNext; - } -} - -} // namespace System -} // namespace chip diff --git a/src/system/WatchableEventManagerLibevent.h b/src/system/WatchableEventManagerLibevent.h deleted file mode 100644 index 828b3565a6a273..00000000000000 --- a/src/system/WatchableEventManagerLibevent.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * - * Copyright (c) 2021 Project CHIP Authors - * - * 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 declares an implementation of WatchableEvents using libevent. - */ - -#pragma once - -#if !INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE -#error "This file should only be included from " -#include -#endif // !INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE - -#include - -namespace chip { - -namespace System { - -class WatchableEventManager -{ -public: - WatchableEventManager() : mActiveSockets(nullptr), mSystemLayer(nullptr), mEventBase(nullptr), mTimeoutEvent(nullptr) {} - void Init(Layer & systemLayer); - void Shutdown(); - void Signal(); - - void EventLoopBegins() {} - void PrepareEvents(); - void WaitForEvents(); - void HandleEvents(); - void EventLoopEnds() {} - - // TODO(#5556): Some unit tests supply a timeout at low level, due to originally using select(); these should a proper timer. - void PrepareEventsWithTimeout(timeval & nextTimeout); - -private: - /* - * In this implementation, libevent invokes LibeventCallbackHandler from beneath WaitForEvents(), - * which means that the CHIP stack is unlocked. LibeventCallbackHandler adds the WatchableSocket - * to a queue (implemented as a simple intrusive list to avoid dynamic memory allocation), and - * then HandleEvents() invokes the WatchableSocket callbacks. - */ - friend class WatchableSocket; - static void LibeventCallbackHandler(evutil_socket_t fd, short eventFlags, void * data); - void RemoveFromQueueIfPresent(WatchableSocket * watcher); - WatchableSocket * mActiveSockets; ///< List of sockets activated by libevent. - - Layer * mSystemLayer; - event_base * mEventBase; ///< libevent shared state. - event * mTimeoutEvent; - - WakeEvent mWakeEvent; -}; - -} // namespace System -} // namespace chip diff --git a/src/system/WatchableEventManagerSelect.cpp b/src/system/WatchableEventManagerSelect.cpp deleted file mode 100644 index f1f975b3bbd18e..00000000000000 --- a/src/system/WatchableEventManagerSelect.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - * - * 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 WatchableEventManager using select(). - */ - -#include -#include -#include -#include -#include - -#include - -#define DEFAULT_MIN_SLEEP_PERIOD (60 * 60 * 24 * 30) // Month [sec] - -#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ - -namespace chip { -namespace Mdns { -void GetMdnsTimeout(timeval & timeout); -void HandleMdnsTimeout(); -} // namespace Mdns -} // namespace chip - -#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ - -namespace chip { -namespace System { - -void WatchableEventManager::Init(Layer & systemLayer) -{ - mSystemLayer = &systemLayer; - mMaxFd = -1; - FD_ZERO(&mRequest.mReadSet); - FD_ZERO(&mRequest.mWriteSet); - FD_ZERO(&mRequest.mErrorSet); - - // Create an event to allow an arbitrary thread to wake the thread in the select loop. - mWakeEvent.Open(*this); -} - -void WatchableEventManager::Shutdown() -{ - mWakeEvent.Close(); - mSystemLayer = nullptr; -} - -void WatchableEventManager::Signal() -{ - /* - * Wake up the I/O thread by writing a single byte to the wake pipe. - * - * If p WakeIOThread() 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(mSystemLayer->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, ChipError::FormatError(status)); - } -} - -/** - * 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 WatchableEventManager::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; -} - -bool WatchableEventManager::HasAny(int fd) -{ - return FD_ISSET(fd, &mRequest.mReadSet) || FD_ISSET(fd, &mRequest.mWriteSet) || FD_ISSET(fd, &mRequest.mErrorSet); -} - -void WatchableEventManager::Set(int fd, fd_set * fds) -{ - FD_SET(fd, fds); - if (fd > mMaxFd) - { - mMaxFd = fd; - } - // Wake the thread calling select so that it starts selecting on the new socket. - Signal(); -} - -void WatchableEventManager::Clear(int fd, fd_set * fds) -{ - FD_CLR(fd, fds); - if (fd == mMaxFd) - { - MaybeLowerMaxFd(); - } - // Wake the thread calling select so that it starts selecting on the new socket. - Signal(); -} - -void WatchableEventManager::Reset(int fd) -{ - FD_CLR(fd, &mRequest.mReadSet); - FD_CLR(fd, &mRequest.mWriteSet); - FD_CLR(fd, &mRequest.mErrorSet); - if (fd == mMaxFd) - { - MaybeLowerMaxFd(); - } -} - -void WatchableEventManager::MaybeLowerMaxFd() -{ - int fd; - for (fd = mMaxFd; fd >= 0; --fd) - { - if (HasAny(fd)) - { - break; - } - } - mMaxFd = fd; -} - -void WatchableEventManager::PrepareEvents() -{ - assertChipStackLockedByCurrentThread(); - - // Max out this duration and let CHIP set it appropriately. - mNextTimeout.tv_sec = DEFAULT_MIN_SLEEP_PERIOD; - mNextTimeout.tv_usec = 0; - PrepareEventsWithTimeout(mNextTimeout); -} - -void WatchableEventManager::PrepareEventsWithTimeout(struct timeval & nextTimeout) -{ - // TODO(#5556): Integrate timer platform details with WatchableEventManager. - mSystemLayer->GetTimeout(nextTimeout); - -#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ && !__MBED__ - chip::Mdns::GetMdnsTimeout(nextTimeout); -#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ - - mSelected = mRequest; -} - -void WatchableEventManager::WaitForEvents() -{ - mSelectResult = select(mMaxFd + 1, &mSelected.mReadSet, &mSelected.mWriteSet, &mSelected.mErrorSet, &mNextTimeout); -} - -void WatchableEventManager::HandleEvents() -{ - assertChipStackLockedByCurrentThread(); - - if (mSelectResult < 0) - { - ChipLogError(DeviceLayer, "select failed: %s\n", ErrorStr(System::MapErrorPOSIX(errno))); - return; - } - - VerifyOrDie(mSystemLayer != nullptr); - mSystemLayer->HandleTimeout(); - - for (WatchableSocket * watchable = mAttachedSockets; watchable != nullptr; watchable = watchable->mAttachedNext) - { - watchable->SetPendingIO( - SocketEventsFromFDs(watchable->GetFD(), mSelected.mReadSet, mSelected.mWriteSet, mSelected.mErrorSet)); - } - for (WatchableSocket * watchable = mAttachedSockets; watchable != nullptr; watchable = watchable->mAttachedNext) - { - if (watchable->mPendingIO.HasAny()) - { - watchable->InvokeCallback(); - } - } - -#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ && !__MBED__ - chip::Mdns::HandleMdnsTimeout(); -#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ -} - -} // namespace System -} // namespace chip diff --git a/src/system/WatchableEventManagerSelect.h b/src/system/WatchableEventManagerSelect.h deleted file mode 100644 index a7bc285b8536a3..00000000000000 --- a/src/system/WatchableEventManagerSelect.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * - * Copyright (c) 2021 Project CHIP Authors - * - * 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 declares an implementation of WatchableEventManager using select(). - */ - -#pragma once - -#if !INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE -#error "This file should only be included from " -#include -#endif // !INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE - -#include - -#include -#include -#include - -namespace chip { -namespace System { - -class WatchableSocket; - -class WatchableEventManager -{ -public: - void Init(System::Layer & systemLayer); - void Shutdown(); - void Signal(); - - void EventLoopBegins() {} - void PrepareEvents(); - void WaitForEvents(); - void HandleEvents(); - void EventLoopEnds() {} - - // TODO(#5556): Some unit tests supply a timeout at low level, due to originally using select(); these should a proper timer. - void PrepareEventsWithTimeout(timeval & nextTimeout); - - static SocketEvents SocketEventsFromFDs(int socket, const fd_set & readfds, const fd_set & writefds, const fd_set & exceptfds); - -protected: - friend class WatchableSocket; - - void Set(int fd, fd_set * fds); - void Clear(int fd, fd_set * fds); - - Layer * mSystemLayer = nullptr; - WatchableSocket * mAttachedSockets = nullptr; - - // TODO(#5556): Integrate timer platform details with WatchableEventManager. - struct timeval mNextTimeout; - - // Members for select loop - struct SelectSets - { - fd_set mReadSet; - fd_set mWriteSet; - fd_set mErrorSet; - }; - SelectSets mRequest; - SelectSets mSelected; - int mMaxFd; - int mSelectResult; ///< return value from select() - - WakeEvent mWakeEvent; - -private: - bool HasAny(int fd); - void MaybeLowerMaxFd(); - void Reset(int fd); -}; - -} // namespace System -} // namespace chip diff --git a/src/system/WatchableSocket.h b/src/system/WatchableSocket.h deleted file mode 100644 index 32ed69aa21a8e6..00000000000000 --- a/src/system/WatchableSocket.h +++ /dev/null @@ -1,285 +0,0 @@ -/* - * - * Copyright (c) 2020-2021 Project CHIP Authors - * - * 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 declares the WatchableSocket abstraction of socket (file descriptor) events. - */ - -#pragma once - -// Include configuration headers -#include - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - -#include - -#include - -namespace chip { - -namespace System { - -class Layer; -class WatchableEventManager; - -enum class SocketEventFlags : uint8_t -{ - kRead = 0x1, /**< Bit flag indicating if there is a read event on a socket. */ - kWrite = 0x2, /**< Bit flag indicating if there is a write event on a socket. */ - kExcept = 0x4, /**< Bit flag indicating if there is an exceptional condition on a socket (e.g. out-of-band data). */ - kError = 0x8, /**< Bit flag indicating if there is an error event on a socket. */ -}; - -using SocketEvents = BitFlags; - -/** - * @class WatchableSocket - * - * Users of a WatchableSocket should interact with it using the methods defined by WatchableSocketBasis. - * - * Implementations of WatchableSocket must inherit `public WatchableSocketBasis` - * and provide the following methods, which are invoked by the corresponding WatchableSocketBasis functions: - * - * void OnInit() - * void OnAttach() - * void OnClose() - * void OnRequestCallbackOnPendingRead() - * void OnRequestCallbackOnPendingWrite() - * void OnClearCallbackOnPendingRead() - * void OnClearCallbackOnPendingWrite() - * - */ -class WatchableSocket; - -/** - * @class WatchableSocketBasis - * - * This class provides the interface used by platform-independent parts of the CHIP stack. - * - * The general pattern for using a WatchableSocket s is: - * - * s.Init(WatchableEventManager) - * s.Attach(fd) - * s.SetCallback(callbackFunction, callbackData) - * s.{Request|Clear}CallbackOnPending{Read|Write}() - * ... - * s.Close() - * - */ -template -class WatchableSocketBasis -{ -public: - enum : int - { - kInvalidFd = -1 - }; - - /** - * Initialize a WatchableSocket. - * - * @param[in] manager Reference to shared socket-event state (which must already have been initialized). - */ - Impl & Init(WatchableEventManager & manager) - { - mFD = kInvalidFd; - mPendingIO.ClearAll(); - mCallback = nullptr; - mCallbackData = 0; - mSharedState = &manager; - Impl * const impl = static_cast(this); - impl->OnInit(); - return *impl; - } - - /** - * Associate this WatchableSocket with a file descriptor. - * - * @param[in] fd An open file descriptor. - */ - Impl & Attach(int fd) - { - mFD = fd; - Impl * const impl = static_cast(this); - impl->OnAttach(); - return *impl; - } - - /** - * Disassociate this WatchableSocket from its file descriptor. - * - * @returns the file descriptor. - */ - int ReleaseFD() - { - static_cast(this)->OnClose(); - const int fd = mFD; - mFD = kInvalidFd; - return fd; - } - - /** - * Close the associated file descriptor. - * - * @returns the return value of `close(2)`. - */ - int Close() { return close(ReleaseFD()); } - - /** - * Test whether there is an associated open file descriptor. - */ - bool HasFD() const { return mFD >= 0; } - - /** - * Get the associated open file descriptor. - */ - int GetFD() const { return mFD; } - - /** - * Indicate that the socket-event system should invoke the registered callback when the file descriptor is ready to read. - */ - Impl & RequestCallbackOnPendingRead(bool request = true) - { - Impl * const impl = static_cast(this); - if (request) - { - impl->OnRequestCallbackOnPendingRead(); - } - return *impl; - } - - /** - * Indicate that the socket-event system should invoke the registered callback when the file descriptor is ready to write. - */ - Impl & RequestCallbackOnPendingWrite(bool request = true) - { - Impl * const impl = static_cast(this); - if (request) - { - impl->OnRequestCallbackOnPendingWrite(); - } - return *impl; - } - - /** - * Indicate that the socket-event system need not invoke the registered callback when the file descriptor is ready to read. - */ - Impl & ClearCallbackOnPendingRead() - { - Impl * const impl = static_cast(this); - impl->OnClearCallbackOnPendingRead(); - return *impl; - } - - /** - * Indicate that the socket-event system need not invoke the registered callback when the file descriptor is ready to write. - */ - Impl & ClearCallbackOnPendingWrite() - { - Impl * const impl = static_cast(this); - impl->OnClearCallbackOnPendingWrite(); - return *impl; - } - - /** - * The callback is passed a reference to the WatchableSocket for which the requested event(s) are ready. - */ - using Callback = void (*)(WatchableSocket & socket); - - /** - * Register a callback function. - * - * The callback will be invoked (with the CHIP stack lock held) when requested event(s) are ready. - * - * @param[in] callback Function invoked when event(s) are ready. - * @param[in] data Arbitrary data accessible within a callback function. - */ - Impl & SetCallback(Callback callback, intptr_t data) - { - mCallback = callback; - mCallbackData = data; - return *static_cast(this); - } - - /** - * Retrieve callback data. - * - * @returns the pointer supplied to SetCallback(). - */ - intptr_t GetCallbackData() const { return mCallbackData; } - - /** - * Inside a callback function, get the pending SocketEvents. - */ - SocketEvents GetPendingEvents() const { return mPendingIO; } - - /** - * Inside a callback function, test whether the file descriptor is ready to read. - */ - bool HasPendingRead() const { return mPendingIO.Has(SocketEventFlags::kRead); } - - /** - * Inside a callback function, test whether the file descriptor is ready to write. - */ - bool HasPendingWrite() const { return mPendingIO.Has(SocketEventFlags::kWrite); } - - /** - * Inside a callback function, test whether there is an exceptional condition (e.g. out-of-band data) - * associated with the file descriptor. - */ - bool HasPendingException() const { return mPendingIO.Has(SocketEventFlags::kExcept); } - - /** - * Inside a callback function, test whether there is an error condition associated with the file descriptor. - */ - bool HasPendingError() const { return mPendingIO.Has(SocketEventFlags::kError); } - - /** - * Inside a callback function, reset the set of pending events. - */ - void ClearPendingIO() { mPendingIO.ClearAll(); } - -protected: - void InvokeCallback() - { - if (mCallback != nullptr) - { - mCallback(static_cast(*this)); - } - } - - int mFD; - SocketEvents mPendingIO; - Callback mCallback; - intptr_t mCallbackData; - WatchableEventManager * mSharedState; -}; - -} // namespace System -} // namespace chip - -#define INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE 1 -#ifdef CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE -#include CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE -#else // CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE -#include -#endif // CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE -#undef INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE - -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS diff --git a/src/system/WatchableSocketLibevent.cpp b/src/system/WatchableSocketLibevent.cpp index 0913c97857432b..a420c7de069ac5 100644 --- a/src/system/WatchableSocketLibevent.cpp +++ b/src/system/WatchableSocketLibevent.cpp @@ -17,18 +17,154 @@ /** * @file - * This file implements WatchableSocket using libevent. + * This file implements WatchableEvents using libevent. */ #include #include #include -#include -#include +#include + +#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ + +namespace chip { +namespace Mdns { +void GetMdnsTimeout(timeval & timeout); +void HandleMdnsTimeout(); +} // namespace Mdns +} // namespace chip + +#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ + +#ifndef CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS +#define CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS 1 // TODO(#5556): default to off +#endif namespace chip { namespace System { +namespace { + +System::SocketEvents SocketEventsFromLibeventFlags(short eventFlags) +{ + return System::SocketEvents() + .Set(SocketEventFlags::kRead, eventFlags & EV_READ) + .Set(SocketEventFlags::kWrite, eventFlags & EV_WRITE); +} + +void TimeoutCallbackHandler(evutil_socket_t fd, short eventFlags, void * data) +{ + event * const ev = reinterpret_cast(data); + evtimer_del(ev); +} + +} // anonymous namespace + +void WatchableEventManager::Init(System::Layer & systemLayer) +{ +#if CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS + static bool enabled_event_debug_mode = false; + if (!enabled_event_debug_mode) + { + enabled_event_debug_mode = true; + event_enable_debug_mode(); + } +#endif // CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS + + mEventBase = event_base_new(); + mTimeoutEvent = evtimer_new(mEventBase, TimeoutCallbackHandler, event_self_cbarg()); + mActiveSockets = nullptr; + mSystemLayer = &systemLayer; +} + +void WatchableEventManager::PrepareEvents() +{ + // TODO(#5556): Integrate timer platform details with WatchableEventManager. + timeval nextTimeout = { 0, 0 }; + PrepareEventsWithTimeout(nextTimeout); +} + +void WatchableEventManager::PrepareEventsWithTimeout(struct timeval & nextTimeout) +{ + // TODO(#5556): Integrate timer platform details with WatchableEventManager. + mSystemLayer->GetTimeout(nextTimeout); + if (nextTimeout.tv_sec || nextTimeout.tv_usec) + { + evtimer_add(mTimeoutEvent, &nextTimeout); + } +} + +void WatchableEventManager::WaitForEvents() +{ + VerifyOrDie(mEventBase != nullptr); + event_base_loop(mEventBase, EVLOOP_ONCE); +} + +void WatchableEventManager::HandleEvents() +{ + mSystemLayer->HandleTimeout(); + +#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ + chip::Mdns::HandleMdnsTimeout(); +#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ + + while (mActiveSockets != nullptr) + { + WatchableSocket * const watcher = mActiveSockets; + mActiveSockets = watcher->mActiveNext; + watcher->InvokeCallback(); + } +} + +void WatchableEventManager::Shutdown() +{ + event_base_loopbreak(mEventBase); + event_free(mTimeoutEvent); + mTimeoutEvent = nullptr; + event_base_free(mEventBase); + mEventBase = nullptr; +} + +// static +void WatchableEventManager::LibeventCallbackHandler(evutil_socket_t fd, short eventFlags, void * data) +{ + WatchableSocket * const watcher = reinterpret_cast(data); + VerifyOrDie(watcher != nullptr); + VerifyOrDie(watcher->mFD == fd); + + watcher->mPendingIO = SocketEventsFromLibeventFlags(eventFlags); + + // Add to active list. + WatchableSocket ** pp = &watcher->mSharedState->mActiveSockets; + while (*pp != nullptr) + { + if (*pp == watcher) + { + return; + } + pp = &(*pp)->mActiveNext; + } + *pp = watcher; + watcher->mActiveNext = nullptr; +} + +void WatchableEventManager::RemoveFromQueueIfPresent(WatchableSocket * watcher) +{ + VerifyOrDie(watcher != nullptr); + VerifyOrDie(watcher->mSharedState == this); + + WatchableSocket ** pp = &mActiveSockets; + while (*pp != nullptr) + { + if (*pp == watcher) + { + *pp = watcher->mActiveNext; + return; + } + pp = &(*pp)->mActiveNext; + } +} + void WatchableSocket::OnInit() { mEvent = nullptr; @@ -40,12 +176,6 @@ void WatchableSocket::OnAttach() evutil_make_socket_nonblocking(mFD); } -void WatchableSocket::OnClose() -{ - UpdateWatch(0); - mSharedState->RemoveFromQueueIfPresent(this); -} - void WatchableSocket::SetWatch(short eventFlags) { const short oldFlags = mEvent ? event_get_events(mEvent) : 0; diff --git a/src/system/WatchableSocketLibevent.h b/src/system/WatchableSocketLibevent.h index be2448af810190..563e9d794ceeb7 100644 --- a/src/system/WatchableSocketLibevent.h +++ b/src/system/WatchableSocketLibevent.h @@ -23,8 +23,7 @@ #pragma once #if !INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE -#error "This file should only be included from " -#include +#error "This file should only be included from " #endif // !INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE #include @@ -33,14 +32,49 @@ namespace chip { namespace System { -class WatchableEventManager; +class WatchableEventManager +{ +public: + WatchableEventManager() : mActiveSockets(nullptr), mSystemLayer(nullptr), mEventBase(nullptr), mTimeoutEvent(nullptr) {} + void Init(Layer & systemLayer); + void Shutdown(); + + void EventLoopBegins() {} + void PrepareEvents(); + void WaitForEvents(); + void HandleEvents(); + void EventLoopEnds() {} + + // TODO(#5556): Some unit tests supply a timeout at low level, due to originally using select(); these should a proper timer. + void PrepareEventsWithTimeout(timeval & nextTimeout); + +private: + /* + * In this implementation, libevent invokes LibeventCallbackHandler from beneath WaitForEvents(), + * which means that the CHIP stack is unlocked. LibeventCallbackHandler adds the WatchableSocket + * to a queue (implemented as a simple intrusive list to avoid dynamic memory allocation), and + * then HandleEvents() invokes the WatchableSocket callbacks. + */ + friend class WatchableSocket; + static void LibeventCallbackHandler(evutil_socket_t fd, short eventFlags, void * data); + void RemoveFromQueueIfPresent(WatchableSocket * watcher); + WatchableSocket * mActiveSockets; ///< List of sockets activated by libevent. + + Layer * mSystemLayer; + event_base * mEventBase; ///< libevent shared state. + event * mTimeoutEvent; +}; class WatchableSocket : public WatchableSocketBasis { public: void OnInit(); void OnAttach(); - void OnClose(); + void OnClose() + { + UpdateWatch(0); + mSharedState->RemoveFromQueueIfPresent(this); + } void OnRequestCallbackOnPendingRead() { SetWatch(EV_READ); } void OnRequestCallbackOnPendingWrite() { SetWatch(EV_WRITE); } void OnClearCallbackOnPendingRead() { ClearWatch(EV_READ); } diff --git a/src/system/WatchableSocketSelect.cpp b/src/system/WatchableSocketSelect.cpp index 04f8dbb72020c8..0b28fd8c3d12d3 100644 --- a/src/system/WatchableSocketSelect.cpp +++ b/src/system/WatchableSocketSelect.cpp @@ -18,20 +18,193 @@ /** * @file - * This file implements WatchableSocket using select(). + * This file implements WatchableEvents using select(). */ #include #include #include -#include -#include +#include #include +#define DEFAULT_MIN_SLEEP_PERIOD (60 * 60 * 24 * 30) // Month [sec] + +#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ + +namespace chip { +namespace Mdns { +void GetMdnsTimeout(timeval & timeout); +void HandleMdnsTimeout(); +} // namespace Mdns +} // namespace chip + +#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ + namespace chip { namespace System { +void WatchableEventManager::Init(Layer & systemLayer) +{ + mSystemLayer = &systemLayer; + mMaxFd = -1; + FD_ZERO(&mRequest.mReadSet); + FD_ZERO(&mRequest.mWriteSet); + FD_ZERO(&mRequest.mErrorSet); +} + +void WatchableEventManager::Shutdown() +{ + mSystemLayer = nullptr; +} + +/** + * 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 WatchableEventManager::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; +} + +bool WatchableEventManager::HasAny(int fd) +{ + return FD_ISSET(fd, &mRequest.mReadSet) || FD_ISSET(fd, &mRequest.mWriteSet) || FD_ISSET(fd, &mRequest.mErrorSet); +} + +void WatchableEventManager::WakeSelect() +{ +#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD + mSystemLayer->WakeIOThread(); +#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD +} + +void WatchableEventManager::Set(int fd, fd_set * fds) +{ + FD_SET(fd, fds); + if (fd > mMaxFd) + { + mMaxFd = fd; + } + // Wake the thread calling select so that it starts selecting on the new socket. + WakeSelect(); +} + +void WatchableEventManager::Clear(int fd, fd_set * fds) +{ + FD_CLR(fd, fds); + if (fd == mMaxFd) + { + MaybeLowerMaxFd(); + } + // Wake the thread calling select so that it starts selecting on the new socket. + WakeSelect(); +} + +void WatchableEventManager::Reset(int fd) +{ + FD_CLR(fd, &mRequest.mReadSet); + FD_CLR(fd, &mRequest.mWriteSet); + FD_CLR(fd, &mRequest.mErrorSet); + if (fd == mMaxFd) + { + MaybeLowerMaxFd(); + } +} + +void WatchableEventManager::MaybeLowerMaxFd() +{ + int fd; + for (fd = mMaxFd; fd >= 0; --fd) + { + if (HasAny(fd)) + { + break; + } + } + mMaxFd = fd; +} + +void WatchableEventManager::PrepareEvents() +{ + assertChipStackLockedByCurrentThread(); + + // Max out this duration and let CHIP set it appropriately. + mNextTimeout.tv_sec = DEFAULT_MIN_SLEEP_PERIOD; + mNextTimeout.tv_usec = 0; + PrepareEventsWithTimeout(mNextTimeout); +} + +void WatchableEventManager::PrepareEventsWithTimeout(struct timeval & nextTimeout) +{ + // TODO(#5556): Integrate timer platform details with WatchableEventManager. + mSystemLayer->GetTimeout(nextTimeout); + +#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ && !__MBED__ + chip::Mdns::GetMdnsTimeout(nextTimeout); +#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ + + mSelected = mRequest; +} + +void WatchableEventManager::WaitForEvents() +{ + mSelectResult = select(mMaxFd + 1, &mSelected.mReadSet, &mSelected.mWriteSet, &mSelected.mErrorSet, &mNextTimeout); +} + +void WatchableEventManager::HandleEvents() +{ + assertChipStackLockedByCurrentThread(); + + if (mSelectResult < 0) + { + ChipLogError(DeviceLayer, "select failed: %s\n", ErrorStr(System::MapErrorPOSIX(errno))); + return; + } + + VerifyOrDie(mSystemLayer != nullptr); + mSystemLayer->HandleTimeout(); + + for (WatchableSocket * watchable = mAttachedSockets; watchable != nullptr; watchable = watchable->mAttachedNext) + { + watchable->SetPendingIO( + SocketEventsFromFDs(watchable->GetFD(), mSelected.mReadSet, mSelected.mWriteSet, mSelected.mErrorSet)); + } + for (WatchableSocket * watchable = mAttachedSockets; watchable != nullptr; watchable = watchable->mAttachedNext) + { + if (watchable->mPendingIO.HasAny()) + { + watchable->InvokeCallback(); + } + } + +#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ && !__MBED__ + chip::Mdns::HandleMdnsTimeout(); +#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ +} + void WatchableSocket::OnAttach() { mSharedState->Reset(mFD); @@ -57,30 +230,10 @@ void WatchableSocket::OnClose() pp = &(*pp)->mAttachedNext; } -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD // Wake the thread calling select so that it stops selecting on the socket. - mSharedState->Signal(); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK -} - -void WatchableSocket::OnRequestCallbackOnPendingRead() -{ - mSharedState->Set(mFD, &mSharedState->mRequest.mReadSet); -} - -void WatchableSocket::OnRequestCallbackOnPendingWrite() -{ - mSharedState->Set(mFD, &mSharedState->mRequest.mWriteSet); -} - -void WatchableSocket::OnClearCallbackOnPendingRead() -{ - mSharedState->Clear(mFD, &mSharedState->mRequest.mReadSet); -} - -void WatchableSocket::OnClearCallbackOnPendingWrite() -{ - mSharedState->Clear(mFD, &mSharedState->mRequest.mWriteSet); + mSharedState->WakeSelect(); +#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD } /** diff --git a/src/system/WatchableSocketSelect.h b/src/system/WatchableSocketSelect.h index 266ad7e69682a7..1df1e190e5a782 100644 --- a/src/system/WatchableSocketSelect.h +++ b/src/system/WatchableSocketSelect.h @@ -17,24 +17,71 @@ /** * @file - * This file declares an implementation of WatchableSocket using select(). + * This file declares an implementation of WatchableEvents using select(). */ #pragma once -#if !INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE -#error "This file should only be included from " -#include -#endif // !INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE - #include #include -#include + +#if !INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE +#error "This file should only be included from " +#endif // !INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE namespace chip { + namespace System { +class WatchableEventManager +{ +public: + void Init(System::Layer & systemLayer); + void Shutdown(); + + void EventLoopBegins() {} + void PrepareEvents(); + void WaitForEvents(); + void HandleEvents(); + void EventLoopEnds() {} + + // TODO(#5556): Some unit tests supply a timeout at low level, due to originally using select(); these should a proper timer. + void PrepareEventsWithTimeout(timeval & nextTimeout); + + static SocketEvents SocketEventsFromFDs(int socket, const fd_set & readfds, const fd_set & writefds, const fd_set & exceptfds); + +protected: + friend class WatchableSocket; + + void Set(int fd, fd_set * fds); + void Clear(int fd, fd_set * fds); + + Layer * mSystemLayer = nullptr; + WatchableSocket * mAttachedSockets = nullptr; + + // TODO(#5556): Integrate timer platform details with WatchableEventManager. + struct timeval mNextTimeout; + + // Members for select loop + struct SelectSets + { + fd_set mReadSet; + fd_set mWriteSet; + fd_set mErrorSet; + }; + SelectSets mRequest; + SelectSets mSelected; + int mMaxFd; + int mSelectResult; ///< return value from select() + +private: + bool HasAny(int fd); + void MaybeLowerMaxFd(); + void Reset(int fd); + void WakeSelect(); +}; + class WatchableSocket : public WatchableSocketBasis { public: @@ -42,10 +89,10 @@ class WatchableSocket : public WatchableSocketBasis void OnAttach(); void OnClose(); - void OnRequestCallbackOnPendingRead(); - void OnRequestCallbackOnPendingWrite(); - void OnClearCallbackOnPendingRead(); - void OnClearCallbackOnPendingWrite(); + void OnRequestCallbackOnPendingRead() { mSharedState->Set(mFD, &mSharedState->mRequest.mReadSet); } + void OnRequestCallbackOnPendingWrite() { mSharedState->Set(mFD, &mSharedState->mRequest.mWriteSet); } + void OnClearCallbackOnPendingRead() { mSharedState->Clear(mFD, &mSharedState->mRequest.mReadSet); } + void OnClearCallbackOnPendingWrite() { mSharedState->Clear(mFD, &mSharedState->mRequest.mWriteSet); } void SetPendingIO(SocketEvents events) { mPendingIO = events; } void SetFDs(int & nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds); diff --git a/src/system/tests/TestSystemWakeEvent.cpp b/src/system/tests/TestSystemWakeEvent.cpp index 4dbfd0c31ea563..19104e1cd2e93b 100644 --- a/src/system/tests/TestSystemWakeEvent.cpp +++ b/src/system/tests/TestSystemWakeEvent.cpp @@ -33,7 +33,7 @@ #include #include #include -#include +#include #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING #include @@ -42,17 +42,6 @@ using namespace chip::System; #if CHIP_SYSTEM_CONFIG_USE_SOCKETS - -namespace chip { -namespace System { -class WakeEventTest -{ -public: - static int GetReadFD(const WakeEvent & wakeEvent) { return wakeEvent.GetReadFD(); } -}; -} // namespace System -} // namespace chip - namespace { struct TestContext @@ -76,15 +65,15 @@ struct TestContext FD_ZERO(&mReadSet); FD_ZERO(&mWriteSet); FD_ZERO(&mErrorSet); - FD_SET(WakeEventTest::GetReadFD(mWakeEvent), &mReadSet); - return select(WakeEventTest::GetReadFD(mWakeEvent) + 1, &mReadSet, &mWriteSet, &mErrorSet, &timeout); + FD_SET(mWakeEvent.GetNotifFD(), &mReadSet); + return select(mWakeEvent.GetNotifFD() + 1, &mReadSet, &mWriteSet, &mErrorSet, &timeout); } }; void TestOpen(nlTestSuite * inSuite, void * aContext) { TestContext & lContext = *static_cast(aContext); - NL_TEST_ASSERT(inSuite, WakeEventTest::GetReadFD(lContext.mWakeEvent) >= 0); + NL_TEST_ASSERT(inSuite, lContext.mWakeEvent.GetNotifFD() >= 0); NL_TEST_ASSERT(inSuite, lContext.SelectWakeEvent() == 0); } @@ -96,11 +85,11 @@ void TestNotify(nlTestSuite * inSuite, void * aContext) // Check that select() succeeds after Notify() has been called lContext.mWakeEvent.Notify(); NL_TEST_ASSERT(inSuite, lContext.SelectWakeEvent() == 1); - NL_TEST_ASSERT(inSuite, FD_ISSET(WakeEventTest::GetReadFD(lContext.mWakeEvent), &lContext.mReadSet)); + NL_TEST_ASSERT(inSuite, FD_ISSET(lContext.mWakeEvent.GetNotifFD(), &lContext.mReadSet)); // ...and state of the event is not cleared automatically NL_TEST_ASSERT(inSuite, lContext.SelectWakeEvent() == 1); - NL_TEST_ASSERT(inSuite, FD_ISSET(WakeEventTest::GetReadFD(lContext.mWakeEvent), &lContext.mReadSet)); + NL_TEST_ASSERT(inSuite, FD_ISSET(lContext.mWakeEvent.GetNotifFD(), &lContext.mReadSet)); } void TestConfirm(nlTestSuite * inSuite, void * aContext) @@ -110,7 +99,7 @@ void TestConfirm(nlTestSuite * inSuite, void * aContext) // Check that select() succeeds after Notify() has been called lContext.mWakeEvent.Notify(); NL_TEST_ASSERT(inSuite, lContext.SelectWakeEvent() == 1); - NL_TEST_ASSERT(inSuite, FD_ISSET(WakeEventTest::GetReadFD(lContext.mWakeEvent), &lContext.mReadSet)); + NL_TEST_ASSERT(inSuite, FD_ISSET(lContext.mWakeEvent.GetNotifFD(), &lContext.mReadSet)); // Check that Confirm() clears state of the event lContext.mWakeEvent.Confirm(); @@ -147,7 +136,7 @@ void TestClose(nlTestSuite * inSuite, void * aContext) TestContext & lContext = *static_cast(aContext); lContext.mWakeEvent.Close(); - const auto notifFD = WakeEventTest::GetReadFD(lContext.mWakeEvent); + const auto notifFD = lContext.mWakeEvent.GetNotifFD(); // Check that Close() has cleaned up itself and reopen is possible NL_TEST_ASSERT(inSuite, lContext.mWakeEvent.Open(lContext.mWatchableEvents) == CHIP_NO_ERROR);