From 10913b20e41a56ce16a69694760af513a280ebb6 Mon Sep 17 00:00:00 2001 From: Kevin Schoedel <67607049+kpschoedel@users.noreply.github.com> Date: Fri, 18 Jun 2021 15:39:06 -0400 Subject: [PATCH] Generalize socket-based event loop (#6561) * Generalize socket-based event loop #### Problem Socket event management is currently tied closely to select(2), and does not integrate well with other event loops. See issue #5556 _rework system layer event loop_ #### Change overview The primary purpose of this change is to provide a clear interface for monitoring socket events, that can be used both within the CHIP SDK and by an application using the SDK, and can be implemented either inside or outside the SDK. Functionality does not change. * Defines an interface, in `system/SystemSockets.h`. * Converts most existing uses of socket event monitoring (except mDNS, to be done as a followup). * Converts the existing select(2)-based event management into an implementation of the new interface. * Adds a second implementation of the interface, using libevent, to verify that the interface is general enough. #### Testing * Converted the applicable unit tests from select(2) to this interface, and ran them with both the select and libevent implementations. (CI remains select-only for now.) * Integration tests involving socket-based platforms exercise this code and confirm no change to functionality. * Manual verification of controller/device communication * Clarify SocketEventFlags error vs exceptional condition * restyle * fixes for CHIP_SYSTEM_CONFIG_USE_DISPATCH merge * restyle * rename System::SystemWakeEvent to System::WakeEvent --- src/controller/CHIPDeviceController.cpp | 8 +- .../java/CHIPDeviceController-JNI.cpp | 29 +- .../GenericPlatformManagerImpl_POSIX.cpp | 109 ++----- .../GenericPlatformManagerImpl_POSIX.h | 14 +- .../GenericPlatformManagerImpl_Zephyr.cpp | 67 +--- src/inet/BUILD.gn | 3 +- src/inet/EndPointBasis.cpp | 6 +- src/inet/EndPointBasis.h | 16 +- src/inet/IPEndPointBasis.cpp | 51 +-- src/inet/InetLayer.cpp | 171 +--------- src/inet/InetLayer.h | 11 +- src/inet/InetLayerBasis.cpp | 93 ------ src/inet/InetLayerBasis.h | 119 +------ src/inet/RawEndPoint.cpp | 52 ++-- src/inet/RawEndPoint.h | 5 +- src/inet/TCPEndPoint.cpp | 163 +++++----- src/inet/TCPEndPoint.h | 3 +- src/inet/UDPEndPoint.cpp | 47 +-- src/inet/UDPEndPoint.h | 5 +- src/inet/tests/TestInetCommonPosix.cpp | 86 +---- src/platform/Linux/PlatformManagerImpl.h | 4 +- src/system/BUILD.gn | 18 +- src/system/SystemConfig.h | 8 + src/system/SystemLayer.cpp | 99 ++---- src/system/SystemLayer.h | 28 +- ...{SystemWakeEvent.cpp => SystemSockets.cpp} | 75 +++-- src/system/SystemSockets.h | 293 ++++++++++++++++++ src/system/SystemTimer.cpp | 17 +- src/system/SystemWakeEvent.h | 66 ---- src/system/WatchableSocketLibevent.cpp | 205 ++++++++++++ src/system/WatchableSocketLibevent.h | 95 ++++++ src/system/WatchableSocketSelect.cpp | 273 ++++++++++++++++ src/system/WatchableSocketSelect.h | 107 +++++++ src/system/system.gni | 5 +- src/system/tests/TestSystemTimer.cpp | 42 +-- src/system/tests/TestSystemWakeEvent.cpp | 18 +- 36 files changed, 1354 insertions(+), 1057 deletions(-) delete mode 100644 src/inet/InetLayerBasis.cpp rename src/system/{SystemWakeEvent.cpp => SystemSockets.cpp} (60%) create mode 100644 src/system/SystemSockets.h delete mode 100644 src/system/SystemWakeEvent.h create mode 100644 src/system/WatchableSocketLibevent.cpp create mode 100644 src/system/WatchableSocketLibevent.h create mode 100644 src/system/WatchableSocketSelect.cpp create mode 100644 src/system/WatchableSocketSelect.h diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index f13b4767dd27c8..a67bf4e2c4e32c 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2013-2017 Nest Labs, Inc. * All rights reserved. * @@ -501,11 +501,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.WakeSelect(); +#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 e0308cdac3f154..99d7c9e1414966 100644 --- a/src/controller/java/CHIPDeviceController-JNI.cpp +++ b/src/controller/java/CHIPDeviceController-JNI.cpp @@ -219,7 +219,7 @@ void JNI_OnUnload(JavaVM * jvm, void * reserved) if (sIOThread != PTHREAD_NULL) { sShutdown = true; - sSystemLayer.WakeSelect(); + sSystemLayer.WakeIOThread(); AndroidDeviceControllerWrapper::StackUnlockGuard unlockGuard(&sStackLock); pthread_join(sIOThread, NULL); @@ -1013,9 +1013,6 @@ void * IOThreadMain(void * arg) { JNIEnv * env; JavaVMAttachArgs attachArgs; - struct timeval sleepTime; - fd_set readFDs, writeFDs, exceptFDs; - int numFDs = 0; // Attach the IO thread to the JVM as a daemon thread. // This allows the JVM to shutdown without waiting for this thread to exit. @@ -1036,26 +1033,19 @@ void * IOThreadMain(void * arg) // Lock the stack to prevent collisions with Java threads. pthread_mutex_lock(&sStackLock); + System::WatchableEventManager & watchState = sSystemLayer.WatchableEvents(); + watchState.EventLoopBegins(); + // Loop until we are told to exit. while (!quit.load(std::memory_order_relaxed)) { - numFDs = 0; - FD_ZERO(&readFDs); - FD_ZERO(&writeFDs); - FD_ZERO(&exceptFDs); - - sleepTime.tv_sec = 10; - sleepTime.tv_usec = 0; - - // Collect the currently active file descriptors. - sSystemLayer.PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, sleepTime); - sInetLayer.PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, sleepTime); + // TODO(#5556): add a timer for `sleepTime.tv_sec = 10; sleepTime.tv_usec = 0;` + watchState.PrepareEvents(); // Unlock the stack so that Java threads can make API calls. pthread_mutex_unlock(&sStackLock); - // Wait for for I/O or for the next timer to expire. - int selectRes = select(numFDs, &readFDs, &writeFDs, &exceptFDs, &sleepTime); + watchState.WaitForEvents(); // Break the loop if requested to shutdown. // if (sShutdown) @@ -1064,10 +1054,9 @@ void * IOThreadMain(void * arg) // Re-lock the stack. pthread_mutex_lock(&sStackLock); - // Perform I/O and/or dispatch timers. - sSystemLayer.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); - sInetLayer.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); + watchState.HandleEvents(); } + watchState.EventLoopEnds(); // Detach the thread from the JVM. sJVM->DetachCurrentThread(); diff --git a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.cpp b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.cpp index 88ae6a4e6f7f06..6cf7999ce1968b 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.cpp +++ b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * 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. @@ -24,7 +24,6 @@ #ifndef GENERIC_PLATFORM_MANAGER_IMPL_POSIX_CPP #define GENERIC_PLATFORM_MANAGER_IMPL_POSIX_CPP -#include "system/SystemError.h" #include #include #include @@ -36,6 +35,7 @@ #endif #include +#include #include #include @@ -43,21 +43,10 @@ #include #include #include -#include #include -#define DEFAULT_MIN_SLEEP_PERIOD (60 * 60 * 24 * 30) // Month [sec] - -#if CHIP_DEVICE_CONFIG_ENABLE_MDNS namespace chip { -namespace Mdns { -void UpdateMdnsDataset(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet, int & maxFd, timeval & timeout); -void ProcessMdns(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet); -} // namespace Mdns -} // namespace chip -#endif -namespace chip { namespace DeviceLayer { namespace Internal { @@ -140,6 +129,8 @@ bool GenericPlatformManagerImpl_POSIX::_IsChipStackLockedByCurrentThr template CHIP_ERROR GenericPlatformManagerImpl_POSIX::_StartChipTimer(int64_t aMilliseconds) { + // TODO(#5556): Integrate timer platform details with WatchableEventManager. + // Let SystemLayer.PrepareSelect() handle timers. return CHIP_NO_ERROR; } @@ -148,7 +139,10 @@ template void GenericPlatformManagerImpl_POSIX::_PostEvent(const ChipDeviceEvent * event) { mChipEventQueue.Push(*event); - SysOnEventSignal(this); // 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 @@ -161,77 +155,6 @@ void GenericPlatformManagerImpl_POSIX::ProcessDeviceEvents() } } -template -void GenericPlatformManagerImpl_POSIX::SysOnEventSignal(void * arg) -{ - SystemLayer.WakeSelect(); -} - -template -void GenericPlatformManagerImpl_POSIX::SysUpdate() -{ - FD_ZERO(&mReadSet); - FD_ZERO(&mWriteSet); - FD_ZERO(&mErrorSet); - mMaxFd = 0; - - // Max out this duration and let CHIP set it appropriately. - mNextTimeout.tv_sec = DEFAULT_MIN_SLEEP_PERIOD; - mNextTimeout.tv_usec = 0; - - if (SystemLayer.State() == System::kLayerState_Initialized) - { - SystemLayer.PrepareSelect(mMaxFd, &mReadSet, &mWriteSet, &mErrorSet, mNextTimeout); - } - -#if !(CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK) - if (InetLayer.State == InetLayer::kState_Initialized) - { - InetLayer.PrepareSelect(mMaxFd, &mReadSet, &mWriteSet, &mErrorSet, mNextTimeout); - } -#endif // !(CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK) -#if CHIP_DEVICE_CONFIG_ENABLE_MDNS - chip::Mdns::UpdateMdnsDataset(mReadSet, mWriteSet, mErrorSet, mMaxFd, mNextTimeout); -#endif -} - -template -void GenericPlatformManagerImpl_POSIX::SysProcess() -{ - int selectRes; - int64_t nextTimeoutMs; - - nextTimeoutMs = mNextTimeout.tv_sec * 1000 + mNextTimeout.tv_usec / 1000; - _StartChipTimer(nextTimeoutMs); - - Impl()->UnlockChipStack(); - selectRes = select(mMaxFd + 1, &mReadSet, &mWriteSet, &mErrorSet, &mNextTimeout); - Impl()->LockChipStack(); - - if (selectRes < 0) - { - ChipLogError(DeviceLayer, "select failed: %s\n", ErrorStr(System::MapErrorPOSIX(errno))); - return; - } - - if (SystemLayer.State() == System::kLayerState_Initialized) - { - SystemLayer.HandleSelectResult(mMaxFd, &mReadSet, &mWriteSet, &mErrorSet); - } - -#if !(CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK) - if (InetLayer.State == InetLayer::kState_Initialized) - { - InetLayer.HandleSelectResult(mMaxFd, &mReadSet, &mWriteSet, &mErrorSet); - } -#endif // !(CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK) - - ProcessDeviceEvents(); -#if CHIP_DEVICE_CONFIG_ENABLE_MDNS - chip::Mdns::ProcessMdns(mReadSet, mWriteSet, mErrorSet); -#endif -} - template void GenericPlatformManagerImpl_POSIX::_RunEventLoop() { @@ -254,11 +177,21 @@ void GenericPlatformManagerImpl_POSIX::_RunEventLoop() Impl()->LockChipStack(); + System::WatchableEventManager & watchState = SystemLayer.WatchableEvents(); + watchState.EventLoopBegins(); do { - SysUpdate(); - SysProcess(); + watchState.PrepareEvents(); + + Impl()->UnlockChipStack(); + watchState.WaitForEvents(); + Impl()->LockChipStack(); + + watchState.HandleEvents(); + + ProcessDeviceEvents(); } while (mShouldRunEventLoop.load(std::memory_order_relaxed)); + watchState.EventLoopEnds(); Impl()->UnlockChipStack(); @@ -340,7 +273,7 @@ CHIP_ERROR GenericPlatformManagerImpl_POSIX::_StopEventLoopTask() // SystemLayer. // Impl()->LockChipStack(); - SystemLayer.WakeSelect(); + SystemLayer.WakeIOThread(); Impl()->UnlockChipStack(); pthread_mutex_lock(&mStateLock); diff --git a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.h b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.h index 5c498cc1f6a2f9..2916cb0ca789a7 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.h +++ b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2018 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,7 +29,6 @@ #include #include -#include #include #include @@ -53,13 +52,6 @@ template class GenericPlatformManagerImpl_POSIX : public GenericPlatformManagerImpl { protected: - // Members for select loop - int mMaxFd; - fd_set mReadSet; - fd_set mWriteSet; - fd_set mErrorSet; - struct timeval mNextTimeout; - // OS-specific members (pthread) pthread_mutex_t mChipStackLock; @@ -115,10 +107,6 @@ class GenericPlatformManagerImpl_POSIX : public GenericPlatformManagerImpl(this); } - void SysUpdate(); - void SysProcess(); - static void SysOnEventSignal(void * arg); - void ProcessDeviceEvents(); DeviceSafeQueue mChipEventQueue; diff --git a/src/include/platform/internal/GenericPlatformManagerImpl_Zephyr.cpp b/src/include/platform/internal/GenericPlatformManagerImpl_Zephyr.cpp index 08aeebd18f6e99..fb67c1a44fdb24 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl_Zephyr.cpp +++ b/src/include/platform/internal/GenericPlatformManagerImpl_Zephyr.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * 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. @@ -82,6 +82,8 @@ void GenericPlatformManagerImpl_Zephyr::_UnlockChipStack(void) template CHIP_ERROR GenericPlatformManagerImpl_Zephyr::_StartChipTimer(uint32_t aMilliseconds) { + // TODO(#5556): Integrate timer platform details with WatchableEventManager. + // Let SystemLayer.PrepareSelect() handle timers. return CHIP_NO_ERROR; } @@ -107,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.WakeSelect(); // Trigger wake select on CHIP thread + SystemLayer.WakeIOThread(); // Trigger wake on CHIP thread else ChipLogError(DeviceLayer, "Failed to post event to CHIP Platform event queue"); } @@ -122,64 +124,25 @@ void GenericPlatformManagerImpl_Zephyr::ProcessDeviceEvents() } template -void GenericPlatformManagerImpl_Zephyr::SysUpdate() -{ - FD_ZERO(&mReadSet); - FD_ZERO(&mWriteSet); - FD_ZERO(&mErrorSet); - mMaxFd = 0; - - // Max out this duration and let CHIP set it appropriately. - mNextTimeout.tv_sec = DEFAULT_MIN_SLEEP_PERIOD; - mNextTimeout.tv_usec = 0; - - if (SystemLayer.State() == System::kLayerState_Initialized) - { - SystemLayer.PrepareSelect(mMaxFd, &mReadSet, &mWriteSet, &mErrorSet, mNextTimeout); - } - - if (InetLayer.State == InetLayer::kState_Initialized) - { - InetLayer.PrepareSelect(mMaxFd, &mReadSet, &mWriteSet, &mErrorSet, mNextTimeout); - } -} - -template -void GenericPlatformManagerImpl_Zephyr::SysProcess() +void GenericPlatformManagerImpl_Zephyr::_RunEventLoop(void) { - Impl()->UnlockChipStack(); - int selectRes = select(mMaxFd + 1, &mReadSet, &mWriteSet, &mErrorSet, &mNextTimeout); Impl()->LockChipStack(); - if (selectRes < 0) - { - ChipLogError(DeviceLayer, "select failed: %s\n", ErrorStr(System::MapErrorPOSIX(errno))); - return; - } - - if (SystemLayer.State() == System::kLayerState_Initialized) - { - SystemLayer.HandleSelectResult(mMaxFd, &mReadSet, &mWriteSet, &mErrorSet); - } - - if (InetLayer.State == InetLayer::kState_Initialized) + System::WatchableEventManager & watchState = SystemLayer.WatchableEvents(); + watchState.EventLoopBegins(); + while (true) { - InetLayer.HandleSelectResult(mMaxFd, &mReadSet, &mWriteSet, &mErrorSet); - } + watchState.PrepareEvents(); - ProcessDeviceEvents(); -} + Impl()->UnlockChipStack(); + watchState.WaitForEvents(); + Impl()->LockChipStack(); -template -void GenericPlatformManagerImpl_Zephyr::_RunEventLoop(void) -{ - Impl()->LockChipStack(); + watchState.HandleEvents(); - while (true) - { - SysUpdate(); - SysProcess(); + ProcessDeviceEvents(); } + watchState.EventLoopEnds(); Impl()->UnlockChipStack(); } diff --git a/src/inet/BUILD.gn b/src/inet/BUILD.gn index 167f9fec9441bb..632958b4b710c3 100644 --- a/src/inet/BUILD.gn +++ b/src/inet/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2020 Project CHIP Authors +# 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. @@ -89,7 +89,6 @@ static_library("inet") { "InetInterface.h", "InetLayer.cpp", "InetLayer.h", - "InetLayerBasis.cpp", "InetLayerBasis.h", "InetLayerEvents.h", "InetUtils.cpp", diff --git a/src/inet/EndPointBasis.cpp b/src/inet/EndPointBasis.cpp index 1fe33c05b0e35e..339051bce53464 100644 --- a/src/inet/EndPointBasis.cpp +++ b/src/inet/EndPointBasis.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2015-2017 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,9 +39,7 @@ void EndPointBasis::InitEndPointBasis(InetLayer & aInetLayer, void * aAppState) #endif // CHIP_SYSTEM_CONFIG_USE_LWIP #if CHIP_SYSTEM_CONFIG_USE_SOCKETS - mSocket = INET_INVALID_SOCKET_FD; - mPendingIO.Clear(); - mRequestIO.Clear(); + mSocket.Init(aInetLayer.SystemLayer()->WatchableEvents()); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS } diff --git a/src/inet/EndPointBasis.h b/src/inet/EndPointBasis.h index f495bdef4e76dd..0baa1a8be6260c 100644 --- a/src/inet/EndPointBasis.h +++ b/src/inet/EndPointBasis.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2015-2017 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,6 +34,10 @@ #include +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS +#include +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS + #if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK #include #endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK @@ -93,11 +97,9 @@ class DLL_EXPORT EndPointBasis : public InetLayerBasis #endif #if CHIP_SYSTEM_CONFIG_USE_SOCKETS - int mSocket; /**< Encapsulated socket descriptor. */ - IPAddressType mAddrType; /**< Protocol family, i.e. IPv4 or IPv6. */ - SocketEvents mPendingIO; /**< Socket event masks (read/write/error) currently available */ - SocketEvents mRequestIO; /**< Socket event masks (read/write) to wait for */ -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS + System::WatchableSocket mSocket; /**< Encapsulated socket descriptor. */ + IPAddressType mAddrType; /**< Protocol family, i.e. IPv4 or IPv6. */ +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS #if CHIP_SYSTEM_CONFIG_USE_LWIP /** Encapsulated LwIP protocol control block */ @@ -143,7 +145,7 @@ inline bool EndPointBasis::IsNetworkFrameworkEndPoint(void) const #if CHIP_SYSTEM_CONFIG_USE_SOCKETS inline bool EndPointBasis::IsSocketsEndPoint() const { - return mSocket >= 0; + return mSocket.HasFD(); } #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS diff --git a/src/inet/IPEndPointBasis.cpp b/src/inet/IPEndPointBasis.cpp index 75f55f390d4753..4c34c06349e305 100644 --- a/src/inet/IPEndPointBasis.cpp +++ b/src/inet/IPEndPointBasis.cpp @@ -433,7 +433,7 @@ INET_ERROR IPEndPointBasis::SetMulticastLoopback(IPVersion aIPVersion, bool aLoo #endif // CHIP_SYSTEM_CONFIG_USE_LWIP #if CHIP_SYSTEM_CONFIG_USE_SOCKETS - lRetval = SocketsSetMulticastLoopback(mSocket, aIPVersion, aLoopback); + lRetval = SocketsSetMulticastLoopback(mSocket.GetFD(), aIPVersion, aLoopback); SuccessOrExit(lRetval); exit: @@ -493,7 +493,7 @@ INET_ERROR IPEndPointBasis::JoinMulticastGroup(InterfaceId aInterfaceId, const I #endif // CHIP_SYSTEM_CONFIG_USE_LWIP #if CHIP_SYSTEM_CONFIG_USE_SOCKETS - lRetval = SocketsIPv4JoinLeaveMulticastGroup(mSocket, aInterfaceId, aAddress, IP_ADD_MEMBERSHIP); + lRetval = SocketsIPv4JoinLeaveMulticastGroup(mSocket.GetFD(), aInterfaceId, aAddress, IP_ADD_MEMBERSHIP); SuccessOrExit(lRetval); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS } @@ -518,7 +518,7 @@ INET_ERROR IPEndPointBasis::JoinMulticastGroup(InterfaceId aInterfaceId, const I #endif // CHIP_SYSTEM_CONFIG_USE_LWIP #if CHIP_SYSTEM_CONFIG_USE_SOCKETS - lRetval = SocketsIPv6JoinMulticastGroup(mSocket, aInterfaceId, aAddress); + lRetval = SocketsIPv6JoinMulticastGroup(mSocket.GetFD(), aInterfaceId, aAddress); SuccessOrExit(lRetval); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS } @@ -585,7 +585,7 @@ INET_ERROR IPEndPointBasis::LeaveMulticastGroup(InterfaceId aInterfaceId, const #endif // CHIP_SYSTEM_CONFIG_USE_LWIP #if CHIP_SYSTEM_CONFIG_USE_SOCKETS - lRetval = SocketsIPv4JoinLeaveMulticastGroup(mSocket, aInterfaceId, aAddress, IP_DROP_MEMBERSHIP); + lRetval = SocketsIPv4JoinLeaveMulticastGroup(mSocket.GetFD(), aInterfaceId, aAddress, IP_DROP_MEMBERSHIP); SuccessOrExit(lRetval); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS } @@ -610,7 +610,7 @@ INET_ERROR IPEndPointBasis::LeaveMulticastGroup(InterfaceId aInterfaceId, const #endif // CHIP_SYSTEM_CONFIG_USE_LWIP #if CHIP_SYSTEM_CONFIG_USE_SOCKETS - lRetval = SocketsIPv6LeaveMulticastGroup(mSocket, aInterfaceId, aAddress); + lRetval = SocketsIPv6LeaveMulticastGroup(mSocket.GetFD(), aInterfaceId, aAddress); SuccessOrExit(lRetval); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS } @@ -737,21 +737,21 @@ INET_ERROR IPEndPointBasis::Bind(IPAddressType aAddressType, const IPAddress & a } sa.sin6_scope_id = static_cast(aInterfaceId); - if (bind(mSocket, reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) + if (bind(mSocket.GetFD(), reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) lRetval = chip::System::MapErrorPOSIX(errno); // Instruct the kernel that any messages to multicast destinations should be // sent down the interface specified by the caller. #ifdef IPV6_MULTICAST_IF if (lRetval == INET_NO_ERROR) - setsockopt(mSocket, IPPROTO_IPV6, IPV6_MULTICAST_IF, &aInterfaceId, sizeof(aInterfaceId)); + setsockopt(mSocket.GetFD(), IPPROTO_IPV6, IPV6_MULTICAST_IF, &aInterfaceId, sizeof(aInterfaceId)); #endif // defined(IPV6_MULTICAST_IF) // Instruct the kernel that any messages to multicast destinations should be // set with the configured hop limit value. #ifdef IPV6_MULTICAST_HOPS int hops = INET_CONFIG_IP_MULTICAST_HOP_LIMIT; - setsockopt(mSocket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops)); + setsockopt(mSocket.GetFD(), IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops)); #endif // defined(IPV6_MULTICAST_HOPS) } #if INET_CONFIG_ENABLE_IPV4 @@ -766,26 +766,26 @@ INET_ERROR IPEndPointBasis::Bind(IPAddressType aAddressType, const IPAddress & a sa.sin_port = htons(aPort); sa.sin_addr = aAddress.ToIPv4(); - if (bind(mSocket, reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) + if (bind(mSocket.GetFD(), reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) lRetval = chip::System::MapErrorPOSIX(errno); // Instruct the kernel that any messages to multicast destinations should be // sent down the interface to which the specified IPv4 address is bound. #ifdef IP_MULTICAST_IF if (lRetval == INET_NO_ERROR) - setsockopt(mSocket, IPPROTO_IP, IP_MULTICAST_IF, &sa, sizeof(sa)); + setsockopt(mSocket.GetFD(), IPPROTO_IP, IP_MULTICAST_IF, &sa, sizeof(sa)); #endif // defined(IP_MULTICAST_IF) // Instruct the kernel that any messages to multicast destinations should be // set with the configured hop limit value. #ifdef IP_MULTICAST_TTL int ttl = INET_CONFIG_IP_MULTICAST_HOP_LIMIT; - setsockopt(mSocket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); + setsockopt(mSocket.GetFD(), IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); #endif // defined(IP_MULTICAST_TTL) // Allow socket transmitting broadcast packets. if (lRetval == INET_NO_ERROR) - setsockopt(mSocket, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(enable)); + setsockopt(mSocket.GetFD(), SOL_SOCKET, SO_BROADCAST, &enable, sizeof(enable)); } #endif // INET_CONFIG_ENABLE_IPV4 else @@ -802,7 +802,7 @@ INET_ERROR IPEndPointBasis::BindInterface(IPAddressType aAddressType, InterfaceI if (aInterfaceId == INET_NULL_INTERFACEID) { // Stop interface-based filtering. - if (setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE, "", 0) == -1) + if (setsockopt(mSocket.GetFD(), SOL_SOCKET, SO_BINDTODEVICE, "", 0) == -1) { lRetval = chip::System::MapErrorPOSIX(errno); } @@ -818,7 +818,7 @@ INET_ERROR IPEndPointBasis::BindInterface(IPAddressType aAddressType, InterfaceI } if (lRetval == INET_NO_ERROR && - setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE, lInterfaceName, socklen_t(strlen(lInterfaceName))) == -1) + setsockopt(mSocket.GetFD(), SOL_SOCKET, SO_BINDTODEVICE, lInterfaceName, socklen_t(strlen(lInterfaceName))) == -1) { lRetval = chip::System::MapErrorPOSIX(errno); } @@ -955,7 +955,7 @@ INET_ERROR IPEndPointBasis::SendMsg(const IPPacketInfo * aPktInfo, chip::System: // Send IP packet. { - const ssize_t lenSent = sendmsg(mSocket, &msgHeader, 0); + const ssize_t lenSent = sendmsg(mSocket.GetFD(), &msgHeader, 0); if (lenSent == -1) res = chip::System::MapErrorPOSIX(errno); else if (lenSent != aBuffer->DataLength()) @@ -970,7 +970,7 @@ INET_ERROR IPEndPointBasis::GetSocket(IPAddressType aAddressType, int aType, int { INET_ERROR res = INET_NO_ERROR; - if (mSocket == INET_INVALID_SOCKET_FD) + if (!mSocket.HasFD()) { const int one = 1; int family; @@ -991,9 +991,10 @@ INET_ERROR IPEndPointBasis::GetSocket(IPAddressType aAddressType, int aType, int return INET_ERROR_WRONG_ADDRESS_TYPE; } - mSocket = ::socket(family, aType, aProtocol); - if (mSocket == -1) + const int fd = ::socket(family, aType, aProtocol); + if (fd == -1) return chip::System::MapErrorPOSIX(errno); + mSocket.Attach(fd); mAddrType = aAddressType; @@ -1006,11 +1007,11 @@ INET_ERROR IPEndPointBasis::GetSocket(IPAddressType aAddressType, int aType, int // logic up to check for implementations of these options and // to provide appropriate HAVE_xxxxx definitions accordingly. - res = setsockopt(mSocket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + res = setsockopt(mSocket.GetFD(), SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); static_cast(res); #ifdef SO_REUSEPORT - res = setsockopt(mSocket, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); + res = setsockopt(mSocket.GetFD(), SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); if (res != 0) { ChipLogError(Inet, "SO_REUSEPORT failed: %d", errno); @@ -1024,7 +1025,7 @@ INET_ERROR IPEndPointBasis::GetSocket(IPAddressType aAddressType, int aType, int #ifdef IPV6_V6ONLY if (aAddressType == kIPAddressType_IPv6) { - res = setsockopt(mSocket, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); + res = setsockopt(mSocket.GetFD(), IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); if (res != 0) { ChipLogError(Inet, "IPV6_V6ONLY failed: %d", errno); @@ -1036,7 +1037,7 @@ INET_ERROR IPEndPointBasis::GetSocket(IPAddressType aAddressType, int aType, int #ifdef IP_PKTINFO if (aAddressType == kIPAddressType_IPv4) { - res = setsockopt(mSocket, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); + res = setsockopt(mSocket.GetFD(), IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); if (res != 0) { ChipLogError(Inet, "IP_PKTINFO failed: %d", errno); @@ -1048,7 +1049,7 @@ INET_ERROR IPEndPointBasis::GetSocket(IPAddressType aAddressType, int aType, int #ifdef IPV6_RECVPKTINFO if (aAddressType == kIPAddressType_IPv6) { - res = setsockopt(mSocket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); + res = setsockopt(mSocket.GetFD(), IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); if (res != 0) { ChipLogError(Inet, "IPV6_PKTINFO failed: %d", errno); @@ -1062,7 +1063,7 @@ INET_ERROR IPEndPointBasis::GetSocket(IPAddressType aAddressType, int aType, int // SIGPIPEs on unconnected UDP sockets. #ifdef SO_NOSIGPIPE { - res = setsockopt(mSocket, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); + res = setsockopt(mSocket.GetFD(), SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); if (res != 0) { ChipLogError(Inet, "SO_NOSIGPIPE failed: %d", errno); @@ -1110,7 +1111,7 @@ void IPEndPointBasis::HandlePendingIO(uint16_t aPort) msgHeader.msg_control = controlData; msgHeader.msg_controllen = sizeof(controlData); - ssize_t rcvLen = recvmsg(mSocket, &msgHeader, MSG_DONTWAIT); + ssize_t rcvLen = recvmsg(mSocket.GetFD(), &msgHeader, MSG_DONTWAIT); if (rcvLen < 0) { diff --git a/src/inet/InetLayer.cpp b/src/inet/InetLayer.cpp index 681c769a5fe009..ddd856d7394d2f 100644 --- a/src/inet/InetLayer.cpp +++ b/src/inet/InetLayer.cpp @@ -31,12 +31,11 @@ * * UDP network transport * * Raw network transport * - * For BSD/POSIX Sockets, event readiness notification is handled - * via file descriptors and a traditional poll / select - * implementation on the platform adaptation. + * For BSD/POSIX Sockets (CHIP_SYSTEM_CONFIG_USE_SOCKETS), event readiness + * notification is handled via file descriptors, using System::WatchableSocket. * - * For LwIP, event readiness notification is handle via events / - * messages and platform- and system-specific hooks for the event + * For LwIP (CHIP_SYSTEM_CONFIG_USE_LWIP), event readiness notification is handled + * via events / messages and platform- and system-specific hooks for the event * / message system. * */ @@ -1096,168 +1095,6 @@ chip::System::Error InetLayer::HandleInetLayerEvent(chip::System::Object & aTarg #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS -/** - * Prepare the sets of file descriptors for @p select() to work with. - * - * @param[out] nfds The range of file descriptors in the file - * descriptor 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. - * - * @param[in] sleepTimeTV A pointer to a structure specifying how long the select should sleep - * - */ -void InetLayer::PrepareSelect(int & nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval & sleepTimeTV) -{ - assertChipStackLockedByCurrentThread(); - - if (State != kState_Initialized) - return; - -#if INET_CONFIG_ENABLE_RAW_ENDPOINT - for (size_t i = 0; i < RawEndPoint::sPool.Size(); i++) - { - RawEndPoint * lEndPoint = RawEndPoint::sPool.Get(*mSystemLayer, i); - if ((lEndPoint != nullptr) && lEndPoint->IsCreatedByInetLayer(*this)) - lEndPoint->mRequestIO.SetFDs(lEndPoint->mSocket, nfds, readfds, writefds, exceptfds); - } -#endif // INET_CONFIG_ENABLE_RAW_ENDPOINT - -#if INET_CONFIG_ENABLE_TCP_ENDPOINT - for (size_t i = 0; i < TCPEndPoint::sPool.Size(); i++) - { - TCPEndPoint * lEndPoint = TCPEndPoint::sPool.Get(*mSystemLayer, i); - if ((lEndPoint != nullptr) && lEndPoint->IsCreatedByInetLayer(*this)) - lEndPoint->mRequestIO.SetFDs(lEndPoint->mSocket, nfds, readfds, writefds, exceptfds); - } -#endif // INET_CONFIG_ENABLE_TCP_ENDPOINT - -#if INET_CONFIG_ENABLE_UDP_ENDPOINT - for (size_t i = 0; i < UDPEndPoint::sPool.Size(); i++) - { - UDPEndPoint * lEndPoint = UDPEndPoint::sPool.Get(*mSystemLayer, i); - if ((lEndPoint != nullptr) && lEndPoint->IsCreatedByInetLayer(*this)) - lEndPoint->mRequestIO.SetFDs(lEndPoint->mSocket, nfds, readfds, writefds, exceptfds); - } -#endif // INET_CONFIG_ENABLE_UDP_ENDPOINT -} - -/** - * Handle I/O from a select call. This method registers the pending I/O - * event in each active endpoint and then invokes the respective I/O - * handling functions for those endpoints. - * - * @note - * It is important to set the pending I/O fields for all endpoints - * *before* making any callbacks. This avoids the case where an - * endpoint is closed and then re-opened within the callback for - * another endpoint. When this happens the new endpoint is likely - * to be assigned the same file descriptor as the old endpoint. - * However, any pending I/O for that file descriptor number represents - * I/O related to the old incarnation of the endpoint, not the current - * one. Saving the pending I/O state in each endpoint before acting - * on it allows the endpoint code to clear the I/O flags in the event - * of a close, thus avoiding any confusion. - * - * @param[in] selectRes The return value of the select call. - * - * @param[in] readfds A pointer to the set of read file descriptors. - * - * @param[in] writefds A pointer to the set of write file descriptors. - * - * @param[in] exceptfds A pointer to the set of file descriptors with - * errors. - * - */ -void InetLayer::HandleSelectResult(int selectRes, fd_set * readfds, fd_set * writefds, fd_set * exceptfds) -{ - assertChipStackLockedByCurrentThread(); - - if (State != kState_Initialized) - return; - - if (selectRes < 0) - return; - - if (selectRes > 0) - { - // Set the pending I/O field for each active endpoint based on the value returned by select. -#if INET_CONFIG_ENABLE_RAW_ENDPOINT - for (size_t i = 0; i < RawEndPoint::sPool.Size(); i++) - { - RawEndPoint * lEndPoint = RawEndPoint::sPool.Get(*mSystemLayer, i); - if ((lEndPoint != nullptr) && lEndPoint->IsCreatedByInetLayer(*this)) - { - lEndPoint->mPendingIO = SocketEvents::FromFDs(lEndPoint->mSocket, readfds, writefds, exceptfds); - } - } -#endif // INET_CONFIG_ENABLE_RAW_ENDPOINT - -#if INET_CONFIG_ENABLE_TCP_ENDPOINT - for (size_t i = 0; i < TCPEndPoint::sPool.Size(); i++) - { - TCPEndPoint * lEndPoint = TCPEndPoint::sPool.Get(*mSystemLayer, i); - if ((lEndPoint != nullptr) && lEndPoint->IsCreatedByInetLayer(*this)) - { - lEndPoint->mPendingIO = SocketEvents::FromFDs(lEndPoint->mSocket, readfds, writefds, exceptfds); - } - } -#endif // INET_CONFIG_ENABLE_TCP_ENDPOINT - -#if INET_CONFIG_ENABLE_UDP_ENDPOINT - for (size_t i = 0; i < UDPEndPoint::sPool.Size(); i++) - { - UDPEndPoint * lEndPoint = UDPEndPoint::sPool.Get(*mSystemLayer, i); - if ((lEndPoint != nullptr) && lEndPoint->IsCreatedByInetLayer(*this)) - { - lEndPoint->mPendingIO = SocketEvents::FromFDs(lEndPoint->mSocket, readfds, writefds, exceptfds); - } - } -#endif // INET_CONFIG_ENABLE_UDP_ENDPOINT - - // Now call each active endpoint to handle its pending I/O. -#if INET_CONFIG_ENABLE_RAW_ENDPOINT - for (size_t i = 0; i < RawEndPoint::sPool.Size(); i++) - { - RawEndPoint * lEndPoint = RawEndPoint::sPool.Get(*mSystemLayer, i); - if ((lEndPoint != nullptr) && lEndPoint->IsCreatedByInetLayer(*this)) - { - lEndPoint->HandlePendingIO(); - } - } -#endif // INET_CONFIG_ENABLE_RAW_ENDPOINT - -#if INET_CONFIG_ENABLE_TCP_ENDPOINT - for (size_t i = 0; i < TCPEndPoint::sPool.Size(); i++) - { - TCPEndPoint * lEndPoint = TCPEndPoint::sPool.Get(*mSystemLayer, i); - if ((lEndPoint != nullptr) && lEndPoint->IsCreatedByInetLayer(*this)) - { - lEndPoint->HandlePendingIO(); - } - } -#endif // INET_CONFIG_ENABLE_TCP_ENDPOINT - -#if INET_CONFIG_ENABLE_UDP_ENDPOINT - for (size_t i = 0; i < UDPEndPoint::sPool.Size(); i++) - { - UDPEndPoint * lEndPoint = UDPEndPoint::sPool.Get(*mSystemLayer, i); - if ((lEndPoint != nullptr) && lEndPoint->IsCreatedByInetLayer(*this)) - { - lEndPoint->HandlePendingIO(); - } - } -#endif // INET_CONFIG_ENABLE_UDP_ENDPOINT - } -} - -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - /** * Reset the members of the IPPacketInfo object. * diff --git a/src/inet/InetLayer.h b/src/inet/InetLayer.h index 52793025fefada..5762f44d50d5c0 100644 --- a/src/inet/InetLayer.h +++ b/src/inet/InetLayer.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2013-2017 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -74,9 +74,11 @@ #endif // INET_CONFIG_ENABLE_UDP_ENDPOINT #if CHIP_SYSTEM_CONFIG_USE_SOCKETS + #if INET_CONFIG_ENABLE_DNS_RESOLVER && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS #include #endif // INET_CONFIG_ENABLE_DNS_RESOLVER && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS + #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS #include @@ -179,6 +181,8 @@ class DLL_EXPORT InetLayer InetLayer(); INET_ERROR Init(chip::System::Layer & aSystemLayer, void * aContext); + + // Must be called before System::Layer::Shutdown(), since this holds a pointer to that. INET_ERROR Shutdown(); chip::System::Layer * SystemLayer() const; @@ -218,11 +222,6 @@ class DLL_EXPORT InetLayer INET_ERROR GetLinkLocalAddr(InterfaceId link, IPAddress * llAddr); bool MatchLocalIPv6Subnet(const IPAddress & addr); -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - void PrepareSelect(int & nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval & sleepTime); - void HandleSelectResult(int selectRes, fd_set * readfds, fd_set * writefds, fd_set * exceptfds); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - static void UpdateSnapshot(chip::System::Stats::Snapshot & aSnapshot); void * GetPlatformData(); diff --git a/src/inet/InetLayerBasis.cpp b/src/inet/InetLayerBasis.cpp deleted file mode 100644 index 61cefcf86a1bb2..00000000000000 --- a/src/inet/InetLayerBasis.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * - * Copyright (c) 2020 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 contains the basis class for reference counting - * objects by the Inet layer as well as a class for representing - * the pending or resulting I/O events on a socket. - */ - -#include "InetLayerBasis.h" - -namespace chip { -namespace Inet { - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS -/** - * Sets the bit for the specified file descriptor in the given sets of file descriptors. - * - * @param[in] socket The file descriptor for which the bit is being set. - * - * @param[out] nfds A reference to the range of file descriptors in the 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. - * - */ -void SocketEvents::SetFDs(int socket, int & nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds) -{ - if (socket != INET_INVALID_SOCKET_FD) - { - if (IsReadable()) - FD_SET(socket, readfds); - if (IsWriteable()) - FD_SET(socket, writefds); - if (IsError()) - FD_SET(socket, exceptfds); - if (IsSet() && (socket + 1) > nfds) - nfds = socket + 1; - } -} - -/** - * 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 SocketEvents::FromFDs(int socket, fd_set * readfds, fd_set * writefds, fd_set * exceptfds) -{ - SocketEvents res; - - if (socket != INET_INVALID_SOCKET_FD) - { - if (FD_ISSET(socket, readfds)) - res.SetRead(); - if (FD_ISSET(socket, writefds)) - res.SetWrite(); - if (FD_ISSET(socket, exceptfds)) - res.SetError(); - } - - return res; -} -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - -} // namespace Inet -} // namespace chip diff --git a/src/inet/InetLayerBasis.h b/src/inet/InetLayerBasis.h index d8951ac751e98a..8ba35661fedf5b 100644 --- a/src/inet/InetLayerBasis.h +++ b/src/inet/InetLayerBasis.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2014-2017 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,10 +27,12 @@ #include +#include #include #include #include +#include #if CHIP_SYSTEM_CONFIG_USE_SOCKETS #include #endif @@ -94,121 +96,6 @@ inline void InetLayerBasis::InitInetLayerBasis(InetLayer & aInetLayer, void * aA #if CHIP_SYSTEM_CONFIG_USE_SOCKETS -/** - * @class SocketEvents - * - * @brief - * Represent a set of I/O events requested/pending on a socket. - * - */ -class SocketEvents -{ -public: - enum : uint8_t - { - kRead = 0x01, /**< Bit flag indicating if there is a read event on a socket. */ - kWrite = 0x02, /**< Bit flag indicating if there is a write event on a socket. */ - kError = 0x04, /**< Bit flag indicating if there is an error event on a socket. */ - }; - - int Value; /**< Contains the bit flags for the socket event. */ - - /** - * Constructor for the SocketEvents class. - * - */ - SocketEvents() { Value = 0; } - - /** - * Copy constructor for the SocketEvents class. - * - */ - SocketEvents(const SocketEvents & other) { Value = other.Value; } - - /** - * Copy assignment operator for the SocketEvents class. - * - */ - SocketEvents & operator=(const SocketEvents & other) = default; - - /** - * Check if any of the bit flags for the socket events are set. - * - * @return true if set, otherwise false. - * - */ - bool IsSet() const { return Value != 0; } - - /** - * Check if the bit flags indicate that the socket is readable. - * - * @return true if socket is readable, otherwise false. - * - */ - bool IsReadable() const { return (Value & kRead) != 0; } - - /** - * Check if the bit flags indicate that the socket is writable. - * - * @return true if socket is writable, otherwise false. - * - */ - bool IsWriteable() const { return (Value & kWrite) != 0; } - - /** - * Check if the bit flags indicate that the socket has an error. - * - * @return true if socket has an error, otherwise false. - * - */ - bool IsError() const { return (Value & kError) != 0; } - - /** - * Set the read bit flag for the socket. - * - */ - void SetRead() { Value |= kRead; } - - /** - * Set the write bit flag for the socket. - * - */ - void SetWrite() { Value |= kWrite; } - - /** - * Set the error bit flag for the socket. - * - */ - void SetError() { Value |= kError; } - - /** - * Clear the bit flags for the socket. - * - */ - void Clear() { Value = 0; } - - /** - * Clear the read bit flag for the socket. - * - */ - void ClearRead() { Value &= ~kRead; } - - /** - * Clear the write bit flag for the socket. - * - */ - void ClearWrite() { Value &= ~kWrite; } - - /** - * Clear the error bit flag for the socket. - * - */ - void ClearError() { Value &= ~kError; } - - void SetFDs(int socket, int & nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds); - static SocketEvents FromFDs(int socket, fd_set * readfds, fd_set * writefds, fd_set * exceptfds); -}; - /** * @def INET_INVALID_SOCKET_FD * diff --git a/src/inet/RawEndPoint.cpp b/src/inet/RawEndPoint.cpp index 2890fb2ab6031c..4fe3c1db997c7b 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 @@ -219,15 +219,13 @@ INET_ERROR RawEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, Int dispatch_queue_t dispatchQueue = SystemLayer().GetDispatchQueue(); if (dispatchQueue != nullptr) { - unsigned long fd = static_cast(mSocket); + unsigned long fd = static_cast(mSocket.GetFD()); mReadableSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatchQueue); ReturnErrorCodeIf(mReadableSource == nullptr, INET_ERROR_NO_MEMORY); dispatch_source_set_event_handler(mReadableSource, ^{ - SocketEvents res; - res.SetRead(); - this->mPendingIO = res; + this->mSocket.SetPendingIO(System::SocketEventFlags::kRead); this->HandlePendingIO(); }); @@ -334,17 +332,17 @@ INET_ERROR RawEndPoint::BindIPv6LinkLocal(InterfaceId intfId, const IPAddress & goto ret; } - if (::setsockopt(mSocket, IPPROTO_IPV6, IPV6_MULTICAST_IF, &lIfIndex, sizeof(lIfIndex)) != 0) + if (::setsockopt(mSocket.GetFD(), IPPROTO_IPV6, IPV6_MULTICAST_IF, &lIfIndex, sizeof(lIfIndex)) != 0) { goto optfail; } - if (::setsockopt(mSocket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &sInt255, sizeof(sInt255)) != 0) + if (::setsockopt(mSocket.GetFD(), IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &sInt255, sizeof(sInt255)) != 0) { goto optfail; } - if (::setsockopt(mSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &sInt255, sizeof(sInt255)) != 0) + if (::setsockopt(mSocket.GetFD(), IPPROTO_IPV6, IPV6_UNICAST_HOPS, &sInt255, sizeof(sInt255)) != 0) { goto optfail; } @@ -354,8 +352,7 @@ INET_ERROR RawEndPoint::BindIPv6LinkLocal(InterfaceId intfId, const IPAddress & optfail: res = chip::System::MapErrorPOSIX(errno); - ::close(mSocket); - mSocket = INET_INVALID_SOCKET_FD; + mSocket.Close(); mAddrType = kIPAddressType_Unknown; #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS @@ -423,16 +420,12 @@ INET_ERROR RawEndPoint::Listen(IPEndPointBasis::OnMessageReceivedFunct onMessage #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - // Wake the thread calling select so that it starts selecting on the new socket. - SystemLayer().WakeSelect(); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - mState = kState_Listening; #if CHIP_SYSTEM_CONFIG_USE_SOCKETS // Wait for ability to read on this endpoint. - mRequestIO.SetRead(); + mSocket.SetCallback(HandlePendingIO, this); + mSocket.RequestCallbackOnPendingRead(); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS return INET_NO_ERROR; @@ -473,22 +466,13 @@ void RawEndPoint::Close() #if CHIP_SYSTEM_CONFIG_USE_SOCKETS - if (mSocket != INET_INVALID_SOCKET_FD) + if (mSocket.HasFD()) { - chip::System::Layer & lSystemLayer = SystemLayer(); - - // Wake the thread calling select so that it recognizes the socket is closed. - lSystemLayer.WakeSelect(); - - close(mSocket); - mSocket = INET_INVALID_SOCKET_FD; + mSocket.Close(); } // Clear any results from select() that indicate pending I/O for the socket. - mPendingIO.Clear(); - - // Do not wait for I/O on this endpoint. - mRequestIO.Clear(); + mSocket.ClearPendingIO(); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH if (mReadableSource) @@ -735,7 +719,7 @@ INET_ERROR RawEndPoint::SetICMPFilter(uint8_t numICMPTypes, const uint8_t * aICM { ICMP6_FILTER_SETPASSALL(&filter); } - if (setsockopt(mSocket, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) == -1) + if (setsockopt(mSocket.GetFD(), IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) == -1) { return chip::System::MapErrorPOSIX(errno); } @@ -1039,16 +1023,22 @@ INET_ERROR RawEndPoint::GetSocket(IPAddressType aAddressType) return IPEndPointBasis::GetSocket(aAddressType, lType, lProtocol); } +// static +void RawEndPoint::HandlePendingIO(System::WatchableSocket & socket) +{ + static_cast(socket.GetCallbackData())->HandlePendingIO(); +} + void RawEndPoint::HandlePendingIO() { - if (mState == kState_Listening && OnMessageReceived != nullptr && mPendingIO.IsReadable()) + if (mState == kState_Listening && OnMessageReceived != nullptr && mSocket.HasPendingRead()) { const uint16_t lPort = 0; IPEndPointBasis::HandlePendingIO(lPort); } - mPendingIO.Clear(); + mSocket.ClearPendingIO(); } #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS diff --git a/src/inet/RawEndPoint.h b/src/inet/RawEndPoint.h index 9738ad81317f02..58979285e7e986 100644 --- a/src/inet/RawEndPoint.h +++ b/src/inet/RawEndPoint.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2018 Google LLC * Copyright (c) 2013-2017 Nest Labs, Inc. * @@ -113,10 +113,11 @@ class DLL_EXPORT RawEndPoint : public IPEndPointBasis #if CHIP_SYSTEM_CONFIG_USE_SOCKETS INET_ERROR GetSocket(IPAddressType addrType); void HandlePendingIO(); + static void HandlePendingIO(System::WatchableSocket & socket); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH dispatch_source_t mReadableSource = nullptr; -#endif +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS }; diff --git a/src/inet/TCPEndPoint.cpp b/src/inet/TCPEndPoint.cpp index aa9a8fa35d48f9..4b48a35ff4a1aa 100644 --- a/src/inet/TCPEndPoint.cpp +++ b/src/inet/TCPEndPoint.cpp @@ -190,7 +190,7 @@ INET_ERROR TCPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uin if (res == INET_NO_ERROR && reuseAddr) { int n = 1; - setsockopt(mSocket, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); + setsockopt(mSocket.GetFD(), SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); #ifdef SO_REUSEPORT // Enable SO_REUSEPORT. This permits coexistence between an @@ -203,7 +203,7 @@ INET_ERROR TCPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uin // e.g. two untargetted-listen CHIP clients, or two // targetted-listen CHIP clients with the same node id. - if (setsockopt(mSocket, SOL_SOCKET, SO_REUSEPORT, &n, sizeof(n)) != 0) + if (setsockopt(mSocket.GetFD(), SOL_SOCKET, SO_REUSEPORT, &n, sizeof(n)) != 0) { ChipLogError(Inet, "SO_REUSEPORT: %d", errno); } @@ -222,7 +222,7 @@ INET_ERROR TCPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uin sa.sin6_addr = addr.ToIPv6(); sa.sin6_scope_id = 0; - if (bind(mSocket, reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) + if (bind(mSocket.GetFD(), reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) res = chip::System::MapErrorPOSIX(errno); } #if INET_CONFIG_ENABLE_IPV4 @@ -234,7 +234,7 @@ INET_ERROR TCPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uin sa.sin_port = htons(port); sa.sin_addr = addr.ToIPv4(); - if (bind(mSocket, reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) + if (bind(mSocket.GetFD(), reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) res = chip::System::MapErrorPOSIX(errno); } #endif // INET_CONFIG_ENABLE_IPV4 @@ -246,7 +246,7 @@ INET_ERROR TCPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uin dispatch_queue_t dispatchQueue = SystemLayer().GetDispatchQueue(); if (dispatchQueue != nullptr) { - unsigned long fd = static_cast(mSocket); + unsigned long fd = static_cast(mSocket.GetFD()); mReadableSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatchQueue); ReturnErrorCodeIf(mReadableSource == nullptr, INET_ERROR_NO_MEMORY); @@ -255,16 +255,12 @@ INET_ERROR TCPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uin ReturnErrorCodeIf(mWriteableSource == nullptr, INET_ERROR_NO_MEMORY); dispatch_source_set_event_handler(mReadableSource, ^{ - SocketEvents events; - events.SetRead(); - this->mPendingIO = events; + this->mSocket.SetPendingIO(System::SocketEventFlags::kRead); this->HandlePendingIO(); }); dispatch_source_set_event_handler(mWriteableSource, ^{ - SocketEvents events; - events.SetWrite(); - this->mPendingIO = events; + this->mSocket.SetPendingIO(System::SocketEventFlags::kWrite); this->HandlePendingIO(); }); @@ -303,14 +299,12 @@ INET_ERROR TCPEndPoint::Listen(uint16_t backlog) #if CHIP_SYSTEM_CONFIG_USE_SOCKETS - if (listen(mSocket, backlog) != 0) + if (listen(mSocket.GetFD(), backlog) != 0) res = chip::System::MapErrorPOSIX(errno); // Wait for ability to read on this endpoint. - mRequestIO.SetRead(); - - // Wake the thread calling select so that it recognizes the new socket. - SystemLayer().WakeSelect(); + mSocket.SetCallback(HandlePendingIO, this); + mSocket.RequestCallbackOnPendingRead(); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS @@ -329,10 +323,6 @@ INET_ERROR TCPEndPoint::Connect(const IPAddress & addr, uint16_t port, Interface { INET_ERROR res = INET_NO_ERROR; -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - chip::System::Layer & lSystemLayer = SystemLayer(); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - if (State != kState_Ready && State != kState_Bound) return INET_ERROR_INCORRECT_STATE; @@ -443,7 +433,7 @@ INET_ERROR TCPEndPoint::Connect(const IPAddress & addr, uint16_t port, Interface // If the permission is denied(EACCES) because CHIP is running in a context // that does not have privileged access, choose a source address on the // interface to bind the connetion to. - int r = setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)); + int r = setsockopt(mSocket.GetFD(), SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)); if (r < 0 && errno != EACCES) { return res = chip::System::MapErrorPOSIX(errno); @@ -465,12 +455,12 @@ INET_ERROR TCPEndPoint::Connect(const IPAddress & addr, uint16_t port, Interface // Disable generation of SIGPIPE. #ifdef SO_NOSIGPIPE int n = 1; - setsockopt(mSocket, SOL_SOCKET, SO_NOSIGPIPE, &n, sizeof(n)); + setsockopt(mSocket.GetFD(), SOL_SOCKET, SO_NOSIGPIPE, &n, sizeof(n)); #endif // defined(SO_NOSIGPIPE) // Enable non-blocking mode for the socket. - int flags = fcntl(mSocket, F_GETFL, 0); - fcntl(mSocket, F_SETFL, flags | O_NONBLOCK); + int flags = fcntl(mSocket.GetFD(), F_GETFL, 0); + fcntl(mSocket.GetFD(), F_SETFL, flags | O_NONBLOCK); socklen_t sockaddrsize = 0; const sockaddr * sockaddrptr = nullptr; @@ -508,7 +498,7 @@ INET_ERROR TCPEndPoint::Connect(const IPAddress & addr, uint16_t port, Interface else return INET_ERROR_WRONG_ADDRESS_TYPE; - int conRes = connect(mSocket, sockaddrptr, sockaddrsize); + int conRes = connect(mSocket.GetFD(), sockaddrptr, sockaddrsize); if (conRes == -1 && errno != EINPROGRESS) { @@ -517,6 +507,8 @@ INET_ERROR TCPEndPoint::Connect(const IPAddress & addr, uint16_t port, Interface return res; } + mSocket.SetCallback(HandlePendingIO, this); + // Once Connecting or Connected, bump the reference count. The corresponding Release() // [or on LwIP, DeferredRelease()] will happen in DoClose(). Retain(); @@ -525,7 +517,7 @@ INET_ERROR TCPEndPoint::Connect(const IPAddress & addr, uint16_t port, Interface { State = kState_Connected; // Wait for ability to read on this endpoint. - mRequestIO.SetRead(); + mSocket.RequestCallbackOnPendingRead(); if (OnConnectComplete != nullptr) OnConnectComplete(this, INET_NO_ERROR); } @@ -533,12 +525,9 @@ INET_ERROR TCPEndPoint::Connect(const IPAddress & addr, uint16_t port, Interface { State = kState_Connecting; // Wait for ability to write on this endpoint. - mRequestIO.SetWrite(); + mSocket.RequestCallbackOnPendingWrite(); } - // Wake the thread calling select so that it recognizes the new socket. - lSystemLayer.WakeSelect(); - #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS StartConnectTimerIfSet(); @@ -632,7 +621,7 @@ INET_ERROR TCPEndPoint::GetPeerInfo(IPAddress * retAddr, uint16_t * retPort) con memset(&sa, 0, sizeof(sa)); socklen_t saLen = sizeof(sa); - if (getpeername(mSocket, &sa.any, &saLen) != 0) + if (getpeername(mSocket.GetFD(), &sa.any, &saLen) != 0) return chip::System::MapErrorPOSIX(errno); if (sa.any.sa_family == AF_INET6) @@ -703,7 +692,7 @@ INET_ERROR TCPEndPoint::GetLocalInfo(IPAddress * retAddr, uint16_t * retPort) memset(&sa, 0, sizeof(sa)); socklen_t saLen = sizeof(sa); - if (getsockname(mSocket, &sa.any, &saLen) != 0) + if (getsockname(mSocket.GetFD(), &sa.any, &saLen) != 0) return chip::System::MapErrorPOSIX(errno); if (sa.any.sa_family == AF_INET6) @@ -752,7 +741,7 @@ INET_ERROR TCPEndPoint::GetInterfaceId(InterfaceId * retInterface) memset(&sa, 0, sizeof(sa)); socklen_t saLen = sizeof(sa); - if (getpeername(mSocket, &sa.any, &saLen) != 0) + if (getpeername(mSocket.GetFD(), &sa.any, &saLen) != 0) { return chip::System::MapErrorPOSIX(errno); } @@ -801,7 +790,7 @@ INET_ERROR TCPEndPoint::Send(System::PacketBufferHandle && data, bool push) mSendQueue = std::move(data); #if CHIP_SYSTEM_CONFIG_USE_SOCKETS // Wait for ability to write on this endpoint. - mRequestIO.SetWrite(); + mSocket.RequestCallbackOnPendingWrite(); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS } else @@ -836,21 +825,14 @@ void TCPEndPoint::DisableReceive() void TCPEndPoint::EnableReceive() { -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - chip::System::Layer & lSystemLayer = SystemLayer(); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - ReceiveEnabled = true; DriveReceiving(); -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - - // Wake the thread calling select so that it can include the socket - // in the select read fd_set. - lSystemLayer.WakeSelect(); - -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD + // Wake the thread waiting for I/O so that it can include the socket. + SystemLayer().WakeIOThread(); +#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD } /** @@ -890,7 +872,7 @@ INET_ERROR TCPEndPoint::EnableNoDelay() #ifdef TCP_NODELAY // Disable TCP Nagle buffering by setting TCP_NODELAY socket option to true val = 1; - if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_NODELAY, &val, sizeof(val)) != 0) + if (setsockopt(mSocket.GetFD(), TCP_SOCKOPT_LEVEL, TCP_NODELAY, &val, sizeof(val)) != 0) return chip::System::MapErrorPOSIX(errno); #endif // defined(TCP_NODELAY) } @@ -948,22 +930,22 @@ INET_ERROR TCPEndPoint::EnableKeepAlive(uint16_t interval, uint16_t timeoutCount // Set the idle interval val = interval; - if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_IDLE_INTERVAL_OPT_NAME, &val, sizeof(val)) != 0) + if (setsockopt(mSocket.GetFD(), TCP_SOCKOPT_LEVEL, TCP_IDLE_INTERVAL_OPT_NAME, &val, sizeof(val)) != 0) return chip::System::MapErrorPOSIX(errno); // Set the probe retransmission interval. val = interval; - if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_KEEPINTVL, &val, sizeof(val)) != 0) + if (setsockopt(mSocket.GetFD(), TCP_SOCKOPT_LEVEL, TCP_KEEPINTVL, &val, sizeof(val)) != 0) return chip::System::MapErrorPOSIX(errno); // Set the probe timeout count val = timeoutCount; - if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_KEEPCNT, &val, sizeof(val)) != 0) + if (setsockopt(mSocket.GetFD(), TCP_SOCKOPT_LEVEL, TCP_KEEPCNT, &val, sizeof(val)) != 0) return chip::System::MapErrorPOSIX(errno); // Enable keepalives for the connection. val = 1; // enable - if (setsockopt(mSocket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) != 0) + if (setsockopt(mSocket.GetFD(), SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) != 0) return chip::System::MapErrorPOSIX(errno); } @@ -1024,7 +1006,7 @@ INET_ERROR TCPEndPoint::DisableKeepAlive() // Disable keepalives on the connection. val = 0; // disable - if (setsockopt(mSocket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) != 0) + if (setsockopt(mSocket.GetFD(), SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) != 0) return chip::System::MapErrorPOSIX(errno); } @@ -1074,7 +1056,7 @@ INET_ERROR TCPEndPoint::SetUserTimeout(uint32_t userTimeoutMillis) #if defined(TCP_USER_TIMEOUT) // Set the user timeout uint32_t val = userTimeoutMillis; - if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_USER_TIMEOUT, &val, sizeof(val)) != 0) + if (setsockopt(mSocket.GetFD(), TCP_SOCKOPT_LEVEL, TCP_USER_TIMEOUT, &val, sizeof(val)) != 0) return chip::System::MapErrorPOSIX(errno); #else // TCP_USER_TIMEOUT res = INET_ERROR_NOT_IMPLEMENTED; @@ -1228,9 +1210,7 @@ void TCPEndPoint::SetIdleTimeout(uint32_t timeoutMS) if (!isIdleTimerRunning && mIdleTimeout) { - chip::System::Layer & lSystemLayer = SystemLayer(); - - lSystemLayer.StartTimer(INET_TCP_IDLE_CHECK_INTERVAL, InetLayer::HandleTCPInactivityTimer, &lInetLayer); + SystemLayer().StartTimer(INET_TCP_IDLE_CHECK_INTERVAL, InetLayer::HandleTCPInactivityTimer, &lInetLayer); } } #endif // INET_TCP_IDLE_CHECK_INTERVAL > 0 @@ -1243,6 +1223,7 @@ bool TCPEndPoint::IsConnected(int state) void TCPEndPoint::Init(InetLayer * inetLayer) { InitEndPointBasis(*inetLayer); + ReceiveEnabled = true; // Initialize to zero for using system defaults. @@ -1264,7 +1245,6 @@ void TCPEndPoint::Init(InetLayer * inetLayer) #endif // INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS #if CHIP_SYSTEM_CONFIG_USE_SOCKETS - mBytesWrittenSinceLastProbe = 0; mLastTCPKernelSendQueueLen = 0; @@ -1402,7 +1382,7 @@ INET_ERROR TCPEndPoint::DriveSending() { uint16_t bufLen = mSendQueue->DataLength(); - ssize_t lenSentRaw = send(mSocket, mSendQueue->Start(), bufLen, sendFlags); + ssize_t lenSentRaw = send(mSocket.GetFD(), mSendQueue->Start(), bufLen, sendFlags); if (lenSentRaw == -1) { @@ -1433,7 +1413,7 @@ INET_ERROR TCPEndPoint::DriveSending() if (mSendQueue.IsNull()) { // Do not wait for ability to write on this endpoint. - mRequestIO.ClearWrite(); + mSocket.ClearCallbackOnPendingWrite(); } } @@ -1482,7 +1462,7 @@ INET_ERROR TCPEndPoint::DriveSending() // If we're in the SendShutdown state and the send queue is now empty, shutdown writing on the socket. if (State == kState_SendShutdown && mSendQueue.IsNull()) { - if (shutdown(mSocket, SHUT_WR) != 0) + if (shutdown(mSocket.GetFD(), SHUT_WR) != 0) err = chip::System::MapErrorPOSIX(errno); } } @@ -1536,8 +1516,8 @@ void TCPEndPoint::HandleConnectComplete(INET_ERROR err) #if CHIP_SYSTEM_CONFIG_USE_SOCKETS // Wait for ability to read or write on this endpoint. - mRequestIO.SetRead(); - mRequestIO.SetWrite(); + mSocket.RequestCallbackOnPendingRead(); + mSocket.RequestCallbackOnPendingWrite(); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS if (OnConnectComplete != nullptr) @@ -1647,32 +1627,25 @@ INET_ERROR TCPEndPoint::DoClose(INET_ERROR err, bool suppressCallback) #if CHIP_SYSTEM_CONFIG_USE_SOCKETS // If the socket hasn't been closed already... - if (mSocket != INET_INVALID_SOCKET_FD) + if (mSocket.HasFD()) { // If entering the Closed state // OR if entering the Closing state, and there's no unsent data in the send queue // THEN close the socket. if (State == kState_Closed || (State == kState_Closing && mSendQueue.IsNull())) { - chip::System::Layer & lSystemLayer = SystemLayer(); - // If aborting the connection, ensure we send a TCP RST. if (IsConnected(oldState) && err != INET_NO_ERROR) { lingerStruct.l_onoff = 1; lingerStruct.l_linger = 0; - if (setsockopt(mSocket, SOL_SOCKET, SO_LINGER, &lingerStruct, sizeof(lingerStruct)) != 0) + if (setsockopt(mSocket.GetFD(), SOL_SOCKET, SO_LINGER, &lingerStruct, sizeof(lingerStruct)) != 0) ChipLogError(Inet, "SO_LINGER: %d", errno); } - if (close(mSocket) != 0 && err == INET_NO_ERROR) + if (mSocket.Close() != 0 && err == INET_NO_ERROR) err = chip::System::MapErrorPOSIX(errno); - mSocket = INET_INVALID_SOCKET_FD; - mRequestIO.Clear(); - - // Wake the thread calling select so that it recognizes the socket is closed. - lSystemLayer.WakeSelect(); } } @@ -1690,7 +1663,7 @@ INET_ERROR TCPEndPoint::DoClose(INET_ERROR err, bool suppressCallback) #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH // Clear any results from select() that indicate pending I/O for the socket. - mPendingIO.Clear(); + mSocket.ClearPendingIO(); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS @@ -2418,7 +2391,7 @@ INET_ERROR TCPEndPoint::BindSrcAddrFromIntf(IPAddressType addrType, InterfaceId INET_ERROR TCPEndPoint::GetSocket(IPAddressType addrType) { - if (mSocket == INET_INVALID_SOCKET_FD) + if (!mSocket.HasFD()) { int family; if (addrType == kIPAddressType_IPv6) @@ -2429,9 +2402,10 @@ INET_ERROR TCPEndPoint::GetSocket(IPAddressType addrType) #endif // INET_CONFIG_ENABLE_IPV4 else return INET_ERROR_WRONG_ADDRESS_TYPE; - mSocket = ::socket(family, SOCK_STREAM | SOCK_FLAGS, 0); - if (mSocket == -1) + const int fd = ::socket(family, SOCK_STREAM | SOCK_FLAGS, 0); + if (fd == -1) return chip::System::MapErrorPOSIX(errno); + mSocket.Attach(fd); mAddrType = addrType; // If creating an IPv6 socket, tell the kernel that it will be IPv6 only. This makes it @@ -2440,7 +2414,7 @@ INET_ERROR TCPEndPoint::GetSocket(IPAddressType addrType) if (family == PF_INET6) { int one = 1; - setsockopt(mSocket, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); + setsockopt(mSocket.GetFD(), IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); } #endif // defined(IPV6_V6ONLY) @@ -2449,7 +2423,7 @@ INET_ERROR TCPEndPoint::GetSocket(IPAddressType addrType) #ifdef SO_NOSIGPIPE { int one = 1; - int res = setsockopt(mSocket, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); + int res = setsockopt(mSocket.GetFD(), SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); if (res != 0) { ChipLogError(Inet, "SO_NOSIGPIPE: %d", errno); @@ -2463,6 +2437,12 @@ INET_ERROR TCPEndPoint::GetSocket(IPAddressType addrType) return INET_NO_ERROR; } +// static +void TCPEndPoint::HandlePendingIO(System::WatchableSocket & socket) +{ + static_cast(socket.GetCallbackData())->HandlePendingIO(); +} + void TCPEndPoint::HandlePendingIO() { // Prevent the end point from being freed while in the middle of a callback. @@ -2472,20 +2452,22 @@ void TCPEndPoint::HandlePendingIO() // ready to be received on the socket, process the incoming connection. if (State == kState_Listening) { - if (OnConnectionReceived != nullptr && mPendingIO.IsReadable()) + if (OnConnectionReceived != nullptr && mSocket.HasPendingRead()) + { HandleIncomingConnection(); + } } // If in the processes of initiating a connection... else if (State == kState_Connecting) { // The socket being writable indicates the connection has completed (successfully or otherwise). - if (mPendingIO.IsWriteable()) + if (mSocket.HasPendingWrite()) { // Get the connection result from the socket. int osConRes; socklen_t optLen = sizeof(osConRes); - if (getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &osConRes, &optLen) != 0) + if (getsockopt(mSocket.GetFD(), SOL_SOCKET, SO_ERROR, &osConRes, &optLen) != 0) osConRes = errno; INET_ERROR conRes = chip::System::MapErrorPOSIX(osConRes); @@ -2498,17 +2480,17 @@ void TCPEndPoint::HandlePendingIO() { // If in a state where sending is allowed, and there is data to be sent, and the socket is ready for // writing, drive outbound data into the connection. - if (IsConnected() && !mSendQueue.IsNull() && mPendingIO.IsWriteable()) + if (IsConnected() && !mSendQueue.IsNull() && mSocket.HasPendingWrite()) DriveSending(); // If in a state were receiving is allowed, and the app is ready to receive data, and data is ready // on the socket, receive inbound data from the connection. if ((State == kState_Connected || State == kState_SendShutdown) && ReceiveEnabled && OnDataReceived != nullptr && - mPendingIO.IsReadable()) + mSocket.HasPendingRead()) ReceiveData(); } - mPendingIO.Clear(); + mSocket.ClearPendingIO(); Release(); } @@ -2541,7 +2523,7 @@ void TCPEndPoint::ReceiveData() } // Attempt to receive data from the socket. - ssize_t rcvLen = recv(mSocket, rcvBuf->Start() + rcvBuf->DataLength(), rcvBuf->AvailableDataLength(), 0); + ssize_t rcvLen = recv(mSocket.GetFD(), rcvBuf->Start() + rcvBuf->DataLength(), rcvBuf->AvailableDataLength(), 0); #if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT INET_ERROR err; @@ -2613,7 +2595,7 @@ void TCPEndPoint::ReceiveData() else State = kState_Closing; // Do not wait for ability to read on this endpoint. - mRequestIO.ClearRead(); + mSocket.ClearCallbackOnPendingRead(); // Call the app's OnPeerClose. if (OnPeerClose != nullptr) OnPeerClose(this); @@ -2662,7 +2644,7 @@ void TCPEndPoint::HandleIncomingConnection() socklen_t saLen = sizeof(sa); // Accept the new connection. - int conSocket = accept(mSocket, &sa.any, &saLen); + int conSocket = accept(mSocket.GetFD(), &sa.any, &saLen); if (conSocket == -1) err = chip::System::MapErrorPOSIX(errno); @@ -2701,8 +2683,8 @@ void TCPEndPoint::HandleIncomingConnection() if (err == INET_NO_ERROR) { // Put the new end point into the Connected state. - conEP->State = kState_Connected; - conEP->mSocket = conSocket; + conEP->mSocket.Attach(conSocket); + conEP->State = kState_Connected; #if INET_CONFIG_ENABLE_IPV4 conEP->mAddrType = (sa.any.sa_family == AF_INET6) ? kIPAddressType_IPv6 : kIPAddressType_IPv4; #else // !INET_CONFIG_ENABLE_IPV4 @@ -2711,7 +2693,8 @@ void TCPEndPoint::HandleIncomingConnection() conEP->Retain(); // Wait for ability to read on this endpoint. - conEP->mRequestIO.SetRead(); + conEP->mSocket.SetCallback(HandlePendingIO, conEP); + conEP->mSocket.RequestCallbackOnPendingRead(); // Call the app's callback function. OnConnectionReceived(this, conEP, peerAddr, peerPort); @@ -2745,7 +2728,7 @@ INET_ERROR TCPEndPoint::CheckConnectionProgress(bool & isProgressing) // Fetch the bytes pending successful transmission in the TCP out queue. - if (ioctl(mSocket, TIOCOUTQ, &currPendingBytesRaw) < 0) + if (ioctl(mSocket.GetFD(), TIOCOUTQ, &currPendingBytesRaw) < 0) { return chip::System::MapErrorPOSIX(errno); } diff --git a/src/inet/TCPEndPoint.h b/src/inet/TCPEndPoint.h index 220c7a55a2082f..c53d2c28350270 100644 --- a/src/inet/TCPEndPoint.h +++ b/src/inet/TCPEndPoint.h @@ -692,11 +692,12 @@ class DLL_EXPORT TCPEndPoint : public EndPointBasis void ReceiveData(); void HandleIncomingConnection(); INET_ERROR BindSrcAddrFromIntf(IPAddressType addrType, InterfaceId intfId); + static void HandlePendingIO(System::WatchableSocket & socket); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH dispatch_source_t mReadableSource = nullptr; dispatch_source_t mWriteableSource = nullptr; -#endif +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS }; diff --git a/src/inet/UDPEndPoint.cpp b/src/inet/UDPEndPoint.cpp index 5f404abd18a5d6..c1bd4edea63c46 100644 --- a/src/inet/UDPEndPoint.cpp +++ b/src/inet/UDPEndPoint.cpp @@ -47,7 +47,6 @@ #endif // CHIP_SYSTEM_CONFIG_USE_LWIP #if CHIP_SYSTEM_CONFIG_USE_SOCKETS -#include #if HAVE_SYS_SOCKET_H #include #endif // HAVE_SYS_SOCKET_H @@ -233,7 +232,7 @@ INET_ERROR UDPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uin } boundAddr; socklen_t boundAddrLen = sizeof(boundAddr); - if (getsockname(mSocket, &boundAddr.any, &boundAddrLen) == 0) + if (getsockname(mSocket.GetFD(), &boundAddr.any, &boundAddrLen) == 0) { if (boundAddr.any.sa_family == AF_INET) { @@ -250,15 +249,13 @@ INET_ERROR UDPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uin dispatch_queue_t dispatchQueue = SystemLayer().GetDispatchQueue(); if (dispatchQueue != nullptr) { - unsigned long fd = static_cast(mSocket); + unsigned long fd = static_cast(mSocket.GetFD()); mReadableSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatchQueue); ReturnErrorCodeIf(mReadableSource == nullptr, INET_ERROR_NO_MEMORY); dispatch_source_set_event_handler(mReadableSource, ^{ - SocketEvents res; - res.SetRead(); - this->mPendingIO = res; + this->mSocket.SetPendingIO(System::SocketEventFlags::kRead); this->HandlePendingIO(); }); dispatch_resume(mReadableSource); @@ -345,25 +342,16 @@ INET_ERROR UDPEndPoint::Listen(OnMessageReceivedFunct onMessageReceived, OnRecei #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - chip::System::Layer & lSystemLayer = SystemLayer(); - - // Wake the thread calling select so that it starts selecting on the new socket. - lSystemLayer.WakeSelect(); - -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - #if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - ReturnErrorOnFailure(StartListener()); - #endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK mState = kState_Listening; #if CHIP_SYSTEM_CONFIG_USE_SOCKETS // Wait for ability to read on this endpoint. - mRequestIO.SetRead(); + mSocket.SetCallback(HandlePendingIO, this); + mSocket.RequestCallbackOnPendingRead(); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS return INET_NO_ERROR; @@ -404,22 +392,13 @@ void UDPEndPoint::Close() #if CHIP_SYSTEM_CONFIG_USE_SOCKETS - if (mSocket != INET_INVALID_SOCKET_FD) + if (mSocket.HasFD()) { - chip::System::Layer & lSystemLayer = SystemLayer(); - - // Wake the thread calling select so that it recognizes the socket is closed. - lSystemLayer.WakeSelect(); - - close(mSocket); - mSocket = INET_INVALID_SOCKET_FD; + mSocket.Close(); } // Clear any results from select() that indicate pending I/O for the socket. - mPendingIO.Clear(); - - // Do not wait for I/O on this endpoint. - mRequestIO.Clear(); + mSocket.ClearPendingIO(); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH if (mReadableSource) @@ -927,16 +906,22 @@ INET_ERROR UDPEndPoint::GetSocket(IPAddressType aAddressType) return IPEndPointBasis::GetSocket(aAddressType, lType, lProtocol); } +// static +void UDPEndPoint::HandlePendingIO(System::WatchableSocket & socket) +{ + static_cast(socket.GetCallbackData())->HandlePendingIO(); +} + void UDPEndPoint::HandlePendingIO() { - if (mState == kState_Listening && OnMessageReceived != nullptr && mPendingIO.IsReadable()) + if (mState == kState_Listening && OnMessageReceived != nullptr && mSocket.HasPendingRead()) { const uint16_t lPort = mBoundPort; IPEndPointBasis::HandlePendingIO(lPort); } - mPendingIO.Clear(); + mSocket.ClearPendingIO(); } #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS diff --git a/src/inet/UDPEndPoint.h b/src/inet/UDPEndPoint.h index df08b38223dcc8..5747deb429d89d 100644 --- a/src/inet/UDPEndPoint.h +++ b/src/inet/UDPEndPoint.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2018 Google LLC * Copyright (c) 2013-2017 Nest Labs, Inc. * @@ -92,10 +92,11 @@ class DLL_EXPORT UDPEndPoint : public IPEndPointBasis INET_ERROR GetSocket(IPAddressType addrType); void HandlePendingIO(); + static void HandlePendingIO(System::WatchableSocket & socket); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH dispatch_source_t mReadableSource = nullptr; -#endif +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS }; diff --git a/src/inet/tests/TestInetCommonPosix.cpp b/src/inet/tests/TestInetCommonPosix.cpp index 3289afc9ea3d2e..95dd56110e4515 100644 --- a/src/inet/tests/TestInetCommonPosix.cpp +++ b/src/inet/tests/TestInetCommonPosix.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2013-2018 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -71,14 +71,13 @@ #endif // CHIP_TARGET_STYLE_UNIX #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS -#include -#include -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - using namespace chip; using namespace chip::Inet; +System::Layer gSystemLayer; + +Inet::InetLayer gInet; + #if CHIP_SYSTEM_CONFIG_USE_LWIP static sys_mbox_t * sLwIPEventQueue = NULL; static unsigned int sLwIPAcquireCount = 0; @@ -100,25 +99,16 @@ static void ReleaseLwIP(void) } #endif } -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -System::Layer gSystemLayer; -Inet::InetLayer gInet; - -#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_SOCKETS #if CHIP_TARGET_STYLE_UNIX // TapAddrAutoconf and TapInterface are only needed for LwIP on // sockets simulation in which a host tap/tun interface is used to // proxy the LwIP stack onto a host native network interface. // CollectTapAddresses() is only available on such targets. - static std::vector sTapIFs; -#endif // CHIP_TARGET_STYLE_UNIX -static std::vector sNetIFs; // interface to filter -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif // CHIP_TARGET_STYLE_UNIX -#if CHIP_SYSTEM_CONFIG_USE_LWIP +static std::vector sNetIFs; // interface to filter static bool NetworkIsReady(); static void OnLwIPInitComplete(void * arg); @@ -230,11 +220,6 @@ void InitNetwork() void * lContext = nullptr; #if CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - - tcpip_init(NULL, NULL); - -#else // !CHIP_SYSTEM_CONFIG_USE_SOCKETS // If an tap device name hasn't been specified, derive one from the IPv6 interface id. @@ -447,8 +432,6 @@ void InitNetwork() PrintNetworkState(); -#endif // !CHIP_SYSTEM_CONFIG_USE_SOCKETS - AcquireLwIP(); lContext = sLwIPEventQueue; @@ -463,7 +446,7 @@ void ServiceEvents(struct ::timeval & aSleepTime) if (!printed) { -#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_LWIP if (NetworkIsReady()) #endif { @@ -472,45 +455,18 @@ void ServiceEvents(struct ::timeval & aSleepTime) printed = true; } } -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - fd_set readFDs, writeFDs, exceptFDs; - int numFDs = 0; - - FD_ZERO(&readFDs); - FD_ZERO(&writeFDs); - FD_ZERO(&exceptFDs); #if CHIP_SYSTEM_CONFIG_USE_SOCKETS - if (gSystemLayer.State() == System::kLayerState_Initialized) - gSystemLayer.PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - if (gInet.State == InetLayer::kState_Initialized) - gInet.PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - - int selectRes = select(numFDs, &readFDs, &writeFDs, &exceptFDs, &aSleepTime); - if (selectRes < 0) - { - printf("select failed: %s\n", ErrorStr(System::MapErrorPOSIX(errno))); - return; - } + gSystemLayer.WatchableEvents().PrepareEventsWithTimeout(aSleepTime); + gSystemLayer.WatchableEvents().WaitForEvents(); + gSystemLayer.WatchableEvents().HandleEvents(); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_LWIP if (gSystemLayer.State() == System::kLayerState_Initialized) { -#if CHIP_SYSTEM_CONFIG_USE_LWIP static uint32_t sRemainingSystemLayerEventDelay = 0; -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - - gSystemLayer.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); - -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS -#if CHIP_SYSTEM_CONFIG_USE_LWIP if (gSystemLayer.State() == System::kLayerState_Initialized) { if (sRemainingSystemLayerEventDelay == 0) @@ -526,10 +482,7 @@ void ServiceEvents(struct ::timeval & aSleepTime) gSystemLayer.HandlePlatformTimer(); } -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP } - -#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_SOCKETS #if CHIP_TARGET_STYLE_UNIX // TapAddrAutoconf and TapInterface are only needed for LwIP on // sockets simulation in which a host tap/tun interface is used to @@ -538,19 +491,10 @@ void ServiceEvents(struct ::timeval & aSleepTime) TapInterface_Select(&(sTapIFs[0]), &(sNetIFs[0]), aSleepTime, gNetworkOptions.TapDeviceName.size()); #endif // CHIP_TARGET_STYLE_UNIX -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_SOCKETS - - if (gInet.State == InetLayer::kState_Initialized) - { -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - - gInet.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); - -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - } +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP } -#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_SOCKETS +#if CHIP_SYSTEM_CONFIG_USE_LWIP static bool NetworkIsReady() { bool ready = true; @@ -574,7 +518,7 @@ static void OnLwIPInitComplete(void * arg) printf("Waiting for addresses assignment...\n"); } -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_SOCKETS +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP void ShutdownNetwork() { diff --git a/src/platform/Linux/PlatformManagerImpl.h b/src/platform/Linux/PlatformManagerImpl.h index be1757c538f311..c0c82101b5e2b8 100644 --- a/src/platform/Linux/PlatformManagerImpl.h +++ b/src/platform/Linux/PlatformManagerImpl.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2018 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,6 +24,8 @@ #pragma once #include + +#include #include #if CHIP_WITH_GIO diff --git a/src/system/BUILD.gn b/src/system/BUILD.gn index c3912081a7fbce..b1c73915e28028 100644 --- a/src/system/BUILD.gn +++ b/src/system/BUILD.gn @@ -92,6 +92,10 @@ buildconfig_header("system_buildconfig") { "SYSTEM_PLATFORM_CONFIG_INCLUDE=${chip_system_platform_config_include}", ] } + + if (chip_system_config_use_sockets) { + defines += [ "CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE=" ] + } } config("system_config") { @@ -136,12 +140,12 @@ static_library("system") { "SystemObject.h", "SystemPacketBuffer.cpp", "SystemPacketBuffer.h", + "SystemSockets.cpp", + "SystemSockets.h", "SystemStats.cpp", "SystemStats.h", "SystemTimer.cpp", "SystemTimer.h", - "SystemWakeEvent.cpp", - "SystemWakeEvent.h", "TLVPacketBufferBackingStore.cpp", "TLVPacketBufferBackingStore.h", "TimeSource.h", @@ -157,6 +161,16 @@ static_library("system") { allow_circular_includes_from = [ "${chip_root}/src/lib/support" ] + if (chip_system_config_use_sockets) { + sources += [ + "WatchableSocket${chip_system_config_sockets_event_loop}.cpp", + "WatchableSocket${chip_system_config_sockets_event_loop}.h", + ] + if (chip_system_config_sockets_event_loop == "Libevent") { + libs = [ "event" ] + } + } + if (chip_with_nlfaultinjection) { sources += [ "SystemFaultInjection.cpp" ] public_deps += [ "${nlfaultinjection_root}:nlfaultinjection" ] diff --git a/src/system/SystemConfig.h b/src/system/SystemConfig.h index 54ec1a3f1692aa..16347e5f6d3a35 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 eb11df220a9ae8..3abfd7d62d12fd 100644 --- a/src/system/SystemLayer.cpp +++ b/src/system/SystemLayer.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2016-2017 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -124,13 +124,16 @@ Error Layer::Init(void * aContext) lReturn = Platform::Layer::WillInit(*this, aContext); SuccessOrExit(lReturn); +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS + mWatchableEvents.Init(*this); +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS #if CHIP_SYSTEM_CONFIG_USE_LWIP 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. - lReturn = this->mWakeEvent.Open(); + lReturn = this->mWakeEvent.Open(mWatchableEvents); SuccessOrExit(lReturn); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK @@ -169,6 +172,10 @@ Error Layer::Shutdown() } } +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS + mWatchableEvents.Shutdown(); +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS + this->mContext = nullptr; this->mLayerState = kLayerState_NotInitialized; @@ -593,37 +600,24 @@ void Layer::DispatchTimerCallbacks(const uint64_t kCurrentEpoch) #if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK -/** - * Prepare the sets of file descriptors for @p select() to work with. - * - * @param[out] aSetSize The range of file descriptors in the file descriptor set. - * @param[in] aReadSet A pointer to the set of readable file descriptors. - * @param[in] aWriteSet A pointer to the set of writable file descriptors. - * @param[in] aExceptionSet A pointer to the set of file descriptors with errors. - * @param[in] aSleepTime A reference to the maximum sleep time. - */ -void Layer::PrepareSelect(int & aSetSize, fd_set * aReadSet, fd_set * aWriteSet, fd_set * aExceptionSet, - struct timeval & aSleepTime) +bool Layer::GetTimeout(struct timeval & aSleepTime) { if (this->State() != kLayerState_Initialized) - return; - - const int wakeEventFd = this->mWakeEvent.GetNotifFD(); - FD_SET(wakeEventFd, aReadSet); - - if (wakeEventFd + 1 > aSetSize) - aSetSize = wakeEventFd + 1; + return false; const Timer::Epoch kCurrentEpoch = Timer::GetCurrentEpoch(); Timer::Epoch lAwakenEpoch = kCurrentEpoch + static_cast(aSleepTime.tv_sec) * 1000 + static_cast(aSleepTime.tv_usec) / 1000; + bool anyTimer = false; for (size_t i = 0; i < Timer::sPool.Size(); i++) { Timer * lTimer = Timer::sPool.Get(*this, i); if (lTimer != nullptr) { + anyTimer = true; + if (!Timer::IsEarlierEpoch(kCurrentEpoch, lTimer->mAwakenEpoch)) { lAwakenEpoch = kCurrentEpoch; @@ -641,6 +635,7 @@ void Layer::PrepareSelect(int & aSetSize, fd_set * aReadSet, fd_set * aWriteSet, Cancelable * ca = mTimerCallbacks.First(); if (ca != nullptr && !Timer::IsEarlierEpoch(kCurrentEpoch, ca->mInfoScalar)) { + anyTimer = true; lAwakenEpoch = ca->mInfoScalar; } } @@ -648,64 +643,20 @@ void Layer::PrepareSelect(int & aSetSize, fd_set * aReadSet, fd_set * aWriteSet, const Timer::Epoch kSleepTime = lAwakenEpoch - kCurrentEpoch; aSleepTime.tv_sec = static_cast(kSleepTime / 1000); aSleepTime.tv_usec = static_cast((kSleepTime % 1000) * 1000); + + return anyTimer; } -/** - * Handle I/O from a select call. This method registers the pending I/O event in each active endpoint and then invokes the - * respective I/O handling functions for those endpoints. - * - * @note - * It is important to set the pending I/O fields for all endpoints *before* making any callbacks. This avoids the case where an - * endpoint is closed and then re-opened within the callback for another endpoint. When this happens the new endpoint is likely - * to be assigned the same file descriptor as the old endpoint. However, any pending I/O for that file descriptor number - * represents I/O related to the old incarnation of the endpoint, not the current one. Saving the pending I/O state in each - * endpoint before acting on it allows the endpoint code to clear the I/O flags in the event of a close, thus avoiding any - * confusion. - * - * @param[in] aSetSize The return value of the select call. - * @param[in] aReadSet A pointer to the set of read file descriptors. - * @param[in] aWriteSet A pointer to the set of write file descriptors. - * @param[in] aExceptionSet A pointer to the set of file descriptors with errors. - * - */ -void Layer::HandleSelectResult(int aSetSize, fd_set * aReadSet, fd_set * aWriteSet, fd_set * aExceptionSet) +void Layer::HandleTimeout() { assertChipStackLockedByCurrentThread(); #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING - pthread_t lThreadSelf; -#endif - Error lReturn; - - if (this->State() != kLayerState_Initialized) - return; - - if (aSetSize < 0) - return; - -#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING - lThreadSelf = pthread_self(); + this->mHandleSelectThread = pthread_self(); #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING - if (aSetSize > 0) - { - // If we woke because of someone writing to the wake event, clear the event before returning. - if (FD_ISSET(this->mWakeEvent.GetNotifFD(), aReadSet)) - { - lReturn = this->mWakeEvent.Confirm(); - if (lReturn != CHIP_SYSTEM_NO_ERROR) - { - ChipLogError(chipSystemLayer, "System wake event confirm failed: %s", ErrorStr(lReturn)); - } - } - } - const Timer::Epoch kCurrentEpoch = Timer::GetCurrentEpoch(); -#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING - this->mHandleSelectThread = lThreadSelf; -#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING - for (size_t i = 0; i < Timer::sPool.Size(); i++) { Timer * lTimer = Timer::sPool.Get(*this, i); @@ -723,17 +674,21 @@ void Layer::HandleSelectResult(int aSetSize, fd_set * aReadSet, fd_set * aWriteS #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING } +#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 that monitors the file descriptors using select() by writing a single byte to the wake pipe. + * Wake up the I/O thread by writing a single byte to the wake pipe. * * @note - * If @p WakeSelect() is being called from within @p HandleSelectResult(), then writing to the wake pipe can be skipped, + * 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::WakeSelect() +void Layer::WakeIOThread() { Error lReturn; @@ -755,7 +710,7 @@ void Layer::WakeSelect() } } -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#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 b90b532cbd1100..7d146bec4c0461 100644 --- a/src/system/SystemLayer.h +++ b/src/system/SystemLayer.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2016-2017 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,9 +37,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 @@ -121,8 +119,7 @@ class LwIPEventHandlerDelegate * @brief * This provides access to timers according to the configured event handling model. * - * For \c CHIP_SYSTEM_CONFIG_USE_SOCKETS, event readiness notification is handled via traditional poll/select implementation on - * the platform adaptation. + * For \c CHIP_SYSTEM_CONFIG_USE_SOCKETS, event readiness notification is handled via WatchableEventManager. * * For \c CHIP_SYSTEM_CONFIG_USE_LWIP, event readiness notification is handle via events / messages and platform- and * system-specific hooks for the event/message system. @@ -133,6 +130,9 @@ class DLL_EXPORT Layer Layer(); Error Init(void * aContext); + + // Some other layers hold pointers to System::Layer, so care must be taken + // to ensure that they are not used after calling Shutdown(). Error Shutdown(); void * GetPlatformData() const; @@ -151,11 +151,14 @@ class DLL_EXPORT Layer Error ScheduleWork(TimerCompleteFunct aComplete, void * aAppState); -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - void PrepareSelect(int & aSetSize, fd_set * aReadSet, fd_set * aWriteSet, fd_set * aExceptionSet, struct timeval & aSleepTime); - void HandleSelectResult(int aSetSize, fd_set * aReadSet, fd_set * aWriteSet, fd_set * aExceptionSet); - void WakeSelect(); -#endif // 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 +#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD + void WakeIOThread(); +#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD #if CHIP_SYSTEM_CONFIG_USE_LWIP typedef Error (*EventHandler)(Object & aTarget, EventType aEventType, uintptr_t aArgument); @@ -198,7 +201,8 @@ class DLL_EXPORT Layer #endif // CHIP_SYSTEM_CONFIG_USE_LWIP #if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - SystemWakeEvent mWakeEvent; + WatchableEventManager mWatchableEvents; + WakeEvent mWakeEvent; #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING pthread_t mHandleSelectThread; #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING diff --git a/src/system/SystemWakeEvent.cpp b/src/system/SystemSockets.cpp similarity index 60% rename from src/system/SystemWakeEvent.cpp rename to src/system/SystemSockets.cpp index 153adee6d1bb31..a5b3368a0ee7cc 100644 --- a/src/system/SystemWakeEvent.cpp +++ b/src/system/SystemSockets.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * 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. @@ -21,7 +21,7 @@ * data stream built on top of two file descriptors. */ -#include +#include #if CHIP_SYSTEM_CONFIG_USE_SOCKETS @@ -50,29 +50,41 @@ inline int SetNonBlockingMode(int fd) } } // anonymous namespace -Error SystemWakeEvent::Open() +Error WakeEvent::Open(WatchableEventManager & watchState) { - mFDs[FD_READ] = mFDs[FD_WRITE] = -1; + enum + { + FD_READ = 0, + FD_WRITE = 1 + }; + int fds[2]; - if (::pipe(mFDs) < 0) + if (::pipe(fds) < 0) return chip::System::MapErrorPOSIX(errno); - if (SetNonBlockingMode(mFDs[FD_READ]) < 0) + if (SetNonBlockingMode(fds[FD_READ]) < 0) return chip::System::MapErrorPOSIX(errno); - if (SetNonBlockingMode(mFDs[FD_WRITE]) < 0) + if (SetNonBlockingMode(fds[FD_WRITE]) < 0) return chip::System::MapErrorPOSIX(errno); + mFD.Init(watchState); + mFD.Attach(fds[FD_READ]); + mFD.SetCallback(Confirm, this); + mFD.RequestCallbackOnPendingRead(); + + mWriteFD = fds[FD_WRITE]; + return CHIP_SYSTEM_NO_ERROR; } -Error SystemWakeEvent::Close() +Error WakeEvent::Close() { int res = 0; - res |= ::close(mFDs[FD_WRITE]); - res |= ::close(mFDs[FD_READ]); - mFDs[FD_READ] = mFDs[FD_WRITE] = -1; + res |= mFD.Close(); + res |= ::close(mWriteFD); + mWriteFD = -1; if (res < 0) { @@ -82,28 +94,27 @@ Error SystemWakeEvent::Close() return CHIP_SYSTEM_NO_ERROR; } -Error SystemWakeEvent::Confirm() +void WakeEvent::Confirm() { uint8_t buffer[128]; ssize_t res; do { - res = ::read(mFDs[FD_READ], buffer, sizeof(buffer)); + res = ::read(mFD.GetFD(), buffer, sizeof(buffer)); if (res < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { - return chip::System::MapErrorPOSIX(errno); + ChipLogError(chipSystemLayer, "System wake event confirm failed: %s", ErrorStr(chip::System::MapErrorPOSIX(errno))); + return; } } while (res == sizeof(buffer)); - - return CHIP_SYSTEM_NO_ERROR; } -Error SystemWakeEvent::Notify() +Error WakeEvent::Notify() { char byte = 1; - if (::write(mFDs[FD_WRITE], &byte, 1) < 0 && errno != EAGAIN && errno != EWOULDBLOCK) + if (::write(mWriteFD, &byte, 1) < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { return chip::System::MapErrorPOSIX(errno); } @@ -113,22 +124,26 @@ Error SystemWakeEvent::Notify() #else // CHIP_SYSTEM_CONFIG_USE_POSIX_PIPE -Error SystemWakeEvent::Open() +Error WakeEvent::Open(WatchableEventManager & watchState) { - mFD = ::eventfd(0, 0); + mFD.Init(watchState); - if (mFD == -1) + const int fd = ::eventfd(0, 0); + if (fd == -1) { return chip::System::MapErrorPOSIX(errno); } + mFD.Attach(fd); + mFD.SetCallback(Confirm, this); + mFD.RequestCallbackOnPendingRead(); + return CHIP_SYSTEM_NO_ERROR; } -Error SystemWakeEvent::Close() +Error WakeEvent::Close() { - int res = ::close(mFD); - mFD = -1; + int res = mFD.Close(); if (res < 0) { @@ -138,23 +153,21 @@ Error SystemWakeEvent::Close() return CHIP_SYSTEM_NO_ERROR; } -Error SystemWakeEvent::Confirm() +void WakeEvent::Confirm() { uint64_t value; - if (::read(mFD, &value, sizeof(value)) < 0 && errno != EAGAIN && errno != EWOULDBLOCK) + if (::read(mFD.GetFD(), &value, sizeof(value)) < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { - return chip::System::MapErrorPOSIX(errno); + ChipLogError(chipSystemLayer, "System wake event confirm failed: %s", ErrorStr(chip::System::MapErrorPOSIX(errno))); } - - return CHIP_SYSTEM_NO_ERROR; } -Error SystemWakeEvent::Notify() +Error WakeEvent::Notify() { uint64_t value = 1; - if (::write(mFD, &value, sizeof(value)) < 0 && errno != EAGAIN && errno != EWOULDBLOCK) + if (::write(mFD.GetFD(), &value, sizeof(value)) < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { return chip::System::MapErrorPOSIX(errno); } diff --git a/src/system/SystemSockets.h b/src/system/SystemSockets.h new file mode 100644 index 00000000000000..b8aa4415213231 --- /dev/null +++ b/src/system/SystemSockets.h @@ -0,0 +1,293 @@ +/* + * + * 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 + +#include + +namespace chip { + +namespace System { + +class Layer; + +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 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 two methods: + * + * void Init(System::Layer & systemLayer) -- called from System::Layer::Init() + * void Shutdown() -- called from System::Layer::Shutdown() + * + * 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; + +/** + * @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). + */ + void Init(WatchableEventManager & manager) + { + mFD = kInvalidFd; + mPendingIO.ClearAll(); + mCallback = nullptr; + mCallbackData = nullptr; + mSharedState = &manager; + static_cast(this)->OnInit(); + } + + /** + * Associate this WatchableSocket with a file descriptor. + * + * @param[in] fd An open file descriptor. + */ + void Attach(int fd) + { + mFD = fd; + static_cast(this)->OnAttach(); + } + + /** + * Close the associated file descriptor. + * + * @returns the return value of `close()`. + */ + int Close() + { + static_cast(this)->OnClose(); + const int r = close(mFD); + mFD = kInvalidFd; + return r; + } + + /** + * 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. + */ + void RequestCallbackOnPendingRead() { static_cast(this)->OnRequestCallbackOnPendingRead(); } + + /** + * Indicate that the socket-event system should invoke the registered callback when the file descriptor is ready to write. + */ + void RequestCallbackOnPendingWrite() { static_cast(this)->OnRequestCallbackOnPendingWrite(); } + + /** + * Indicate that the socket-event system need not invoke the registered callback when the file descriptor is ready to read. + */ + void ClearCallbackOnPendingRead() { static_cast(this)->OnClearCallbackOnPendingRead(); } + + /** + * Indicate that the socket-event system need not invoke the registered callback when the file descriptor is ready to write. + */ + void ClearCallbackOnPendingWrite() { static_cast(this)->OnClearCallbackOnPendingWrite(); } + + /** + * 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 pointer accessible within a callback function. + */ + void SetCallback(Callback callback, void * data) + { + mCallback = callback; + mCallbackData = data; + } + + /** + * Retrieve callback data. + * + * @returns the pointer supplied to SetCallback(). + */ + void * GetCallbackData() const { return mCallbackData; } + + /** + * 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; + void * 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 + +namespace chip { +namespace System { + +using ::chip::System::Error; + +class WakeEvent +{ +public: + Error Open(WatchableEventManager & watchState); /**< Initialize the pipeline */ + Error Close(); /**< Close both ends of the pipeline. */ + + int GetNotifFD() const { return mFD.GetFD(); } + + Error Notify(); /**< Set the event. */ + void Confirm(); /**< Clear the event. */ + static void Confirm(WatchableSocket & socket) { static_cast(socket.GetCallbackData())->Confirm(); } + +private: +#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/SystemTimer.cpp b/src/system/SystemTimer.cpp index 5c8ea60f9cf614..382aadc1762def 100644 --- a/src/system/SystemTimer.cpp +++ b/src/system/SystemTimer.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2016-2017 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -168,6 +168,7 @@ Error Timer::Start(uint32_t aDelayMilliseconds, OnCompleteFunct aOnComplete, voi this->mNextTimer = lTimer->mNextTimer; lTimer->mNextTimer = this; } + return CHIP_SYSTEM_NO_ERROR; #endif // CHIP_SYSTEM_CONFIG_USE_LWIP #if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK @@ -190,14 +191,13 @@ Error Timer::Start(uint32_t aDelayMilliseconds, OnCompleteFunct aOnComplete, voi this->HandleComplete(); }); dispatch_resume(timerSource); - } - else - { -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH - lLayer.WakeSelect(); -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH + return CHIP_SYSTEM_NO_ERROR; } #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH + +#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_SYSTEM_NO_ERROR; @@ -218,6 +218,7 @@ Error Timer::ScheduleWork(OnCompleteFunct aOnComplete, void * aAppState) #if CHIP_SYSTEM_CONFIG_USE_LWIP err = lLayer.PostEvent(*this, chip::System::kEvent_ScheduleWork, 0); #endif // CHIP_SYSTEM_CONFIG_USE_LWIP + #if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK #if CHIP_SYSTEM_CONFIG_USE_DISPATCH dispatch_queue_t dispatchQueue = lLayer.GetDispatchQueue(); @@ -230,7 +231,7 @@ Error Timer::ScheduleWork(OnCompleteFunct aOnComplete, void * aAppState) else { #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH - lLayer.WakeSelect(); + lLayer.WakeIOThread(); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH } #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH diff --git a/src/system/SystemWakeEvent.h b/src/system/SystemWakeEvent.h deleted file mode 100644 index bdbe9a892a1b3d..00000000000000 --- a/src/system/SystemWakeEvent.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * - * Copyright (c) 2020 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 system wake event used for - * resuming task from select system call. - */ - -#pragma once - -// Include configuration headers -#include - -#include - -namespace chip { -namespace System { - -using ::chip::System::Error; - -class SystemWakeEvent -{ -public: - Error Open(); /**< Initialize the pipeline */ - Error Close(); /**< Close both ends of the pipeline. */ - -#if CHIP_SYSTEM_CONFIG_USE_POSIX_PIPE - int GetNotifFD() const { return mFDs[FD_READ]; } -#else - int GetNotifFD() const { return mFD; } -#endif - - Error Notify(); /**< Set the event. */ - Error Confirm(); /**< Clear the event. */ - -private: -#if CHIP_SYSTEM_CONFIG_USE_POSIX_PIPE - enum - { - FD_READ = 0, - FD_WRITE = 1 - }; - - int mFDs[2]; -#else - int mFD; -#endif -}; - -} // namespace System -} // namespace chip diff --git a/src/system/WatchableSocketLibevent.cpp b/src/system/WatchableSocketLibevent.cpp new file mode 100644 index 00000000000000..8813ff3b736086 --- /dev/null +++ b/src/system/WatchableSocketLibevent.cpp @@ -0,0 +1,205 @@ +/* + * + * 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 WatchableEvents using libevent. + */ + +#include +#include +#include + +#if CHIP_DEVICE_CONFIG_ENABLE_MDNS +// TODO(#5556): Convert MDNS to WatchableSocket. +#error "POSIX platform with MDNS currently must use select()" +#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS + +#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(); + + 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; + mActiveNext = nullptr; +} + +void WatchableSocket::OnAttach() +{ + evutil_make_socket_nonblocking(mFD); +} + +void WatchableSocket::SetWatch(short eventFlags) +{ + const short oldFlags = mEvent ? event_get_events(mEvent) : 0; + const short newFlags = static_cast(EV_PERSIST | oldFlags | eventFlags); + if (oldFlags != newFlags) + { + UpdateWatch(newFlags); + } +} + +void WatchableSocket::ClearWatch(short eventFlags) +{ + const short oldFlags = mEvent ? event_get_events(mEvent) : 0; + const short newFlags = static_cast(EV_PERSIST | (oldFlags & ~eventFlags)); + if (oldFlags != newFlags) + { + UpdateWatch(newFlags); + } +} + +void WatchableSocket::UpdateWatch(short eventFlags) +{ + if (mEvent) + { + event_del(mEvent); + event_free(mEvent); + mEvent = nullptr; + } + if (eventFlags) + { + event_base * const base = mSharedState->mEventBase; + mEvent = event_new(base, mFD, eventFlags, WatchableEventManager::LibeventCallbackHandler, this); + event_add(mEvent, nullptr); + } +} + +} // namespace System +} // namespace chip diff --git a/src/system/WatchableSocketLibevent.h b/src/system/WatchableSocketLibevent.h new file mode 100644 index 00000000000000..563e9d794ceeb7 --- /dev/null +++ b/src/system/WatchableSocketLibevent.h @@ -0,0 +1,95 @@ +/* + * + * 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_SOCKET_CONFIG_FILE +#error "This file should only be included from " +#endif // !INCLUDING_CHIP_SYSTEM_WATCHABLE_SOCKET_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 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() + { + UpdateWatch(0); + mSharedState->RemoveFromQueueIfPresent(this); + } + void OnRequestCallbackOnPendingRead() { SetWatch(EV_READ); } + void OnRequestCallbackOnPendingWrite() { SetWatch(EV_WRITE); } + void OnClearCallbackOnPendingRead() { ClearWatch(EV_READ); } + void OnClearCallbackOnPendingWrite() { ClearWatch(EV_WRITE); } + +private: + friend class WatchableEventManager; + + void SetWatch(short eventFlags); + void ClearWatch(short eventFlags); + void UpdateWatch(short eventFlags); + + WatchableSocket * mActiveNext; ///< Next element in the list of sockets activated by libevent. + struct event * mEvent; ///< libevent state. +}; + +} // namespace System +} // namespace chip diff --git a/src/system/WatchableSocketSelect.cpp b/src/system/WatchableSocketSelect.cpp new file mode 100644 index 00000000000000..c90878bba8cf4c --- /dev/null +++ b/src/system/WatchableSocketSelect.cpp @@ -0,0 +1,273 @@ +/* + * + * 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 WatchableEvents using select(). + */ + +#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 { +// TODO(#5556): Convert MDNS to WatchableSocket. +void UpdateMdnsDataset(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet, int & maxFd, timeval & timeout); +void ProcessMdns(fd_set & readFdSet, fd_set & writeFdSet, fd_set & errorFdSet); +} // 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); + + mSelected = mRequest; + +#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ + // TODO(#5556): Convert MDNS to WatchableSocket. + chip::Mdns::UpdateMdnsDataset(mSelected.mReadSet, mSelected.mWriteSet, mSelected.mErrorSet, mMaxFd, nextTimeout); +#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ +} + +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__ + // TODO(#5556): Convert MDNS to WatchableSocket. + chip::Mdns::ProcessMdns(mSelected.mReadSet, mSelected.mWriteSet, mSelected.mErrorSet); +#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ +} + +void WatchableSocket::OnAttach() +{ + mSharedState->Reset(mFD); + + VerifyOrDie(mAttachedNext == nullptr); + mAttachedNext = mSharedState->mAttachedSockets; + mSharedState->mAttachedSockets = this; +} + +void WatchableSocket::OnClose() +{ + VerifyOrDie(mFD >= 0); + mSharedState->Reset(mFD); + + WatchableSocket ** pp = &mSharedState->mAttachedSockets; + while (*pp != nullptr) + { + if (*pp == this) + { + *pp = this->mAttachedNext; + break; + } + pp = &(*pp)->mAttachedNext; + } + +#if CHIP_SYSTEM_CONFIG_USE_IO_THREAD + // Wake the thread calling select so that it stops selecting on the socket. + mSharedState->WakeSelect(); +#endif // CHIP_SYSTEM_CONFIG_USE_IO_THREAD +} + +/** + * Sets the bit for the specified file descriptor in the given sets of file descriptors. + * + * @param[out] nfds A reference to the range of file descriptors in the 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. + * + */ +void WatchableSocket::SetFDs(int & nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds) +{ + if (mFD >= 0) + { + int r = FD_ISSET(mFD, &mSharedState->mRequest.mReadSet); + int w = FD_ISSET(mFD, &mSharedState->mRequest.mWriteSet); + int e = FD_ISSET(mFD, &mSharedState->mRequest.mErrorSet); + if (r) + FD_SET(mFD, readfds); + if (w) + FD_SET(mFD, writefds); + if (e) + FD_SET(mFD, exceptfds); + if ((r || w || e) && mFD >= nfds) + nfds = mFD + 1; + } +} + +} // namespace System +} // namespace chip diff --git a/src/system/WatchableSocketSelect.h b/src/system/WatchableSocketSelect.h new file mode 100644 index 00000000000000..1df1e190e5a782 --- /dev/null +++ b/src/system/WatchableSocketSelect.h @@ -0,0 +1,107 @@ +/* + * + * 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 select(). + */ + +#pragma once + +#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: + void OnInit() { mAttachedNext = nullptr; } + void OnAttach(); + void OnClose(); + + 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); + +private: + friend class WatchableEventManager; + + WatchableSocket * mAttachedNext; ///< Next element in the list of sockets attached to the WatchableEventManager. +}; + +} // namespace System +} // namespace chip diff --git a/src/system/system.gni b/src/system/system.gni index 1e5c4347ba9d60..00c2f2f50e3d3e 100644 --- a/src/system/system.gni +++ b/src/system/system.gni @@ -1,4 +1,4 @@ -# Copyright (c) 2020 Project CHIP Authors +# 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. @@ -23,6 +23,9 @@ declare_args() { # Use BSD/POSIX socket API. chip_system_config_use_sockets = current_os != "freertos" + # Socket event loop type. + chip_system_config_sockets_event_loop = "Select" + # Mutex implementation: posix, freertos, none. chip_system_config_locking = "" diff --git a/src/system/tests/TestSystemTimer.cpp b/src/system/tests/TestSystemTimer.cpp index d8eb5730a1bfbe..6adbcebcee2f9c 100644 --- a/src/system/tests/TestSystemTimer.cpp +++ b/src/system/tests/TestSystemTimer.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2016-2017 Nest Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -43,10 +43,6 @@ #include #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK -#include -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - #include #include #include @@ -57,39 +53,19 @@ using namespace chip::System; static void ServiceEvents(Layer & aLayer, ::timeval & aSleepTime) { #if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - fd_set readFDs, writeFDs, exceptFDs; - int numFDs = 0; - - FD_ZERO(&readFDs); - FD_ZERO(&writeFDs); - FD_ZERO(&exceptFDs); - - if (aLayer.State() == kLayerState_Initialized) - aLayer.PrepareSelect(numFDs, &readFDs, &writeFDs, &exceptFDs, aSleepTime); - - int selectRes = select(numFDs, &readFDs, &writeFDs, &exceptFDs, &aSleepTime); - if (selectRes < 0) - { - printf("select failed: %s\n", ErrorStr(MapErrorPOSIX(errno))); - return; - } + aLayer.WatchableEvents().PrepareEventsWithTimeout(aSleepTime); + aLayer.WatchableEvents().WaitForEvents(); + aLayer.WatchableEvents().HandleEvents(); #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#if CHIP_SYSTEM_CONFIG_USE_LWIP if (aLayer.State() == kLayerState_Initialized) { -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - aLayer.HandleSelectResult(selectRes, &readFDs, &writeFDs, &exceptFDs); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - -#if CHIP_SYSTEM_CONFIG_USE_LWIP - if (aLayer.State() == kLayerState_Initialized) - { - // TODO: Currently timers are delayed by aSleepTime above. A improved solution would have a mechanism to reduce - // aSleepTime according to the next timer. - aLayer.HandlePlatformTimer(); - } -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + // TODO: Currently timers are delayed by aSleepTime above. A improved solution would have a mechanism to reduce + // aSleepTime according to the next timer. + aLayer.HandlePlatformTimer(); } +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP } // Test input vector format. diff --git a/src/system/tests/TestSystemWakeEvent.cpp b/src/system/tests/TestSystemWakeEvent.cpp index 375fefe1a246b1..4fecf03207f47d 100644 --- a/src/system/tests/TestSystemWakeEvent.cpp +++ b/src/system/tests/TestSystemWakeEvent.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2020 Project CHIP Authors + * 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. @@ -17,7 +17,7 @@ /** * @file - * This is a unit test suite for chip::System::SystemWakeEvent + * This is a unit test suite for chip::System::WakeEvent * */ @@ -33,7 +33,7 @@ #include #include #include -#include +#include #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING #include @@ -46,12 +46,18 @@ namespace { struct TestContext { - SystemWakeEvent mWakeEvent; + ::chip::System::Layer mSystemLayer; + WatchableEventManager mWatchableEvents; + WakeEvent mWakeEvent; fd_set mReadSet; fd_set mWriteSet; fd_set mErrorSet; - TestContext() { mWakeEvent.Open(); } + TestContext() + { + mWatchableEvents.Init(mSystemLayer); + mWakeEvent.Open(mWatchableEvents); + } ~TestContext() { mWakeEvent.Close(); } int SelectWakeEvent(timeval timeout = {}) @@ -133,7 +139,7 @@ void TestClose(nlTestSuite * inSuite, void * aContext) const auto notifFD = lContext.mWakeEvent.GetNotifFD(); // Check that Close() has cleaned up itself and reopen is possible - NL_TEST_ASSERT(inSuite, lContext.mWakeEvent.Open() == CHIP_SYSTEM_NO_ERROR); + NL_TEST_ASSERT(inSuite, lContext.mWakeEvent.Open(lContext.mWatchableEvents) == CHIP_SYSTEM_NO_ERROR); NL_TEST_ASSERT(inSuite, notifFD < 0); } } // namespace