diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 356b5bace489ba..dff13f223e3fa9 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 81d7584cdc6f26..0919169c518ebc 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); // Thread safe due to ChipStackLock taken by App thread - 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 7d15d505cba86f..c895f216567472 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"); @@ -28,7 +28,6 @@ #include #include -#include #include #include @@ -52,13 +51,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; std::queue mChipEventQueue; @@ -115,10 +107,6 @@ class GenericPlatformManagerImpl_POSIX : public GenericPlatformManagerImpl(this); } - void SysUpdate(); - void SysProcess(); - static void SysOnEventSignal(void * arg); - void ProcessDeviceEvents(); std::atomic mShouldRunEventLoop; 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..db83fcc6bd09d3 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 @@ -334,17 +334,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 +354,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 +422,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 +468,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 +721,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 +1025,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..2574488228a5fe 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 @@ -303,14 +303,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 +327,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 +437,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 +459,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 +502,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 +511,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 +521,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 +529,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 +625,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 +696,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 +745,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 +794,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 +829,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 +876,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 +934,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 +1010,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 +1060,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 +1214,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 +1227,7 @@ bool TCPEndPoint::IsConnected(int state) void TCPEndPoint::Init(InetLayer * inetLayer) { InitEndPointBasis(*inetLayer); + ReceiveEnabled = true; // Initialize to zero for using system defaults. @@ -1264,7 +1249,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 +1386,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 +1417,7 @@ INET_ERROR TCPEndPoint::DriveSending() if (mSendQueue.IsNull()) { // Do not wait for ability to write on this endpoint. - mRequestIO.ClearWrite(); + mSocket.ClearCallbackOnPendingWrite(); } } @@ -1482,7 +1466,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 +1520,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 +1631,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 +1667,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 +2395,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 +2406,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 +2418,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 +2427,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 +2441,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 +2456,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 +2484,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 +2527,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 +2599,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 +2648,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 +2687,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 +2697,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 +2732,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..674127771a8cfd 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) { @@ -345,25 +344,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 +394,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 +908,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..dec42afcd41a4d 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,6 +201,7 @@ class DLL_EXPORT Layer #endif // CHIP_SYSTEM_CONFIG_USE_LWIP #if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + WatchableEventManager mWatchableEvents; SystemWakeEvent mWakeEvent; #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING pthread_t mHandleSelectThread; diff --git a/src/system/SystemWakeEvent.cpp b/src/system/SystemSockets.cpp similarity index 62% rename from src/system/SystemWakeEvent.cpp rename to src/system/SystemSockets.cpp index 153adee6d1bb31..2c48264153db0d 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,19 +50,31 @@ inline int SetNonBlockingMode(int fd) } } // anonymous namespace -Error SystemWakeEvent::Open() +Error SystemWakeEvent::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; } @@ -70,9 +82,9 @@ Error SystemWakeEvent::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 SystemWakeEvent::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() { 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 SystemWakeEvent::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() { - 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 SystemWakeEvent::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() { 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..a1e852619fa8bd --- /dev/null +++ b/src/system/SystemSockets.h @@ -0,0 +1,286 @@ +/* + * + * 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. */ + kError = 0x4, /**< 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 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 SystemWakeEvent +{ +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..4a277803175ea1 --- /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::kError); + } + + 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..b1c8746d35d648 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. @@ -33,7 +33,7 @@ #include #include #include -#include +#include #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING #include @@ -46,12 +46,18 @@ namespace { struct TestContext { + ::chip::System::Layer mSystemLayer; + WatchableEventManager mWatchableEvents; SystemWakeEvent 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