diff --git a/examples/chip-tool/commands/common/Command.cpp b/examples/chip-tool/commands/common/Command.cpp index a0b3d5a6380a3a..015ade990baca6 100644 --- a/examples/chip-tool/commands/common/Command.cpp +++ b/examples/chip-tool/commands/common/Command.cpp @@ -422,16 +422,10 @@ static void OnResponseTimeout(chip::System::Layer *, void *, CHIP_ERROR) CHIP_ERROR Command::ScheduleWaitForResponse(uint16_t seconds) { - chip::System::Timer * timer = nullptr; - - CHIP_ERROR err = chip::DeviceLayer::SystemLayer.NewTimer(timer); - if (err == CHIP_NO_ERROR) - { - timer->Start(seconds * 1000, OnResponseTimeout, this); - } - else + CHIP_ERROR err = chip::DeviceLayer::SystemLayer.StartTimer(seconds * 1000, OnResponseTimeout, this); + if (err != CHIP_NO_ERROR) { - ChipLogError(chipTool, "Failed to allocate timer"); + ChipLogError(chipTool, "Failed to allocate timer %" CHIP_ERROR_FORMAT, chip::ChipError::FormatError(err)); } return err; } diff --git a/examples/minimal-mdns/client.cpp b/examples/minimal-mdns/client.cpp index b1ad74d45ed3bd..fe3f62ee3c2fac 100644 --- a/examples/minimal-mdns/client.cpp +++ b/examples/minimal-mdns/client.cpp @@ -327,22 +327,16 @@ int main(int argc, char ** args) BroadcastPacket(&mdnsServer); - System::Timer * timer = nullptr; - - if (DeviceLayer::SystemLayer.NewTimer(timer) == CHIP_NO_ERROR) - { - timer->Start( - gOptions.runtimeMs, - [](System::Layer *, void *, CHIP_ERROR err) { - DeviceLayer::PlatformMgr().StopEventLoopTask(); - DeviceLayer::PlatformMgr().Shutdown(); - }, - nullptr); - } - else + CHIP_ERROR err = DeviceLayer::SystemLayer.StartTimer( + gOptions.runtimeMs, + [](System::Layer *, void *, CHIP_ERROR err) { + DeviceLayer::PlatformMgr().StopEventLoopTask(); + DeviceLayer::PlatformMgr().Shutdown(); + }, + nullptr); + if (err != CHIP_NO_ERROR) { - - printf("Failed to create the shutdown timer. Kill with ^C.\n"); + printf("Failed to create the shutdown timer. Kill with ^C. %" CHIP_ERROR_FORMAT "\n", ChipError::FormatError(err)); } DeviceLayer::PlatformMgr().RunEventLoop(); diff --git a/src/ble/BleLayer.h b/src/ble/BleLayer.h index e506ca04919691..12ef0445c3dce6 100644 --- a/src/ble/BleLayer.h +++ b/src/ble/BleLayer.h @@ -254,11 +254,6 @@ class DLL_EXPORT BleLayer CHIP_ERROR CloseAllBleConnections(); CHIP_ERROR CloseBleConnection(BLE_CONNECTION_OBJECT connObj); - CHIP_ERROR ScheduleWork(chip::System::Layer::TimerCompleteFunct aComplete, void * aAppState) - { - return mSystemLayer->ScheduleWork(aComplete, aAppState); - } - /**< Platform interface functions: * Calling conventions: diff --git a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj index af46e66b4053d5..7329eee395367f 100644 --- a/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/CHIP.xcodeproj/project.pbxproj @@ -594,6 +594,8 @@ "CHIP_DEVICE_LAYER_TARGET_NRF5=0", "CHIP_DEVICE_LAYER_TARGET_EFR32=0", "CHIP_SYSTEM_CONFIG_USE_SOCKETS=1", + "CHIP_SYSTEM_CONFIG_POSIX_LOCKING=0", + "CHIP_SYSTEM_CONFIG_NO_LOCKING=1", "$(inherited)", ); HEADER_SEARCH_PATHS = ( @@ -734,6 +736,8 @@ "CHIP_DEVICE_LAYER_TARGET_NRF5=0", "CHIP_DEVICE_LAYER_TARGET_EFR32=0", "CHIP_SYSTEM_CONFIG_USE_SOCKETS=1", + "CHIP_SYSTEM_CONFIG_POSIX_LOCKING=0", + "CHIP_SYSTEM_CONFIG_NO_LOCKING=1", "$(inherited)", ); HEADER_SEARCH_PATHS = ( diff --git a/src/include/platform/PlatformManager.h b/src/include/platform/PlatformManager.h index a53554b56f41d7..373654049812a0 100644 --- a/src/include/platform/PlatformManager.h +++ b/src/include/platform/PlatformManager.h @@ -28,6 +28,7 @@ #include namespace chip { + namespace DeviceLayer { class PlatformManagerImpl; @@ -36,6 +37,7 @@ class ConfigurationManagerImpl; class TraitManager; class ThreadStackManagerImpl; class TimeSyncManager; + namespace Internal { class DeviceControlServer; class FabricProvisioningServer; @@ -174,14 +176,7 @@ class PlatformManager friend class Internal::GenericThreadStackManagerImpl_OpenThread_LwIP; template friend class Internal::GenericConfigurationManagerImpl; - // Parentheses used to fix clang parsing issue with these declarations - friend ::CHIP_ERROR(::chip::System::Platform::Eventing::PostEvent)(::chip::System::Layer & aLayer, - ::chip::System::Object & aTarget, - ::chip::System::EventType aType, uintptr_t aArgument); - friend ::CHIP_ERROR(::chip::System::Platform::Eventing::DispatchEvents)(::chip::System::Layer & aLayer); - friend ::CHIP_ERROR(::chip::System::Platform::Eventing::DispatchEvent)(::chip::System::Layer & aLayer, - ::chip::System::Event aEvent); - friend ::CHIP_ERROR(::chip::System::Platform::Eventing::StartTimer)(::chip::System::Layer & aLayer, uint32_t aMilliseconds); + friend class System::PlatformEventing; /* * PostEvent can be called safely on any thread without locking the stack. diff --git a/src/include/platform/internal/GenericPlatformManagerImpl.cpp b/src/include/platform/internal/GenericPlatformManagerImpl.cpp index 60f758bf6d3b5c..882e93c5641ede 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl.cpp +++ b/src/include/platform/internal/GenericPlatformManagerImpl.cpp @@ -254,8 +254,8 @@ void GenericPlatformManagerImpl::DispatchEventToSystemLayer(const Chi CHIP_ERROR err = CHIP_NO_ERROR; // Invoke the System Layer's event handler function. - err = SystemLayer.HandleEvent(*event->ChipSystemLayerEvent.Target, event->ChipSystemLayerEvent.Type, - event->ChipSystemLayerEvent.Argument); + err = SystemLayer.WatchableEvents().HandleEvent(*event->ChipSystemLayerEvent.Target, event->ChipSystemLayerEvent.Type, + event->ChipSystemLayerEvent.Argument); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Error handling CHIP System Layer event (type %d): %s", event->Type, ErrorStr(err)); diff --git a/src/include/platform/internal/GenericPlatformManagerImpl_FreeRTOS.cpp b/src/include/platform/internal/GenericPlatformManagerImpl_FreeRTOS.cpp index a83b6c34fef570..21bdea3282db90 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl_FreeRTOS.cpp +++ b/src/include/platform/internal/GenericPlatformManagerImpl_FreeRTOS.cpp @@ -138,7 +138,7 @@ void GenericPlatformManagerImpl_FreeRTOS::_RunEventLoop(void) // Call into the system layer to dispatch the callback functions for all timers // that have expired. - err = SystemLayer.HandlePlatformTimer(); + err = SystemLayer.WatchableEvents().HandlePlatformTimer(); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Error handling CHIP timers: %s", ErrorStr(err)); diff --git a/src/inet/IPEndPointBasis.cpp b/src/inet/IPEndPointBasis.cpp index 63c8c084560697..67cc52160938cd 100644 --- a/src/inet/IPEndPointBasis.cpp +++ b/src/inet/IPEndPointBasis.cpp @@ -661,8 +661,8 @@ IPPacketInfo * IPEndPointBasis::GetPacketInfo(const System::PacketBufferHandle & CHIP_ERROR IPEndPointBasis::PostPacketBufferEvent(chip::System::Layer & aLayer, System::Object & aTarget, System::EventType aEventType, System::PacketBufferHandle && aBuffer) { - const CHIP_ERROR error = - aLayer.PostEvent(aTarget, aEventType, (uintptr_t) System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(aBuffer)); + const CHIP_ERROR error = aLayer.WatchableEvents().PostEvent( + aTarget, aEventType, (uintptr_t) System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(aBuffer)); if (error == CHIP_NO_ERROR) { // If PostEvent() succeeded, it has ownership of the buffer, so we need to release it (without freeing it). diff --git a/src/inet/InetLayer.cpp b/src/inet/InetLayer.cpp index 43854f5029e8e4..fe1c914d1d23d6 100644 --- a/src/inet/InetLayer.cpp +++ b/src/inet/InetLayer.cpp @@ -276,7 +276,7 @@ CHIP_ERROR InetLayer::Init(chip::System::Layer & aSystemLayer, void * aContext) err = InitQueueLimiter(); SuccessOrExit(err); - mSystemLayer->AddEventHandlerDelegate(sInetEventHandlerDelegate); + mSystemLayer->WatchableEvents().AddEventHandlerDelegate(sInetEventHandlerDelegate); #endif // CHIP_SYSTEM_CONFIG_USE_LWIP State = kState_Initialized; diff --git a/src/inet/RawEndPoint.cpp b/src/inet/RawEndPoint.cpp index c5580ee86e4be6..a311a10c9e8eca 100644 --- a/src/inet/RawEndPoint.cpp +++ b/src/inet/RawEndPoint.cpp @@ -216,7 +216,7 @@ CHIP_ERROR RawEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, Int ReturnErrorOnFailure(IPEndPointBasis::Bind(addrType, addr, 0, intfId)); #if CHIP_SYSTEM_CONFIG_USE_DISPATCH - dispatch_queue_t dispatchQueue = SystemLayer().GetDispatchQueue(); + dispatch_queue_t dispatchQueue = SystemLayer().WatchableEvents().GetDispatchQueue(); if (dispatchQueue != nullptr) { unsigned long fd = static_cast(mSocket.GetFD()); diff --git a/src/inet/TCPEndPoint.cpp b/src/inet/TCPEndPoint.cpp index 661ab523dc474d..f087ca80534661 100644 --- a/src/inet/TCPEndPoint.cpp +++ b/src/inet/TCPEndPoint.cpp @@ -243,7 +243,7 @@ CHIP_ERROR TCPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uin } #if CHIP_SYSTEM_CONFIG_USE_DISPATCH - dispatch_queue_t dispatchQueue = SystemLayer().GetDispatchQueue(); + dispatch_queue_t dispatchQueue = SystemLayer().WatchableEvents().GetDispatchQueue(); if (dispatchQueue != nullptr) { unsigned long fd = static_cast(mSocket.GetFD()); @@ -2204,8 +2204,8 @@ err_t TCPEndPoint::LwIPHandleConnectComplete(void * arg, struct tcp_pcb * tpcb, // Post callback to HandleConnectComplete. conErr = chip::System::MapErrorLwIP(lwipErr); - if (lSystemLayer.PostEvent(*ep, kInetEvent_TCPConnectComplete, static_cast(ChipError::AsInteger(conErr))) != - CHIP_NO_ERROR) + if (lSystemLayer.WatchableEvents().PostEvent(*ep, kInetEvent_TCPConnectComplete, + static_cast(ChipError::AsInteger(conErr))) != CHIP_NO_ERROR) res = ERR_ABRT; } else @@ -2270,7 +2270,8 @@ err_t TCPEndPoint::LwIPHandleIncomingConnection(void * arg, struct tcp_pcb * tpc tcp_err(tpcb, LwIPHandleError); // Post a callback to the HandleConnectionReceived() function, passing it the new end point. - if (lSystemLayer.PostEvent(*listenEP, kInetEvent_TCPConnectionReceived, (uintptr_t) conEP) != CHIP_NO_ERROR) + if (lSystemLayer.WatchableEvents().PostEvent(*listenEP, kInetEvent_TCPConnectionReceived, (uintptr_t) conEP) != + CHIP_NO_ERROR) { err = CHIP_ERROR_CONNECTION_ABORTED; conEP->Release(); // for the Retain() above @@ -2280,7 +2281,8 @@ err_t TCPEndPoint::LwIPHandleIncomingConnection(void * arg, struct tcp_pcb * tpc // Otherwise, there was an error accepting the connection, so post a callback to the HandleError function. else - lSystemLayer.PostEvent(*listenEP, kInetEvent_TCPError, static_cast(ChipError::AsInteger(err))); + lSystemLayer.WatchableEvents().PostEvent(*listenEP, kInetEvent_TCPError, + static_cast(ChipError::AsInteger(err))); } else err = CHIP_ERROR_CONNECTION_ABORTED; @@ -2306,7 +2308,7 @@ err_t TCPEndPoint::LwIPHandleDataReceived(void * arg, struct tcp_pcb * tpcb, str chip::System::Layer & lSystemLayer = ep->SystemLayer(); // Post callback to HandleDataReceived. - if (lSystemLayer.PostEvent(*ep, kInetEvent_TCPDataReceived, (uintptr_t) p) != CHIP_NO_ERROR) + if (lSystemLayer.WatchableEvents().PostEvent(*ep, kInetEvent_TCPDataReceived, (uintptr_t) p) != CHIP_NO_ERROR) res = ERR_ABRT; } else @@ -2328,7 +2330,7 @@ err_t TCPEndPoint::LwIPHandleDataSent(void * arg, struct tcp_pcb * tpcb, u16_t l chip::System::Layer & lSystemLayer = ep->SystemLayer(); // Post callback to HandleDataReceived. - if (lSystemLayer.PostEvent(*ep, kInetEvent_TCPDataSent, (uintptr_t) len) != CHIP_NO_ERROR) + if (lSystemLayer.WatchableEvents().PostEvent(*ep, kInetEvent_TCPDataSent, (uintptr_t) len) != CHIP_NO_ERROR) res = ERR_ABRT; } else @@ -2357,7 +2359,7 @@ void TCPEndPoint::LwIPHandleError(void * arg, err_t lwipErr) // Post callback to HandleError. CHIP_ERROR err = chip::System::MapErrorLwIP(lwipErr); - lSystemLayer.PostEvent(*ep, kInetEvent_TCPError, static_cast(ChipError::AsInteger(err))); + lSystemLayer.WatchableEvents().PostEvent(*ep, kInetEvent_TCPError, static_cast(ChipError::AsInteger(err))); } } diff --git a/src/inet/UDPEndPoint.cpp b/src/inet/UDPEndPoint.cpp index aebc4281c68657..00f9842f43b4ff 100644 --- a/src/inet/UDPEndPoint.cpp +++ b/src/inet/UDPEndPoint.cpp @@ -246,7 +246,7 @@ CHIP_ERROR UDPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uin } #if CHIP_SYSTEM_CONFIG_USE_DISPATCH - dispatch_queue_t dispatchQueue = SystemLayer().GetDispatchQueue(); + dispatch_queue_t dispatchQueue = SystemLayer().WatchableEvents().GetDispatchQueue(); if (dispatchQueue != nullptr) { unsigned long fd = static_cast(mSocket.GetFD()); diff --git a/src/inet/tests/TestInetCommonPosix.cpp b/src/inet/tests/TestInetCommonPosix.cpp index 3a09d6c5341bf9..fb54fcc5bad7d5 100644 --- a/src/inet/tests/TestInetCommonPosix.cpp +++ b/src/inet/tests/TestInetCommonPosix.cpp @@ -469,7 +469,7 @@ void ServiceEvents(struct ::timeval & aSleepTime) { if (sRemainingSystemLayerEventDelay == 0) { - gSystemLayer.DispatchEvents(); + gSystemLayer.WatchableEvents().DispatchEvents(); sRemainingSystemLayerEventDelay = gNetworkOptions.EventDelay; } else @@ -478,7 +478,7 @@ void ServiceEvents(struct ::timeval & aSleepTime) // TODO: Currently timers are delayed by aSleepTime above. A improved solution would have a mechanism to reduce // aSleepTime according to the next timer. - gSystemLayer.HandlePlatformTimer(); + gSystemLayer.WatchableEvents().HandlePlatformTimer(); } } #if CHIP_TARGET_STYLE_UNIX diff --git a/src/platform/BUILD.gn b/src/platform/BUILD.gn index 4ca7023edaf40c..dbed52c95f0527 100644 --- a/src/platform/BUILD.gn +++ b/src/platform/BUILD.gn @@ -254,7 +254,6 @@ if (chip_device_platform != "none") { "LockTracker.cpp", "PersistedStorage.cpp", "SystemEventSupport.cpp", - "SystemTimerSupport.cpp", "TestIdentity.cpp", ] diff --git a/src/platform/Darwin/PlatformManagerImpl.cpp b/src/platform/Darwin/PlatformManagerImpl.cpp index cce976da2e8211..75b0bb9006ed5a 100644 --- a/src/platform/Darwin/PlatformManagerImpl.cpp +++ b/src/platform/Darwin/PlatformManagerImpl.cpp @@ -51,7 +51,7 @@ CHIP_ERROR PlatformManagerImpl::_InitChipStack() err = Internal::GenericPlatformManagerImpl::_InitChipStack(); SuccessOrExit(err); - SystemLayer.SetDispatchQueue(GetWorkQueue()); + SystemLayer.WatchableEvents().SetDispatchQueue(GetWorkQueue()); exit: return err; diff --git a/src/platform/Linux/SystemPlatformConfig.h b/src/platform/Linux/SystemPlatformConfig.h index fb8cbda00dc0fc..296e778c019d8a 100644 --- a/src/platform/Linux/SystemPlatformConfig.h +++ b/src/platform/Linux/SystemPlatformConfig.h @@ -37,9 +37,7 @@ struct ChipDeviceEvent; #define CHIP_SYSTEM_CONFIG_POSIX_LOCKING 1 #define CHIP_SYSTEM_CONFIG_FREERTOS_LOCKING 0 #define CHIP_SYSTEM_CONFIG_NO_LOCKING 0 -#define CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME 0 - -#define CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS 1 +#define CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME 1 // ========== Platform-specific Configuration Overrides ========= diff --git a/src/platform/SystemEventSupport.cpp b/src/platform/SystemEventSupport.cpp index d376c8b21a867a..6c419aa12ba5ea 100644 --- a/src/platform/SystemEventSupport.cpp +++ b/src/platform/SystemEventSupport.cpp @@ -29,12 +29,11 @@ namespace chip { namespace System { -namespace Platform { -namespace Eventing { using namespace ::chip::DeviceLayer; -CHIP_ERROR PostEvent(System::Layer & aLayer, System::Object & aTarget, System::EventType aType, uintptr_t aArgument) +CHIP_ERROR PlatformEventing::PostEvent(System::Layer & aLayer, System::Object & aTarget, System::EventType aType, + uintptr_t aArgument) { ChipDeviceEvent event; event.Type = DeviceEventType::kChipSystemLayerEvent; @@ -47,21 +46,24 @@ CHIP_ERROR PostEvent(System::Layer & aLayer, System::Object & aTarget, System::E return CHIP_NO_ERROR; } -CHIP_ERROR DispatchEvents(System::Layer & aLayer) +CHIP_ERROR PlatformEventing::DispatchEvents(System::Layer & aLayer) { PlatformMgr().RunEventLoop(); return CHIP_NO_ERROR; } -CHIP_ERROR DispatchEvent(System::Layer & aLayer, const ChipDeviceEvent * aEvent) +CHIP_ERROR PlatformEventing::DispatchEvent(System::Layer & aLayer, const ChipDeviceEvent * aEvent) { PlatformMgr().DispatchEvent(aEvent); return CHIP_NO_ERROR; } -} // namespace Eventing -} // namespace Platform +CHIP_ERROR PlatformEventing::StartTimer(System::Layer & aLayer, uint32_t aMilliseconds) +{ + return PlatformMgr().StartChipTimer(aMilliseconds); +} + } // namespace System } // namespace chip diff --git a/src/platform/SystemTimerSupport.cpp b/src/platform/SystemTimerSupport.cpp deleted file mode 100644 index 6c797054146a43..00000000000000 --- a/src/platform/SystemTimerSupport.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * - * Copyright (c) 2020 Project CHIP Authors - * Copyright (c) 2018 Nest Labs, Inc. - * All rights reserved. - * - * 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 - * Provides implementations of the CHIP System Layer platform - * timer functions that are suitable for use on all platforms. - */ -/* this file behaves like a config.h, comes first */ -#include - -#include - -namespace chip { -namespace System { -namespace Platform { -namespace Eventing { - -using namespace ::chip::DeviceLayer; - -CHIP_ERROR StartTimer(System::Layer & aLayer, uint32_t aMilliseconds) -{ - return PlatformMgr().StartChipTimer(aMilliseconds); -} - -} // namespace Eventing -} // namespace Platform -} // namespace System -} // namespace chip diff --git a/src/system/BUILD.gn b/src/system/BUILD.gn index 387ea3c89465aa..fc6c8d2bc9c41c 100644 --- a/src/system/BUILD.gn +++ b/src/system/BUILD.gn @@ -95,9 +95,9 @@ buildconfig_header("system_buildconfig") { ] } + defines += [ "CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE=" ] if (chip_system_config_use_sockets) { - defines += [ "CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE=" ] - defines += [ "CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE=" ] + defines += [ "CHIP_SYSTEM_WATCHABLE_SOCKET_CONFIG_FILE=" ] } } @@ -152,8 +152,9 @@ static_library("system") { "TimeSource.h", "WakeEvent.cpp", "WakeEvent.h", + "WatchableEventManager${chip_system_config_event_loop}.cpp", + "WatchableEventManager${chip_system_config_event_loop}.h", "WatchableEventManager.h", - "WatchableSocket.h", ] cflags = [ "-Wconversion" ] @@ -168,12 +169,11 @@ static_library("system") { if (chip_system_config_use_sockets) { sources += [ - "WatchableEventManager${chip_system_config_sockets_event_loop}.cpp", - "WatchableEventManager${chip_system_config_sockets_event_loop}.h", - "WatchableSocket${chip_system_config_sockets_event_loop}.cpp", - "WatchableSocket${chip_system_config_sockets_event_loop}.h", + "WatchableSocket${chip_system_config_event_loop}.cpp", + "WatchableSocket${chip_system_config_event_loop}.h", + "WatchableSocket.h", ] - if (chip_system_config_sockets_event_loop == "Libevent") { + if (chip_system_config_event_loop == "Libevent") { libs = [ "event" ] } } diff --git a/src/system/SystemClock.cpp b/src/system/SystemClock.cpp index fc6f47ca9457af..38c38c1de3c723 100644 --- a/src/system/SystemClock.cpp +++ b/src/system/SystemClock.cpp @@ -22,23 +22,8 @@ * for POSIX and LwIP platforms. */ -// __STDC_LIMIT_MACROS must be defined for UINT8_MAX to be defined for pre-C++11 clib -#ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS -#endif // __STDC_LIMIT_MACROS - -// __STDC_CONSTANT_MACROS must be defined for INT64_C and UINT64_C to be defined for pre-C++11 clib -#ifndef __STDC_CONSTANT_MACROS -#define __STDC_CONSTANT_MACROS -#endif // __STDC_CONSTANT_MACROS - -// config -#include - -#if !CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME - -// module header #include + // common private #include "SystemLayerPrivate.h" @@ -46,23 +31,38 @@ #include #include -#if CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS -#include -#if !(HAVE_CLOCK_GETTIME) -#include -#endif +#include +#include + +#if !CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME + +#if CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS #include -#endif // CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS +#include +#endif // CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS #if CHIP_SYSTEM_CONFIG_USE_LWIP #include #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#include -#include +#endif // !CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME namespace chip { namespace System { + +bool Clock::IsEarlier(const Clock::MonotonicMilliseconds & inFirst, const Clock::MonotonicMilliseconds & inSecond) +{ + static const Clock::MonotonicMilliseconds kMaxTime_2 = static_cast( + (static_cast(0) - static_cast(1)) / 2); + + // account for timer wrap with the assumption that no two input times will "naturally" + // be more than half the timer range apart. + return (((inFirst < inSecond) && (inSecond - inFirst < kMaxTime_2)) || + ((inFirst > inSecond) && (inFirst - inSecond > kMaxTime_2))); +} + +#if !CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME + namespace Platform { namespace Clock { @@ -239,7 +239,24 @@ CHIP_ERROR SetUnixTimeMicroseconds(uint64_t newCurTime) } // namespace Clock } // namespace Platform -} // namespace System -} // namespace chip #endif // CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME + +#if CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS + +Clock::MonotonicMilliseconds TimevalToMilliseconds(const timeval & in) +{ + return static_cast(in.tv_sec) * 1000 + + static_cast(in.tv_usec / 1000); +} + +void MillisecondsToTimeval(Clock::MonotonicMilliseconds in, timeval & out) +{ + out.tv_sec = static_cast(in / 1000); + out.tv_usec = static_cast((in % 1000) * 1000); +} + +#endif // CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS + +} // namespace System +} // namespace chip diff --git a/src/system/SystemClock.h b/src/system/SystemClock.h index 1c6955ad72b015..b3c4e5975b7b18 100644 --- a/src/system/SystemClock.h +++ b/src/system/SystemClock.h @@ -33,6 +33,10 @@ #include +#if CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS +#include +#endif // CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS + namespace chip { namespace System { @@ -146,6 +150,7 @@ extern CHIP_ERROR SetUnixTimeMicroseconds(uint64_t newCurTime); class Clock { public: + // Note: these provide documentation, not type safety. using MonotonicMicroseconds = uint64_t; using MonotonicMilliseconds = uint64_t; using UnixTimeMicroseconds = uint64_t; @@ -251,7 +256,27 @@ class Clock // Current implementation is a simple pass-through to the platform. return Platform::Clock::SetUnixTimeMicroseconds(newCurTime); } + + /** + * Compares two Clock::MonotonicMilliseconds values and returns true if the first value is earlier than the second value. + * + * @brief + * A static API that gets called to compare 2 time values. This API attempts to account for timer wrap by assuming that + * the difference between the 2 input values will only be more than half the timestamp scalar range if a timer wrap has occurred + * between the 2 samples. + * + * @note + * This implementation assumes that Clock::MonotonicMilliseconds is an unsigned scalar type. + * + * @return true if the first param is earlier than the second, false otherwise. + */ + static bool IsEarlier(const Clock::MonotonicMilliseconds & first, const Clock::MonotonicMilliseconds & second); }; +#if CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS +Clock::MonotonicMilliseconds TimevalToMilliseconds(const timeval & in); +void MillisecondsToTimeval(Clock::MonotonicMilliseconds in, timeval & out); +#endif // CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS || CHIP_SYSTEM_CONFIG_USE_SOCKETS + } // namespace System } // namespace chip diff --git a/src/system/SystemLayer.cpp b/src/system/SystemLayer.cpp index 8c7ac6e7086cf0..c093d8d5a5940d 100644 --- a/src/system/SystemLayer.cpp +++ b/src/system/SystemLayer.cpp @@ -22,127 +22,29 @@ * class methods and related data and functions. */ -// Include module header #include -// Include common private header -#include "SystemLayerPrivate.h" - -// Include local headers -#include -#include - -// Include additional CHIP headers #include -#include #include -#include -#include - -// Include system and language headers -#include -#include - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK -#include -#include -#include -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - -#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING -#include - -// Choose an approximation of PTHREAD_NULL if pthread.h doesn't define one. -#ifndef PTHREAD_NULL -#define PTHREAD_NULL 0 -#endif // PTHREAD_NULL -#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING namespace chip { namespace System { -Layer::Layer() : mLayerState(kLayerState_NotInitialized), mPlatformData(nullptr) -{ -#if CHIP_SYSTEM_CONFIG_USE_LWIP - if (!sSystemEventHandlerDelegate.IsInitialized()) - sSystemEventHandlerDelegate.Init(HandleSystemLayerEvent); - - this->mEventDelegateList = NULL; - this->mTimerList = NULL; - this->mTimerComplete = false; -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK -#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING - this->mHandleSelectThread = PTHREAD_NULL; -#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK -} +Layer::Layer() : mLayerState(kLayerState_NotInitialized) {} CHIP_ERROR Layer::Init() { -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - RegisterPOSIXErrorFormatter(); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS -#if CHIP_SYSTEM_CONFIG_USE_LWIP - RegisterLwIPErrorFormatter(); -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - - if (this->mLayerState != kLayerState_NotInitialized) - return CHIP_ERROR_INCORRECT_STATE; - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS + VerifyOrReturnError(State() == kLayerState_NotInitialized, CHIP_ERROR_INCORRECT_STATE); ReturnErrorOnFailure(mWatchableEvents.Init(*this)); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS -#if CHIP_SYSTEM_CONFIG_USE_LWIP - this->AddEventHandlerDelegate(sSystemEventHandlerDelegate); -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - this->mLayerState = kLayerState_Initialized; - return CHIP_NO_ERROR; } CHIP_ERROR Layer::Shutdown() { - if (this->mLayerState == kLayerState_NotInitialized) - return CHIP_ERROR_INCORRECT_STATE; - - for (size_t i = 0; i < Timer::sPool.Size(); ++i) - { - Timer * lTimer = Timer::sPool.Get(*this, i); - - if (lTimer != nullptr) - { - lTimer->Cancel(); - } - } - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS + VerifyOrReturnError(State() == kLayerState_Initialized, CHIP_ERROR_INCORRECT_STATE); ReturnErrorOnFailure(mWatchableEvents.Shutdown()); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - this->mLayerState = kLayerState_NotInitialized; - - return CHIP_NO_ERROR; -} - -CHIP_ERROR Layer::NewTimer(Timer *& aTimerPtr) -{ - Timer * lTimer = nullptr; - - if (this->State() != kLayerState_Initialized) - return CHIP_ERROR_INCORRECT_STATE; - - lTimer = Timer::sPool.TryCreate(*this); - aTimerPtr = lTimer; - - if (lTimer == nullptr) - { - ChipLogError(chipSystemLayer, "Timer pool EMPTY"); - return CHIP_ERROR_NO_MEMORY; - } - return CHIP_NO_ERROR; } @@ -155,32 +57,19 @@ CHIP_ERROR Layer::NewTimer(Timer *& aTimerPtr) * arguments. If called with @a aComplete and @a aAppState identical to an existing timer, * the currently-running timer will first be cancelled. * - * @param[in] aMilliseconds Expiration time in milliseconds. - * @param[in] aComplete A pointer to the function called when timer expires. - * @param[in] aAppState A pointer to the application state object used when timer expires. + * @param[in] aDelayMilliseconds Time in milliseconds before this timer fires. + * @param[in] aComplete A pointer to the function called when timer expires. + * @param[in] aAppState A pointer to the application state object used when timer expires. * * @return CHIP_NO_ERROR On success. * @return CHIP_ERROR_NO_MEMORY If a timer cannot be allocated. * @return Other Value indicating timer failed to start. * */ -CHIP_ERROR Layer::StartTimer(uint32_t aMilliseconds, TimerCompleteFunct aComplete, void * aAppState) +CHIP_ERROR Layer::StartTimer(uint32_t aDelayMilliseconds, Timers::OnCompleteFunct aComplete, void * aAppState) { - CHIP_ERROR lReturn; - Timer * lTimer; - - this->CancelTimer(aComplete, aAppState); - lReturn = this->NewTimer(lTimer); - SuccessOrExit(lReturn); - - lReturn = lTimer->Start(aMilliseconds, aComplete, aAppState); - if (lReturn != CHIP_NO_ERROR) - { - lTimer->Release(); - } - -exit: - return lReturn; + VerifyOrReturnError(State() == kLayerState_Initialized, CHIP_ERROR_INCORRECT_STATE); + return mWatchableEvents.StartTimer(aDelayMilliseconds, aComplete, aAppState); } /** @@ -196,27 +85,16 @@ CHIP_ERROR Layer::StartTimer(uint32_t aMilliseconds, TimerCompleteFunct aComplet * @param[in] aAppState A pointer to the application state object used in calling @p StartTimer(). * */ -void Layer::CancelTimer(Layer::TimerCompleteFunct aOnComplete, void * aAppState) +void Layer::CancelTimer(Timers::OnCompleteFunct aOnComplete, void * aAppState) { - if (this->State() != kLayerState_Initialized) - return; - - for (size_t i = 0; i < Timer::sPool.Size(); ++i) - { - Timer * lTimer = Timer::sPool.Get(*this, i); - - if (lTimer != nullptr && lTimer->OnComplete == aOnComplete && lTimer->AppState == aAppState) - { - lTimer->Cancel(); - break; - } - } + VerifyOrReturn(this->State() == kLayerState_Initialized); + return mWatchableEvents.CancelTimer(aOnComplete, aAppState); } /** * @brief * Schedules a function with a signature identical to - * `TimerCompleteFunct` to be run as soon as possible on the CHIP + * `OnCompleteFunct` to be run as soon as possible on the CHIP * thread. * * @note @@ -245,330 +123,12 @@ void Layer::CancelTimer(Layer::TimerCompleteFunct aOnComplete, void * aAppState) * * @retval CHIP_NO_ERROR On success. */ -CHIP_ERROR Layer::ScheduleWork(TimerCompleteFunct aComplete, void * aAppState) +CHIP_ERROR Layer::ScheduleWork(Timers::OnCompleteFunct aComplete, void * aAppState) { assertChipStackLockedByCurrentThread(); - - CHIP_ERROR lReturn; - Timer * lTimer; - - lReturn = this->NewTimer(lTimer); - SuccessOrExit(lReturn); - - lReturn = lTimer->ScheduleWork(aComplete, aAppState); - if (lReturn != CHIP_NO_ERROR) - { - lTimer->Release(); - } - -exit: - return lReturn; -} - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - -bool Layer::GetTimeout(struct timeval & aSleepTime) -{ - if (this->State() != kLayerState_Initialized) - return false; - - const Clock::MonotonicMilliseconds kCurrentTime = Clock::GetMonotonicMilliseconds(); - Clock::MonotonicMilliseconds lAwakenTime = kCurrentTime + 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::IsEarlier(kCurrentTime, lTimer->mAwakenTime)) - { - lAwakenTime = kCurrentTime; - break; - } - - if (Timer::IsEarlier(lTimer->mAwakenTime, lAwakenTime)) - lAwakenTime = lTimer->mAwakenTime; - } - } - - const Clock::MonotonicMilliseconds kSleepTime = lAwakenTime - kCurrentTime; - aSleepTime.tv_sec = static_cast(kSleepTime / 1000); - aSleepTime.tv_usec = static_cast((kSleepTime % 1000) * 1000); - - return anyTimer; -} - -void Layer::HandleTimeout() -{ - assertChipStackLockedByCurrentThread(); - -#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING - this->mHandleSelectThread = pthread_self(); -#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING - - const Clock::MonotonicMilliseconds kCurrentTime = Clock::GetMonotonicMilliseconds(); - - for (size_t i = 0; i < Timer::sPool.Size(); i++) - { - Timer * lTimer = Timer::sPool.Get(*this, i); - - if (lTimer != nullptr && !Timer::IsEarlier(kCurrentTime, lTimer->mAwakenTime)) - { - lTimer->HandleComplete(); - } - } - -#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING - this->mHandleSelectThread = PTHREAD_NULL; -#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING -} - -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - -#if CHIP_SYSTEM_CONFIG_USE_LWIP -LwIPEventHandlerDelegate Layer::sSystemEventHandlerDelegate; - -bool LwIPEventHandlerDelegate::IsInitialized() const -{ - return this->mFunction != NULL; -} - -void LwIPEventHandlerDelegate::Init(LwIPEventHandlerFunction aFunction) -{ - this->mFunction = aFunction; - this->mNextDelegate = NULL; -} - -void LwIPEventHandlerDelegate::Prepend(const LwIPEventHandlerDelegate *& aDelegateList) -{ - this->mNextDelegate = aDelegateList; - aDelegateList = this; -} - -/** - * This is the dispatch handler for system layer events. - * - * @param[in,out] aTarget A pointer to the CHIP System Layer object making the post request. - * @param[in] aEventType The type of event to post. - * @param[in,out] aArgument The argument associated with the event to post. - */ -CHIP_ERROR Layer::HandleSystemLayerEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument) -{ - CHIP_ERROR lReturn = CHIP_NO_ERROR; - ; - - // Dispatch logic specific to the event type - switch (aEventType) - { - case kEvent_ReleaseObj: - aTarget.Release(); - break; - - case kEvent_ScheduleWork: - static_cast(aTarget).HandleComplete(); - break; - - default: - lReturn = CHIP_ERROR_UNEXPECTED_EVENT; - break; - } - - return lReturn; -} - -/** - * This adds an event handler delegate to the system layer to extend its ability to handle LwIP events. - * - * @param[in] aDelegate An uninitialied LwIP event handler delegate structure - * - * @retval CHIP_NO_ERROR On success. - * @retval CHIP_ERROR_INVALID_ARGUMENT If the function pointer contained in aDelegate is NULL - */ -CHIP_ERROR Layer::AddEventHandlerDelegate(LwIPEventHandlerDelegate & aDelegate) -{ - CHIP_ERROR lReturn; - - VerifyOrExit(aDelegate.mFunction != NULL, lReturn = CHIP_ERROR_INVALID_ARGUMENT); - aDelegate.Prepend(this->mEventDelegateList); - lReturn = CHIP_NO_ERROR; - -exit: - return lReturn; -} - -/** - * This posts an event / message of the specified type with the provided argument to this instance's platform-specific event - * queue. - * - * @param[in,out] aTarget A pointer to the CHIP System Layer object making the post request. - * @param[in] aEventType The type of event to post. - * @param[in,out] aArgument The argument associated with the event to post. - * - * @retval CHIP_NO_ERROR On success. - * @retval CHIP_ERROR_INCORRECT_STATE If the state of the Layer object is incorrect. - * @retval CHIP_ERROR_NO_MEMORY If the event queue is already full. - * @retval other Platform-specific errors generated indicating the reason for failure. - */ -CHIP_ERROR Layer::PostEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument) -{ - CHIP_ERROR lReturn = CHIP_NO_ERROR; - VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = CHIP_ERROR_INCORRECT_STATE); - - // Sanity check that this instance and the target layer haven't been "crossed". - VerifyOrDieWithMsg(aTarget.IsRetained(*this), chipSystemLayer, "wrong poster! [target %p != this %p]", &(aTarget.SystemLayer()), - this); - - lReturn = Platform::Eventing::PostEvent(*this, aTarget, aEventType, aArgument); - if (lReturn != CHIP_NO_ERROR) - { - ChipLogError(chipSystemLayer, "Failed to queue CHIP System Layer event (type %d): %s", aEventType, ErrorStr(lReturn)); - } - SuccessOrExit(lReturn); - -exit: - return lReturn; -} - -/** - * This is a syntactic wrapper around a platform-specific hook that effects an event loop, waiting on a queue that services this - * instance, pulling events off of that queue, and then dispatching them for handling. - * - * @return #CHIP_NO_ERROR on success; otherwise, a specific error indicating the reason for initialization failure. - */ -CHIP_ERROR Layer::DispatchEvents() -{ - CHIP_ERROR lReturn = CHIP_NO_ERROR; - VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = CHIP_ERROR_INCORRECT_STATE); - - lReturn = Platform::Eventing::DispatchEvents(*this); - SuccessOrExit(lReturn); - -exit: - return lReturn; -} - -/** - * This dispatches the specified event for handling by this instance. - * - * The unmarshalling of the type and arguments from the event is handled by a platform-specific hook which should then call - * back to Layer::HandleEvent for the actual dispatch. - * - * @param[in] aEvent The platform-specific event object to dispatch for handling. - * - * @return CHIP_NO_ERROR on success; otherwise, a specific error indicating the reason for initialization failure. - */ -CHIP_ERROR Layer::DispatchEvent(Event aEvent) -{ - CHIP_ERROR lReturn = CHIP_NO_ERROR; - VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = CHIP_ERROR_INCORRECT_STATE); - - lReturn = Platform::Eventing::DispatchEvent(*this, aEvent); - SuccessOrExit(lReturn); - -exit: - return lReturn; -} - -/** - * This implements the actual dispatch and handling of a CHIP System Layer event. - * - * @param[in,out] aTarget A reference to the layer object to which the event is targeted. - * @param[in] aEventType The event / message type to handle. - * @param[in] aArgument The argument associated with the event / message. - * - * @retval CHIP_NO_ERROR On success. - * @retval CHIP_ERROR_INCORRECT_STATE If the state of the InetLayer object is incorrect. - * @retval CHIP_ERROR_UNEXPECTED_EVENT If the event type is unrecognized. - */ -CHIP_ERROR Layer::HandleEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument) -{ - const LwIPEventHandlerDelegate * lEventDelegate; - CHIP_ERROR lReturn; - VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = CHIP_ERROR_INCORRECT_STATE); - - // Sanity check that this instance and the target layer haven't been "crossed". - VerifyOrDieWithMsg(aTarget.IsRetained(*this), chipSystemLayer, "wrong handler! [target %p != this %p]", - &(aTarget.SystemLayer()), this); - - lReturn = CHIP_ERROR_UNEXPECTED_EVENT; - lEventDelegate = this->mEventDelegateList; - - // Prevent the target object from being freed while dispatching the event. - aTarget.Retain(); - - while (lReturn == CHIP_ERROR_UNEXPECTED_EVENT && lEventDelegate != NULL) - { - lReturn = lEventDelegate->mFunction(aTarget, aEventType, aArgument); - lEventDelegate = lEventDelegate->mNextDelegate; - } - - if (lReturn == CHIP_ERROR_UNEXPECTED_EVENT) - { - ChipLogError(chipSystemLayer, "Unexpected event type %d", aEventType); - } - - /* - Release the reference to the target object. When the object's lifetime finally comes to an end, in most cases this will be - the release call that decrements the ref count to zero. - */ - aTarget.Release(); - -exit: - return lReturn; -} - -/** - * Start the platform timer with specified millsecond duration. - * - * @brief - * Calls the Platform specific API to start a platform timer. This API is called by the chip::System::Timer class when - * one or more timers are active and require deferred execution. - * - * @param[in] aDelayMilliseconds The timer duration in milliseconds. - * - * @return CHIP_NO_ERROR on success, error code otherwise. - * - */ -CHIP_ERROR Layer::StartPlatformTimer(uint32_t aDelayMilliseconds) -{ - CHIP_ERROR lReturn = CHIP_NO_ERROR; - VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = CHIP_ERROR_INCORRECT_STATE); - - lReturn = Platform::Eventing::StartTimer(*this, aDelayMilliseconds); - SuccessOrExit(lReturn); - -exit: - return lReturn; -} - -/** - * Handle the platform timer expiration event. - * - * @brief - * Calls chip::System::Timer::HandleExpiredTimers to handle any expired timers. It is assumed that this API is called - * only while on the thread which owns the CHIP System Layer object. - * - * @return CHIP_NO_ERROR on success, error code otherwise. - * - */ -CHIP_ERROR Layer::HandlePlatformTimer() -{ - CHIP_ERROR lReturn = CHIP_NO_ERROR; - VerifyOrExit(this->State() == kLayerState_Initialized, lReturn = CHIP_ERROR_INCORRECT_STATE); - - lReturn = Timer::HandleExpiredTimers(*this); - - SuccessOrExit(lReturn); - -exit: - return lReturn; + VerifyOrReturnError(State() == kLayerState_Initialized, CHIP_ERROR_INCORRECT_STATE); + return mWatchableEvents.ScheduleWork(aComplete, aAppState); } -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP } // namespace System } // namespace chip diff --git a/src/system/SystemLayer.h b/src/system/SystemLayer.h index 8bf08971df06c9..9b3dd088077263 100644 --- a/src/system/SystemLayer.h +++ b/src/system/SystemLayer.h @@ -36,17 +36,7 @@ #include #include #include - -// Include dependent headers -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS -#include #include -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - -#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING -#include -#include -#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING #if CHIP_SYSTEM_CONFIG_USE_DISPATCH #include @@ -59,16 +49,14 @@ class Layer; class Timer; class Object; -namespace Platform { -namespace Eventing { - -extern CHIP_ERROR PostEvent(System::Layer & aLayer, Object & aTarget, EventType aType, uintptr_t aArgument); -extern CHIP_ERROR DispatchEvents(System::Layer & aLayer); -extern CHIP_ERROR DispatchEvent(System::Layer & aLayer, Event aEvent); -extern CHIP_ERROR StartTimer(System::Layer & aLayer, uint32_t aMilliseconds); - -} // namespace Eventing -} // namespace Platform +class PlatformEventing +{ +public: + static CHIP_ERROR PostEvent(System::Layer & aLayer, Object & aTarget, EventType aType, uintptr_t aArgument); + static CHIP_ERROR DispatchEvents(System::Layer & aLayer); + static CHIP_ERROR DispatchEvent(System::Layer & aLayer, Event aEvent); + static CHIP_ERROR StartTimer(System::Layer & aLayer, uint32_t aMilliseconds); +}; /** * @enum LayerState @@ -81,34 +69,8 @@ enum LayerState kLayerState_Initialized = 1 /**< Initialized state. */ }; -#if CHIP_SYSTEM_CONFIG_USE_LWIP -typedef CHIP_ERROR (*LwIPEventHandlerFunction)(Object & aTarget, EventType aEventType, uintptr_t aArgument); - -class LwIPEventHandlerDelegate -{ - friend class Layer; - -public: - bool IsInitialized(void) const; - void Init(LwIPEventHandlerFunction aFunction); - void Prepend(const LwIPEventHandlerDelegate *& aDelegateList); - -private: - LwIPEventHandlerFunction mFunction; - const LwIPEventHandlerDelegate * mNextDelegate; -}; -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - /** - * @class Layer - * - * @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 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. + * This provides access to timers according to the configured event handling model. */ class DLL_EXPORT Layer { @@ -121,92 +83,24 @@ class DLL_EXPORT Layer // to ensure that they are not used after calling Shutdown(). CHIP_ERROR Shutdown(); - LayerState State() const; - - CHIP_ERROR NewTimer(Timer *& aTimerPtr); - - using TimerCompleteFunct = Timer::OnCompleteFunct; - // typedef void (*TimerCompleteFunct)(Layer * aLayer, void * aAppState, CHIP_ERROR aError); - CHIP_ERROR StartTimer(uint32_t aMilliseconds, TimerCompleteFunct aComplete, void * aAppState); - void CancelTimer(TimerCompleteFunct aOnComplete, void * aAppState); - - CHIP_ERROR ScheduleWork(TimerCompleteFunct aComplete, void * aAppState); - - Clock & GetClock() { return mClock; } + LayerState State() const { return mLayerState; } -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK + CHIP_ERROR StartTimer(uint32_t aMilliseconds, Timers::OnCompleteFunct aComplete, void * aAppState); + void CancelTimer(Timers::OnCompleteFunct aOnComplete, void * aAppState); + CHIP_ERROR ScheduleWork(Timers::OnCompleteFunct aComplete, void * aAppState); WatchableEventManager & WatchableEvents() { return mWatchableEvents; } - bool GetTimeout(struct timeval & aSleepTime); // TODO(#5556): Integrate timer platform details with WatchableEventManager. - void HandleTimeout(); // TODO(#5556): Integrate timer platform details with WatchableEventManager. -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - -#if CHIP_SYSTEM_CONFIG_USE_LWIP - typedef CHIP_ERROR (*EventHandler)(Object & aTarget, EventType aEventType, uintptr_t aArgument); - CHIP_ERROR AddEventHandlerDelegate(LwIPEventHandlerDelegate & aDelegate); - - // Event Handling - CHIP_ERROR PostEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument); - CHIP_ERROR DispatchEvents(void); - CHIP_ERROR DispatchEvent(Event aEvent); - CHIP_ERROR HandleEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument); - // Timer Management - CHIP_ERROR HandlePlatformTimer(void); -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH - void SetDispatchQueue(dispatch_queue_t dispatchQueue) { mDispatchQueue = dispatchQueue; }; - dispatch_queue_t GetDispatchQueue() { return mDispatchQueue; }; -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH + Clock & GetClock() { return mClock; } private: LayerState mLayerState; - void * mPlatformData; - Clock mClock; - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK WatchableEventManager mWatchableEvents; -#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING - friend class WatchableEventManager; - std::atomic mHandleSelectThread; -#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - -#if CHIP_SYSTEM_CONFIG_USE_LWIP - static LwIPEventHandlerDelegate sSystemEventHandlerDelegate; - - const LwIPEventHandlerDelegate * mEventDelegateList; - Timer * mTimerList; - bool mTimerComplete; - - static CHIP_ERROR HandleSystemLayerEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument); - - CHIP_ERROR StartPlatformTimer(uint32_t aDelayMilliseconds); - - friend CHIP_ERROR Platform::Eventing::PostEvent(Layer & aLayer, Object & aTarget, EventType aType, uintptr_t aArgument); - friend CHIP_ERROR Platform::Eventing::DispatchEvents(Layer & aLayer); - friend CHIP_ERROR Platform::Eventing::DispatchEvent(Layer & aLayer, Event aEvent); - friend CHIP_ERROR Platform::Eventing::StartTimer(Layer & aLayer, uint32_t aMilliseconds); -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH - dispatch_queue_t mDispatchQueue; -#endif + Clock mClock; // Copy and assignment NOT DEFINED Layer(const Layer &) = delete; Layer & operator=(const Layer &) = delete; - - friend class Timer; }; -/** - * This returns the current state of the layer object. - */ -inline LayerState Layer::State() const -{ - return this->mLayerState; -} - } // namespace System } // namespace chip diff --git a/src/system/SystemMutex.h b/src/system/SystemMutex.h index 8ba9fa395de960..5710f55e9b4430 100644 --- a/src/system/SystemMutex.h +++ b/src/system/SystemMutex.h @@ -32,8 +32,6 @@ #include -#if !CHIP_SYSTEM_CONFIG_NO_LOCKING - #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING #include #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING @@ -100,10 +98,28 @@ class DLL_EXPORT Mutex Mutex & operator=(const Mutex &) = delete; }; -inline Mutex::Mutex() {} +class ScopedMutexLock +{ +public: + ScopedMutexLock(Mutex & mutex) : mMutex(mutex) { mutex.Lock(); } + ~ScopedMutexLock() { mMutex.Unlock(); } + +private: + Mutex & mMutex; +}; +inline Mutex::Mutex() {} inline Mutex::~Mutex() {} +#if CHIP_SYSTEM_CONFIG_NO_LOCKING +inline CHIP_ERROR Init(Mutex & aMutex) +{ + return CHIP_NO_ERROR; +} +inline void Mutex::Lock() {} +inline void Mutex::Unlock() {} +#endif // CHIP_SYSTEM_CONFIG_NO_LOCKING + #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING inline void Mutex::Lock() { @@ -144,5 +160,3 @@ inline void Mutex::Unlock(void) } // namespace System } // namespace chip - -#endif // !CHIP_SYSTEM_CONFIG_NO_LOCKING diff --git a/src/system/SystemObject.cpp b/src/system/SystemObject.cpp index 3b46936213428b..db3e59790c5d3d 100644 --- a/src/system/SystemObject.cpp +++ b/src/system/SystemObject.cpp @@ -81,7 +81,7 @@ DLL_EXPORT bool Object::TryCreate(Layer & aLayer, size_t aOctets) void Object::DeferredRelease(Object::ReleaseDeferralErrorTactic aTactic) { Layer & lSystemLayer = *this->mSystemLayer; - CHIP_ERROR lError = lSystemLayer.PostEvent(*this, chip::System::kEvent_ReleaseObj, 0); + CHIP_ERROR lError = lSystemLayer.WatchableEvents().PostEvent(*this, chip::System::kEvent_ReleaseObj, 0); if (lError != CHIP_NO_ERROR) { diff --git a/src/system/SystemTimer.cpp b/src/system/SystemTimer.cpp index 1191f6135626b7..57a2413b1adc44 100644 --- a/src/system/SystemTimer.cpp +++ b/src/system/SystemTimer.cpp @@ -38,14 +38,19 @@ #include +namespace chip { +namespace System { + +#if CHIP_SYSTEM_CONFIG_NUM_TIMERS + /******************************************************************************* * Timer state * * There are two fundamental state-change variables: Object::mSystemLayer and - * Timer::OnComplete. These must be checked and changed atomically. The state + * Timer::mOnComplete. These must be checked and changed atomically. The state * of the timer is governed by the following state machine: * - * INITIAL STATE: mSystemLayer == NULL, OnComplete == NULL + * INITIAL STATE: mSystemLayer == NULL, mOnComplete == NULL * | * V * UNALLOCATED<-----------------------------+ @@ -56,336 +61,211 @@ * ALLOCATED-------(set mSystemLayer NULL)--+ * | \-----------------------------+ * | | - * (set OnComplete != NULL) | + * (set mOnComplete != NULL) | * | | * V | - * ARMED ---------( clear OnComplete )--+ + * ARMED ---------( clear mOnComplete )--+ * * When in the ARMED state: * * * None of the member variables may mutate. - * * OnComplete must only be cleared by Cancel() or HandleComplete() + * * mOnComplete must only be cleared by Cancel() or HandleComplete() * * Cancel() and HandleComplete() will test that they are the one to - * successfully set OnComplete NULL. And if so, that will be the + * successfully set mOnComplete NULL. And if so, that will be the * thread that must call Object::Release(). * ******************************************************************************* */ -namespace chip { -namespace System { - ObjectPool Timer::sPool; -/** - * Compares two Clock::MonotonicMilliseconds values and returns true if the first value is earlier than the second value. - * - * @brief - * A static API that gets called to compare 2 time values. This API attempts to account for timer wrap by assuming that the - * difference between the 2 input values will only be more than half the timestamp scalar range if a timer wrap has occurred - * between the 2 samples. - * - * @note - * This implementation assumes that Clock::MonotonicMilliseconds is an unsigned scalar type. - * - * @return true if the first param is earlier than the second, false otherwise. - */ -bool Timer::IsEarlier(const Clock::MonotonicMilliseconds & inFirst, const Clock::MonotonicMilliseconds & inSecond) +Timer * Timer::New(System::Layer & systemLayer, uint32_t delayMilliseconds, Timers::OnCompleteFunct onComplete, void * appState) { - static const Clock::MonotonicMilliseconds kMaxTime_2 = static_cast( - (static_cast(0) - static_cast(1)) / 2); + Timer * timer = Timer::sPool.TryCreate(systemLayer); + if (timer == nullptr) + { + ChipLogError(chipSystemLayer, "Timer pool EMPTY"); + } + else + { + timer->AppState = appState; + timer->mAwakenTime = Clock::GetMonotonicMilliseconds() + static_cast(delayMilliseconds); + if (!__sync_bool_compare_and_swap(&timer->mOnComplete, nullptr, onComplete)) + { + chipDie(); + } + } + return timer; +} + +void Timer::Clear() +{ + Timers::OnCompleteFunct lOnComplete = this->mOnComplete; + + // Check if the timer is armed + VerifyOrReturn(lOnComplete != nullptr); + + // Atomically disarm if the value has not changed + VerifyOrReturn(__sync_bool_compare_and_swap(&mOnComplete, lOnComplete, nullptr)); - // account for timer wrap with the assumption that no two input times will "naturally" - // be more than half the timer range apart. - return (((inFirst < inSecond) && (inSecond - inFirst < kMaxTime_2)) || - ((inFirst > inSecond) && (inFirst - inSecond > kMaxTime_2))); + // Since this thread changed the state of mOnComplete, release the timer. + AppState = nullptr; } /** - * This method registers an one-shot timer with the underlying timer mechanism provided by the platform. - * - * @param[in] aDelayMilliseconds The number of milliseconds before this timer fires - * @param[in] aOnComplete A pointer to the callback function when this timer fires - * @param[in] aAppState An arbitrary pointer to be passed into onComplete when this timer fires - * - * @retval #CHIP_NO_ERROR Unconditionally. - * + * This method is called by the underlying timer mechanism provided by the platform when the timer fires. */ -CHIP_ERROR Timer::Start(uint32_t aDelayMilliseconds, OnCompleteFunct aOnComplete, void * aAppState) +void Timer::HandleComplete() { - Layer & lLayer = this->SystemLayer(); + // Save information needed to perform the callback. + Layer & lLayer = this->SystemLayer(); + const Timers::OnCompleteFunct lOnComplete = this->mOnComplete; + void * lAppState = this->AppState; + + // Check if timer is armed + VerifyOrReturn(lOnComplete != nullptr, ); + // Atomically disarm if the value has not changed. + VerifyOrReturn(__sync_bool_compare_and_swap(&this->mOnComplete, lOnComplete, nullptr), ); - CHIP_SYSTEM_FAULT_INJECT(FaultInjection::kFault_TimeoutImmediate, aDelayMilliseconds = 0); + // Since this thread changed the state of mOnComplete, release the timer. + AppState = nullptr; + this->Release(); - this->AppState = aAppState; - this->mAwakenTime = Clock::GetMonotonicMilliseconds() + static_cast(aDelayMilliseconds); - if (!__sync_bool_compare_and_swap(&this->OnComplete, nullptr, aOnComplete)) - { - chipDie(); - } + // Invoke the app's callback, if it's still valid. + if (lOnComplete != nullptr) + lOnComplete(&lLayer, lAppState, CHIP_NO_ERROR); +} -#if CHIP_SYSTEM_CONFIG_USE_LWIP - // add to the sorted list of timers. Earliest timer appears first. - if (lLayer.mTimerList == NULL || this->IsEarlier(this->mAwakenTime, lLayer.mTimerList->mAwakenTime)) +Timer * Timer::List::Add(Timer * add) +{ + VerifyOrDie(add != mHead); + if (mHead == NULL || Clock::IsEarlier(add->mAwakenTime, mHead->mAwakenTime)) { - this->mNextTimer = lLayer.mTimerList; - lLayer.mTimerList = this; - - // this is the new earliest timer and so the timer needs (re-)starting provided that - // the system is not currently processing expired timers, in which case it is left to - // HandleExpiredTimers() to re-start the timer. - if (!lLayer.mTimerComplete) - { - lLayer.StartPlatformTimer(aDelayMilliseconds); - } + add->mNextTimer = mHead; + mHead = add; } else { - Timer * lTimer = lLayer.mTimerList; - + Timer * lTimer = mHead; while (lTimer->mNextTimer) { - if (this->IsEarlier(this->mAwakenTime, lTimer->mNextTimer->mAwakenTime)) + VerifyOrDie(lTimer->mNextTimer != add); + if (Clock::IsEarlier(add->mAwakenTime, lTimer->mNextTimer->mAwakenTime)) { // found the insert location. break; } - lTimer = lTimer->mNextTimer; } - - this->mNextTimer = lTimer->mNextTimer; - lTimer->mNextTimer = this; + add->mNextTimer = lTimer->mNextTimer; + lTimer->mNextTimer = add; } - return CHIP_NO_ERROR; -#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(); - if (dispatchQueue) - { - dispatch_source_t timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatchQueue); - if (timerSource == nullptr) - { - chipDie(); - } - - mTimerSource = timerSource; - dispatch_source_set_timer(timerSource, dispatch_walltime(NULL, aDelayMilliseconds * NSEC_PER_MSEC), 0, 100 * NSEC_PER_MSEC); - dispatch_source_set_event_handler(timerSource, ^{ - dispatch_source_cancel(timerSource); - dispatch_release(timerSource); - - this->HandleComplete(); - }); - dispatch_resume(timerSource); - return CHIP_NO_ERROR; - } -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - lLayer.WatchableEvents().Signal(); -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - - return CHIP_NO_ERROR; + return mHead; } -CHIP_ERROR Timer::ScheduleWork(OnCompleteFunct aOnComplete, void * aAppState) +Timer * Timer::List::Remove(Timer * remove) { - CHIP_ERROR err = CHIP_NO_ERROR; - Layer & lLayer = this->SystemLayer(); - - this->AppState = aAppState; - this->mAwakenTime = Clock::GetMonotonicMilliseconds(); - if (!__sync_bool_compare_and_swap(&this->OnComplete, nullptr, aOnComplete)) - { - chipDie(); - } - -#if CHIP_SYSTEM_CONFIG_USE_LWIP - err = lLayer.PostEvent(*this, chip::System::kEvent_ScheduleWork, 0); -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + VerifyOrDie(mHead != nullptr); -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH - dispatch_queue_t dispatchQueue = lLayer.GetDispatchQueue(); - if (dispatchQueue) + if (remove == mHead) { - dispatch_async(dispatchQueue, ^{ - this->HandleComplete(); - }); + mHead = remove->mNextTimer; } else { -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH - lLayer.WatchableEvents().Signal(); -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH + Timer * lTimer = mHead; + + while (lTimer->mNextTimer) + { + if (remove == lTimer->mNextTimer) + { + lTimer->mNextTimer = remove->mNextTimer; + break; + } + + lTimer = lTimer->mNextTimer; + } } -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - return err; + remove->mNextTimer = nullptr; + return mHead; } -/** - * This method de-initializes the timer object, and prevents this timer from firing if it hasn't done so. - * - * @retval #CHIP_NO_ERROR Unconditionally. - */ -CHIP_ERROR Timer::Cancel() +Timer * Timer::List::Remove(Timers::OnCompleteFunct aOnComplete, void * aAppState) { -#if CHIP_SYSTEM_CONFIG_USE_LWIP - Layer & lLayer = this->SystemLayer(); -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - OnCompleteFunct lOnComplete = this->OnComplete; - - // Check if the timer is armed - VerifyOrExit(lOnComplete != nullptr, ); - // Atomically disarm if the value has not changed - VerifyOrExit(__sync_bool_compare_and_swap(&this->OnComplete, lOnComplete, nullptr), ); - - // Since this thread changed the state of OnComplete, release the timer. - this->AppState = nullptr; - -#if CHIP_SYSTEM_CONFIG_USE_LWIP - if (lLayer.mTimerList) + Timer * previous = nullptr; + for (Timer * timer = mHead; timer != nullptr; timer = timer->mNextTimer) { - if (this == lLayer.mTimerList) - { - lLayer.mTimerList = this->mNextTimer; - } - else + if (timer->mOnComplete == aOnComplete && timer->AppState == aAppState) { - Timer * lTimer = lLayer.mTimerList; - - while (lTimer->mNextTimer) + if (previous == nullptr) { - if (this == lTimer->mNextTimer) - { - lTimer->mNextTimer = this->mNextTimer; - break; - } - - lTimer = lTimer->mNextTimer; + mHead = timer->mNextTimer; + } + else + { + previous->mNextTimer = timer->mNextTimer; } + timer->mNextTimer = nullptr; + return timer; } - - this->mNextTimer = NULL; + previous = timer; } -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + return nullptr; +} -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH - if (mTimerSource != nullptr) +Timer * Timer::List::PopEarliest() +{ + if (mHead == nullptr) { - dispatch_source_cancel(mTimerSource); - dispatch_release(mTimerSource); + return nullptr; } -#endif - - this->Release(); -exit: - return CHIP_NO_ERROR; + Timer * earliest = mHead; + mHead = mHead->mNextTimer; + earliest->mNextTimer = nullptr; + return earliest; } -/** - * This method is called by the underlying timer mechanism provided by the platform when the timer fires. - */ -void Timer::HandleComplete() +Timer * Timer::List::PopIfEarlier(Clock::MonotonicMilliseconds t) { - // Save information needed to perform the callback. - Layer & lLayer = this->SystemLayer(); - const OnCompleteFunct lOnComplete = this->OnComplete; - void * lAppState = this->AppState; - - // Check if timer is armed - VerifyOrExit(lOnComplete != nullptr, ); - // Atomically disarm if the value has not changed. - VerifyOrExit(__sync_bool_compare_and_swap(&this->OnComplete, lOnComplete, nullptr), ); - - // Since this thread changed the state of OnComplete, release the timer. - AppState = nullptr; - this->Release(); - - // Invoke the app's callback, if it's still valid. - if (lOnComplete != nullptr) - lOnComplete(&lLayer, lAppState, CHIP_NO_ERROR); - -exit: - return; + if ((mHead == nullptr) || !Clock::IsEarlier(mHead->mAwakenTime, t)) + { + return nullptr; + } + Timer * earliest = mHead; + mHead = mHead->mNextTimer; + earliest->mNextTimer = nullptr; + return earliest; } -#if CHIP_SYSTEM_CONFIG_USE_LWIP -/** - * Completes any timers that have expired. - * - * @brief - * A static API that gets called when the platform timer expires. Any expired timers are completed and removed from the list - * of active timers in the layer object. If unexpired timers remain on completion, StartPlatformTimer will be called to - * restart the platform timer. - * - * @note - * It's harmless if this API gets called and there are no expired timers. - * - * @return CHIP_NO_ERROR on success, error code otherwise. - * - */ -CHIP_ERROR Timer::HandleExpiredTimers(Layer & aLayer) +Timer * Timer::List::ExtractEarlier(Clock::MonotonicMilliseconds t) { - size_t timersHandled = 0; - - // Expire each timer in turn until an unexpired timer is reached or the timerlist is emptied. We set the current expiration - // time outside the loop; that way timers set after the current tick will not be executed within this expiration window - // regardless how long the processing of the currently expired timers took - Clock::MonotonicMilliseconds currentTime = Clock::GetMonotonicMilliseconds(); - - while (aLayer.mTimerList) + if ((mHead == nullptr) || !Clock::IsEarlier(mHead->mAwakenTime, t)) { - // limit the number of timers handled before the control is returned to the event queue. The bound is similar to - // (though not exactly same) as that on the sockets-based systems. - - // The platform timer API has MSEC resolution so expire any timer with less than 1 msec remaining. - if ((timersHandled < Timer::sPool.Size()) && Timer::IsEarlier(aLayer.mTimerList->mAwakenTime, currentTime + 1)) - { - Timer & lTimer = *aLayer.mTimerList; - aLayer.mTimerList = lTimer.mNextTimer; - lTimer.mNextTimer = NULL; - - aLayer.mTimerComplete = true; - lTimer.HandleComplete(); - aLayer.mTimerComplete = false; - - timersHandled++; - } - else - { - // timers still exist so restart the platform timer. - uint64_t delayMilliseconds = 0ULL; - - currentTime = Clock::GetMonotonicMilliseconds(); - - // the next timer expires in the future, so set the delayMilliseconds to a non-zero value - if (currentTime < aLayer.mTimerList->mAwakenTime) - { - delayMilliseconds = aLayer.mTimerList->mAwakenTime - currentTime; - } - /* - * StartPlatformTimer() accepts a 32bit value in milliseconds. Timestamps are 64bit numbers. The only way in which this - * could overflow is if time went backwards (e.g. as a result of a time adjustment from time synchronization). Verify - * that the timer can still be executed (even if it is very late) and exit if that is the case. Note: if the time sync - * ever ends up adjusting the clock, we should implement a method that deals with all the timers in the system. - */ - VerifyOrDie(delayMilliseconds <= UINT32_MAX); - - aLayer.StartPlatformTimer(static_cast(delayMilliseconds)); - break; // all remaining timers are still ticking. - } + return nullptr; + } + Timer * begin = mHead; + Timer * end = mHead; + while ((end->mNextTimer != nullptr) && Clock::IsEarlier(end->mNextTimer->mAwakenTime, t)) + { + end = end->mNextTimer; } + mHead = end->mNextTimer; + end->mNextTimer = nullptr; + return begin; +} +CHIP_ERROR Timer::MutexedList::Init() +{ + mHead = nullptr; +#if CHIP_SYSTEM_CONFIG_NO_LOCKING return CHIP_NO_ERROR; +#else // CHIP_SYSTEM_CONFIG_NO_LOCKING + return Mutex::Init(mMutex); +#endif // CHIP_SYSTEM_CONFIG_NO_LOCKING } -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + +#endif // CHIP_SYSTEM_CONFIG_NUM_TIMERS } // namespace System } // namespace chip diff --git a/src/system/SystemTimer.h b/src/system/SystemTimer.h index 044c4efc10da04..cd917094fd4be5 100644 --- a/src/system/SystemTimer.h +++ b/src/system/SystemTimer.h @@ -21,6 +21,8 @@ * This file defines the chip::System::Timer class and its * related types used for representing an in-progress one-shot * timer. + * + * Some platforms use this to implement System::Layer timer events. */ #pragma once @@ -33,6 +35,7 @@ #include #include +#include #include #include @@ -43,47 +46,111 @@ namespace chip { namespace System { -class Layer; +namespace Timers { + +typedef void (*OnCompleteFunct)(Layer * aLayer, void * appState, CHIP_ERROR aError); + +} // namespace Timers + +#if CHIP_SYSTEM_CONFIG_NUM_TIMERS /** - * @class Timer - * - * @brief - * This is an internal class to CHIP System Layer, used to represent an in-progress one-shot timer. There is no real public - * interface available for the application layer. The static public methods used to acquire current system time are intended for - * internal use. - * + * This is an Object-pool based class that System::Layer implementations can use to assist in providing timer functions. */ class DLL_EXPORT Timer : public Object { - friend class Layer; - public: - static bool IsEarlier(const Clock::MonotonicMilliseconds & first, const Clock::MonotonicMilliseconds & second); - - typedef void (*OnCompleteFunct)(Layer * aLayer, void * aAppState, CHIP_ERROR aError); - OnCompleteFunct OnComplete; - - CHIP_ERROR Start(uint32_t aDelayMilliseconds, OnCompleteFunct aOnComplete, void * aAppState); - CHIP_ERROR Cancel(); - - static void GetStatistics(chip::System::Stats::count_t & aNumInUse, chip::System::Stats::count_t & aHighWatermark); + class List + { + public: + List() : mHead(nullptr) {} + List(Timer * head) : mHead(head) {} + bool Empty() const { return mHead == nullptr; } + Timer * Add(Timer * add); + Timer * Remove(Timer * remove); + Timer * Remove(Timers::OnCompleteFunct onComplete, void * appState); + Timer * PopEarliest(); + Timer * PopIfEarlier(Clock::MonotonicMilliseconds t); + Timer * ExtractEarlier(Clock::MonotonicMilliseconds t); + Timer * Earliest() const { return mHead; } + + protected: + Timer * mHead; + List(const List &) = delete; + List & operator=(const List &) = delete; + }; + class MutexedList : private List + { + public: + MutexedList() = default; + CHIP_ERROR Init(); + bool Empty() const + { + ScopedMutexLock lock(mMutex); + return mHead == nullptr; + } + Timer * Add(Timer * add) + { + ScopedMutexLock lock(mMutex); + return List::Add(add); + } + Timer * Remove(Timer * remove) + { + ScopedMutexLock lock(mMutex); + return List::Remove(remove); + } + Timer * Remove(Timers::OnCompleteFunct onComplete, void * appState) + { + ScopedMutexLock lock(mMutex); + return List::Remove(onComplete, appState); + } + Timer * PopEarliest() + { + ScopedMutexLock lock(mMutex); + return List::PopEarliest(); + } + Timer * PopIfEarlier(Clock::MonotonicMilliseconds t) + { + ScopedMutexLock lock(mMutex); + return List::PopIfEarlier(t); + } + Timer * ExtractEarlier(Clock::MonotonicMilliseconds t) + { + ScopedMutexLock lock(mMutex); + return List::ExtractEarlier(t); + } + Timer * Earliest() const + { + ScopedMutexLock lock(mMutex); + return mHead; + } + + private: + mutable Mutex mMutex; + MutexedList(const MutexedList &) = delete; + MutexedList & operator=(const MutexedList &) = delete; + }; + + static Timer * New(System::Layer & systemLayer, uint32_t delayMilliseconds, Timers::OnCompleteFunct onComplete, + void * appState); + void Clear(); + + static void GetStatistics(chip::System::Stats::count_t & aNumInUse, chip::System::Stats::count_t & aHighWatermark) + { + sPool.GetStatistics(aNumInUse, aHighWatermark); + } private: - static ObjectPool sPool; + friend class WatchableEventManager; - Clock::MonotonicMilliseconds mAwakenTime; + static ObjectPool sPool; void HandleComplete(); - CHIP_ERROR ScheduleWork(OnCompleteFunct aOnComplete, void * aAppState); - -#if CHIP_SYSTEM_CONFIG_USE_LWIP + Timers::OnCompleteFunct mOnComplete; + Clock::MonotonicMilliseconds mAwakenTime; Timer * mNextTimer; - static CHIP_ERROR HandleExpiredTimers(Layer & aLayer); -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - #if CHIP_SYSTEM_CONFIG_USE_DISPATCH dispatch_source_t mTimerSource = nullptr; #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH @@ -93,10 +160,7 @@ class DLL_EXPORT Timer : public Object Timer & operator=(const Timer &) = delete; }; -inline void Timer::GetStatistics(chip::System::Stats::count_t & aNumInUse, chip::System::Stats::count_t & aHighWatermark) -{ - sPool.GetStatistics(aNumInUse, aHighWatermark); -} +#endif // CHIP_SYSTEM_CONFIG_NUM_TIMERS } // namespace System } // namespace chip diff --git a/src/system/WatchableEventManager.h b/src/system/WatchableEventManager.h index 0ab91ce77cdfd2..7385480ab0880f 100644 --- a/src/system/WatchableEventManager.h +++ b/src/system/WatchableEventManager.h @@ -25,8 +25,6 @@ // Include configuration headers #include -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - namespace chip { namespace System { @@ -36,16 +34,94 @@ class Layer; /** * @class WatchableEventManager * - * An instance of this type is contained in System::Layer. Its purpose is to hold socket-event system state - * or methods available to every associated instance of WatchableSocket. + * An instance of this type is contained in System::Layer, to provide an implementation of event handling. + */ +class WatchableEventManager; + +/** + * @fn CHIP_ERROR WatchableEventManager::Init(System::Layer & systemLayer) + * + * Initialize the WatchableEventManager. Called from System::Layer::Init(). + */ + +/** + * @fn CHIP_ERROR WatchableEventManager::Shutdown() + * + * Shut down the WatchableEventManager. Called from System::Layer::Shutdown() + */ + +/** + * @fn void WatchableEventManager::Signal() + * + * Called to indicate that event monitoring may need to be refreshed or resumed. + */ + +/** + * @fn CHIP_ERROR WatchableEventManager::StartTimer(uint32_t delayMilliseconds, Timers::OnCompleteFunct onComplete, void * + * appState) + * + * This method starts a one-shot timer. * - * It MUST provide at least three methods: + * Only a single timer is allowed to be started with the same @a onComplete and @a appState + * arguments. If called with @a onComplete and @a appState identical to an existing timer, + * the currently-running timer will first be cancelled. * - * - CHIP_ERROR Init(System::Layer & systemLayer) -- called from System::Layer::Init() - * - CHIP_ERROR Shutdown() -- called from System::Layer::Shutdown() - * - void Signal() -- called to indicate that event monitoring may need to be refreshed or resumed. + * @param[in] delayMilliseconds Expiration time in milliseconds. + * @param[in] onComplete A pointer to the function called when timer expires. + * @param[in] appState A pointer to the application state object used when timer expires. * - * Other contents depend on the contract between socket-event implementation and platform layer implementation. + * @return CHIP_NO_ERROR On success. + * @return CHIP_ERROR_NO_MEMORY If a timer cannot be allocated. + * @return Other value indicating timer failed to start. + */ + +/** + * @fn void WatchableEventManager::CancelTimer(Timers::OnCompleteFunct onComplete, void * appState) + * + * This method cancels a one-shot timer, started earlier through @p StartTimer(). + * + * The cancellation could fail silently in two different ways. If the timer specified by the combination of the callback + * function and application state object couldn't be found, cancellation could fail. If the timer has fired, but not yet + * removed from memory, cancellation could also fail. + * + * @param[in] onComplete A pointer to the callback function used in calling @p StartTimer(). + * @param[in] appState A pointer to the application state object used in calling @p StartTimer(). + */ + +/** + * @fn CHIP_ERROR WatchableEventManager::ScheduleWork(Timers::OnCompleteFunct onComplete, void * appState) + * + * Schedules a function to be run as soon as possible on the CHIP thread. + * + * @note + * This function could, in principle, be implemented as + * `StartTimer`. The specification for + * `SystemTimer` however permits certain optimizations that might + * make that implementation impossible. Specifically, `SystemTimer` + * API may only be called from the thread owning the particular + * `SystemLayer`, whereas the `ScheduleWork` may be called from + * any thread. Additionally, whereas the `SystemTimer` API permits + * the invocation of the already expired handler in line, + * `ScheduleWork` guarantees that the handler function will be + * called only after the current CHIP event completes. + * + * @param[in] onComplete A pointer to a callback function to be called + * when this timer fires. + * + * @param[in] appState A pointer to an application state object to be + * passed to the callback function as argument. + * + * @retval CHIP_ERROR_INCORRECT_STATE If the SystemLayer has + * not been initialized. + * + * @retval CHIP_ERROR_NO_MEMORY If the SystemLayer cannot + * allocate a new timer. + * + * @retval CHIP_NO_ERROR On success. + */ + +/* + * Other contents depend on the contract between 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. @@ -56,7 +132,6 @@ class Layer; * - void HandleEvents() -- Called at the end of each iteration of the event loop. * - void EventLoopEnds() -- Called after the last iteration of the event loop. */ -class WatchableEventManager; } // namespace System } // namespace chip @@ -68,5 +143,3 @@ class WatchableEventManager; #include #endif // CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE #undef INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE - -#endif // CHIP_SYSTEM_CONFIG_USE_EVENT_MANAGERS diff --git a/src/system/WatchableEventManagerLibevent.cpp b/src/system/WatchableEventManagerLibevent.cpp index 141b95f4f2423d..b3cb137fbc6d80 100644 --- a/src/system/WatchableEventManagerLibevent.cpp +++ b/src/system/WatchableEventManagerLibevent.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -38,9 +39,14 @@ void HandleMdnsTimeout(); #endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ #ifndef CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS -#define CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS 1 // TODO(#5556): default to off +#define CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS 1 #endif +// Choose an approximation of PTHREAD_NULL if pthread.h doesn't define one. +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING && !defined(PTHREAD_NULL) +#define PTHREAD_NULL 0 +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING && !defined(PTHREAD_NULL) + namespace chip { namespace System { @@ -53,16 +59,14 @@ System::SocketEvents SocketEventsFromLibeventFlags(short eventFlags) .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); -} +void TimeoutCallbackHandler(evutil_socket_t fd, short eventFlags, void * data) {} } // anonymous namespace CHIP_ERROR WatchableEventManager::Init(System::Layer & systemLayer) { + RegisterPOSIXErrorFormatter(); + #if CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS static bool enabled_event_debug_mode = false; if (!enabled_event_debug_mode) @@ -72,11 +76,17 @@ CHIP_ERROR WatchableEventManager::Init(System::Layer & systemLayer) } #endif // CHIP_CONFIG_LIBEVENT_DEBUG_CHECKS - mEventBase = event_base_new(); - mTimeoutEvent = evtimer_new(mEventBase, TimeoutCallbackHandler, event_self_cbarg()); + mEventBase = event_base_new(); + mTimeoutEvent = evtimer_new(mEventBase, TimeoutCallbackHandler, event_self_cbarg()); + ChipLogError(chipSystemLayer, "XXX Init mTimeoutEvent←%p", mTimeoutEvent); mActiveSockets = nullptr; mSystemLayer = &systemLayer; - return CHIP_NO_ERROR; + +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING + mHandleSelectThread = PTHREAD_NULL; +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING + + return mTimerList.Init(); } void WatchableEventManager::PrepareEvents() @@ -88,8 +98,22 @@ void WatchableEventManager::PrepareEvents() void WatchableEventManager::PrepareEventsWithTimeout(struct timeval & nextTimeout) { - // TODO(#5556): Integrate timer platform details with WatchableEventManager. - mSystemLayer->GetTimeout(nextTimeout); + const Clock::MonotonicMilliseconds currentTime = Clock::GetMonotonicMilliseconds(); + Clock::MonotonicMilliseconds awakenTime = currentTime + TimevalToMilliseconds(nextTimeout); + + Timer * timer = mTimerList.Earliest(); + if (timer && Clock::IsEarlier(timer->mAwakenTime, awakenTime)) + { + awakenTime = timer->mAwakenTime; + } + + const Clock::MonotonicMilliseconds sleepTime = (awakenTime > currentTime) ? (awakenTime - currentTime) : 0; + MillisecondsToTimeval(sleepTime, nextTimeout); + +#if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ && !__MBED__ + chip::Mdns::GetMdnsTimeout(nextTimeout); +#endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ + if (nextTimeout.tv_sec || nextTimeout.tv_usec) { evtimer_add(mTimeoutEvent, &nextTimeout); @@ -104,7 +128,18 @@ void WatchableEventManager::WaitForEvents() void WatchableEventManager::HandleEvents() { - mSystemLayer->HandleTimeout(); +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING + mHandleSelectThread = pthread_self(); +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING + + // Obtain the list of currently expired timers. Any new timers added by timer callback are NOT handled on this pass, + // since that could result in infinite handling of new timers blocking any other progress. + Timer::List expiredTimers(mTimerList.ExtractEarlier(1 + Clock::GetMonotonicMilliseconds())); + Timer * timer = nullptr; + while ((timer = expiredTimers.PopEarliest()) != nullptr) + { + timer->HandleComplete(); + } #if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ chip::Mdns::HandleMdnsTimeout(); @@ -116,6 +151,10 @@ void WatchableEventManager::HandleEvents() mActiveSockets = watcher->mActiveNext; watcher->InvokeCallback(); } + +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING + mHandleSelectThread = PTHREAD_NULL; +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING } CHIP_ERROR WatchableEventManager::Shutdown() @@ -125,6 +164,15 @@ CHIP_ERROR WatchableEventManager::Shutdown() mTimeoutEvent = nullptr; event_base_free(mEventBase); mEventBase = nullptr; + + Timer * timer; + while ((timer = mTimerList.PopEarliest()) != nullptr) + { + timer->Clear(); + timer->Release(); + } + + mSystemLayer = nullptr; return CHIP_NO_ERROR; } @@ -133,14 +181,14 @@ void WatchableEventManager::Signal() /* * Wake up the I/O thread by writing a single byte to the wake pipe. * - * If p WakeIOThread() is being called from within an I/O event callback, then writing to the wake pipe can be skipped, + * If this is being called from within an I/O event callback, then writing to the wake pipe can be skipped, * since the I/O thread is already awake. * * Furthermore, we don't care if this write fails as the only reasonably likely failure is that the pipe is full, in which * case the select calling thread is going to wake up anyway. */ #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING - if (pthread_equal(mSystemLayer->mHandleSelectThread, pthread_self())) + if (pthread_equal(mHandleSelectThread, pthread_self())) { return; } @@ -154,6 +202,35 @@ void WatchableEventManager::Signal() } } +CHIP_ERROR WatchableEventManager::StartTimer(uint32_t delayMilliseconds, Timers::OnCompleteFunct onComplete, void * appState) +{ + // Note: the libevent implementation currently uses a single libevent timer, playing the same role as the select() timeout. + // A more ‘native' implementation would have Timer contain a libevent timer and callback data for each CHIP timer. + CHIP_SYSTEM_FAULT_INJECT(FaultInjection::kFault_TimeoutImmediate, delayMilliseconds = 0); + + CancelTimer(onComplete, appState); + + Timer * timer = Timer::New(*mSystemLayer, delayMilliseconds, onComplete, appState); + VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY); + + if (mTimerList.Add(timer) == timer) + { + // The new timer is the earliest, so the time until the next event has probably changed. + Signal(); + } + return CHIP_NO_ERROR; +} + +void WatchableEventManager::CancelTimer(Timers::OnCompleteFunct onComplete, void * appState) +{ + Timer * timer = mTimerList.Remove(onComplete, appState); + VerifyOrReturn(timer != nullptr); + + timer->Clear(); + timer->Release(); + Signal(); +} + // static void WatchableEventManager::LibeventCallbackHandler(evutil_socket_t fd, short eventFlags, void * data) { diff --git a/src/system/WatchableEventManagerLibevent.h b/src/system/WatchableEventManagerLibevent.h index 5bbab07a131b33..5a00b7006b044c 100644 --- a/src/system/WatchableEventManagerLibevent.h +++ b/src/system/WatchableEventManagerLibevent.h @@ -27,8 +27,15 @@ #include #endif // !INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE +#include + #include +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING +#include +#include +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING + namespace chip { namespace System { @@ -37,10 +44,17 @@ class WatchableEventManager { public: WatchableEventManager() : mActiveSockets(nullptr), mSystemLayer(nullptr), mEventBase(nullptr), mTimeoutEvent(nullptr) {} + + // Core ‘overrides’. CHIP_ERROR Init(Layer & systemLayer); CHIP_ERROR Shutdown(); void Signal(); + // Timer ‘overrides’. + CHIP_ERROR StartTimer(uint32_t delayMilliseconds, Timers::OnCompleteFunct onComplete, void * appState); + void CancelTimer(Timers::OnCompleteFunct onComplete, void * appState); + CHIP_ERROR ScheduleWork(Timers::OnCompleteFunct onComplete, void * appState) { return StartTimer(0, onComplete, appState); } + void EventLoopBegins() {} void PrepareEvents(); void WaitForEvents(); @@ -60,13 +74,19 @@ class WatchableEventManager 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; + Timer::MutexedList mTimerList; WakeEvent mWakeEvent; + +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING + std::atomic mHandleSelectThread; +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING }; } // namespace System diff --git a/src/system/WatchableEventManagerLwIP.cpp b/src/system/WatchableEventManagerLwIP.cpp new file mode 100644 index 00000000000000..0b3b64dc76d086 --- /dev/null +++ b/src/system/WatchableEventManagerLwIP.cpp @@ -0,0 +1,339 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2014-2017 Nest Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file implements WatchableEventManager using LwIP. + */ + +#include +#include +#include +#include + +namespace chip { +namespace System { + +LwIPEventHandlerDelegate WatchableEventManager::sSystemEventHandlerDelegate; + +WatchableEventManager::WatchableEventManager() : mHandlingTimerComplete(false), mEventDelegateList(nullptr) +{ + if (!sSystemEventHandlerDelegate.IsInitialized()) + sSystemEventHandlerDelegate.Init(HandleSystemLayerEvent); +} + +CHIP_ERROR WatchableEventManager::Init(Layer & systemLayer) +{ + RegisterLwIPErrorFormatter(); + + mSystemLayer = &systemLayer; + AddEventHandlerDelegate(sSystemEventHandlerDelegate); + return mTimerList.Init(); +} + +CHIP_ERROR WatchableEventManager::Shutdown() +{ + mSystemLayer = nullptr; + return CHIP_NO_ERROR; +} + +void WatchableEventManager::Signal() {} + +CHIP_ERROR WatchableEventManager::StartTimer(uint32_t delayMilliseconds, Timers::OnCompleteFunct onComplete, void * appState) +{ + CHIP_SYSTEM_FAULT_INJECT(FaultInjection::kFault_TimeoutImmediate, delayMilliseconds = 0); + + CancelTimer(onComplete, appState); + + Timer * timer = Timer::New(*mSystemLayer, delayMilliseconds, onComplete, appState); + VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY); + + if (mTimerList.Add(timer) == timer) + { + // this is the new earliest timer and so the timer needs (re-)starting provided that + // the system is not currently processing expired timers, in which case it is left to + // HandleExpiredTimers() to re-start the timer. + if (!mHandlingTimerComplete) + { + StartPlatformTimer(delayMilliseconds); + } + } + return CHIP_NO_ERROR; +} + +void WatchableEventManager::CancelTimer(Timers::OnCompleteFunct onComplete, void * appState) +{ + Timer * timer = mTimerList.Remove(onComplete, appState); + VerifyOrReturn(timer != nullptr); + + timer->Clear(); + timer->Release(); +} + +CHIP_ERROR WatchableEventManager::ScheduleWork(Timers::OnCompleteFunct onComplete, void * appState) +{ + Timer * timer = Timer::New(*mSystemLayer, 0, onComplete, appState); + VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY); + + return PostEvent(*timer, chip::System::kEvent_ScheduleWork, 0); +} + +bool LwIPEventHandlerDelegate::IsInitialized() const +{ + return mFunction != nullptr; +} + +void LwIPEventHandlerDelegate::Init(LwIPEventHandlerFunction aFunction) +{ + mFunction = aFunction; + mNextDelegate = nullptr; +} + +void LwIPEventHandlerDelegate::Prepend(const LwIPEventHandlerDelegate *& aDelegateList) +{ + mNextDelegate = aDelegateList; + aDelegateList = this; +} + +/** + * This is the dispatch handler for system layer events. + * + * @param[in,out] aTarget A pointer to the CHIP System Layer object making the post request. + * @param[in] aEventType The type of event to post. + * @param[in,out] aArgument The argument associated with the event to post. + */ +CHIP_ERROR WatchableEventManager::HandleSystemLayerEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument) +{ + // Dispatch logic specific to the event type + switch (aEventType) + { + case kEvent_ReleaseObj: + aTarget.Release(); + return CHIP_NO_ERROR; + + case kEvent_ScheduleWork: + static_cast(aTarget).HandleComplete(); + return CHIP_NO_ERROR; + + default: + return CHIP_ERROR_UNEXPECTED_EVENT; + } +} + +/** + * This adds an event handler delegate to the system layer to extend its ability to handle LwIP events. + * + * @param[in] aDelegate An uninitialied LwIP event handler delegate structure + * + * @retval CHIP_NO_ERROR On success. + * @retval CHIP_ERROR_INVALID_ARGUMENT If the function pointer contained in aDelegate is NULL + */ +CHIP_ERROR WatchableEventManager::AddEventHandlerDelegate(LwIPEventHandlerDelegate & aDelegate) +{ + VerifyOrReturnError(aDelegate.mFunction != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + aDelegate.Prepend(mEventDelegateList); + return CHIP_NO_ERROR; +} + +/** + * This posts an event / message of the specified type with the provided argument to this instance's platform-specific event + * queue. + * + * @param[in,out] aTarget A pointer to the CHIP System Layer object making the post request. + * @param[in] aEventType The type of event to post. + * @param[in,out] aArgument The argument associated with the event to post. + * + * @retval CHIP_NO_ERROR On success. + * @retval CHIP_ERROR_INCORRECT_STATE If the state of the Layer object is incorrect. + * @retval CHIP_ERROR_NO_MEMORY If the event queue is already full. + * @retval other Platform-specific errors generated indicating the reason for failure. + */ +CHIP_ERROR WatchableEventManager::PostEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument) +{ + VerifyOrReturnError(mSystemLayer->State() == kLayerState_Initialized, CHIP_ERROR_INCORRECT_STATE); + + // Sanity check that this instance and the target layer haven't been "crossed". + VerifyOrDieWithMsg(aTarget.IsRetained(*mSystemLayer), chipSystemLayer, "wrong poster! [target %p != this %p]", + &(aTarget.SystemLayer()), mSystemLayer); + + CHIP_ERROR lReturn = PlatformEventing::PostEvent(*mSystemLayer, aTarget, aEventType, aArgument); + if (lReturn != CHIP_NO_ERROR) + { + ChipLogError(chipSystemLayer, "Failed to queue CHIP System Layer event (type %d): %s", aEventType, ErrorStr(lReturn)); + } + return lReturn; +} + +/** + * This is a syntactic wrapper around a platform-specific hook that effects an event loop, waiting on a queue that services this + * instance, pulling events off of that queue, and then dispatching them for handling. + * + * @return #CHIP_NO_ERROR on success; otherwise, a specific error indicating the reason for initialization failure. + */ +CHIP_ERROR WatchableEventManager::DispatchEvents() +{ + VerifyOrReturnError(mSystemLayer->State() == kLayerState_Initialized, CHIP_ERROR_INCORRECT_STATE); + return PlatformEventing::DispatchEvents(*mSystemLayer); +} + +/** + * This dispatches the specified event for handling by this instance. + * + * The unmarshalling of the type and arguments from the event is handled by a platform-specific hook which should then call + * back to Layer::HandleEvent for the actual dispatch. + * + * @param[in] aEvent The platform-specific event object to dispatch for handling. + * + * @return CHIP_NO_ERROR on success; otherwise, a specific error indicating the reason for initialization failure. + */ +CHIP_ERROR WatchableEventManager::DispatchEvent(Event aEvent) +{ + VerifyOrReturnError(mSystemLayer->State() == kLayerState_Initialized, CHIP_ERROR_INCORRECT_STATE); + return PlatformEventing::DispatchEvent(*mSystemLayer, aEvent); +} + +/** + * This implements the actual dispatch and handling of a CHIP System Layer event. + * + * @param[in,out] aTarget A reference to the layer object to which the event is targeted. + * @param[in] aEventType The event / message type to handle. + * @param[in] aArgument The argument associated with the event / message. + * + * @retval CHIP_NO_ERROR On success. + * @retval CHIP_ERROR_INCORRECT_STATE If the state of the InetLayer object is incorrect. + * @retval CHIP_ERROR_UNEXPECTED_EVENT If the event type is unrecognized. + */ +CHIP_ERROR WatchableEventManager::HandleEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument) +{ + VerifyOrReturnError(mSystemLayer->State() == kLayerState_Initialized, CHIP_ERROR_INCORRECT_STATE); + + // Sanity check that this instance and the target layer haven't been "crossed". + VerifyOrDieWithMsg(aTarget.IsRetained(*mSystemLayer), chipSystemLayer, "wrong handler! [target %p != this %p]", + &(aTarget.SystemLayer()), mSystemLayer); + + // Prevent the target object from being freed while dispatching the event. + aTarget.Retain(); + + CHIP_ERROR lReturn = CHIP_ERROR_UNEXPECTED_EVENT; + const LwIPEventHandlerDelegate * lEventDelegate = mEventDelegateList; + + while (lReturn == CHIP_ERROR_UNEXPECTED_EVENT && lEventDelegate != nullptr) + { + lReturn = lEventDelegate->mFunction(aTarget, aEventType, aArgument); + lEventDelegate = lEventDelegate->mNextDelegate; + } + + if (lReturn == CHIP_ERROR_UNEXPECTED_EVENT) + { + ChipLogError(chipSystemLayer, "Unexpected event type %d", aEventType); + } + + /* + Release the reference to the target object. When the object's lifetime finally comes to an end, in most cases this will be + the release call that decrements the ref count to zero. + */ + aTarget.Release(); + + return lReturn; +} + +/** + * Start the platform timer with specified millsecond duration. + * + * @brief + * Calls the Platform specific API to start a platform timer. This API is called by the chip::System::Timer class when + * one or more timers are active and require deferred execution. + * + * @param[in] aDelayMilliseconds The timer duration in milliseconds. + * + * @return CHIP_NO_ERROR on success, error code otherwise. + * + */ +CHIP_ERROR WatchableEventManager::StartPlatformTimer(uint32_t aDelayMilliseconds) +{ + VerifyOrReturnError(mSystemLayer->State() == kLayerState_Initialized, CHIP_ERROR_INCORRECT_STATE); + return PlatformEventing::StartTimer(*mSystemLayer, aDelayMilliseconds); +} + +/** + * Handle the platform timer expiration event. Completes any timers that have expired. + * + * A static API that gets called when the platform timer expires. Any expired timers are completed and removed from the list + * of active timers in the layer object. If unexpired timers remain on completion, StartPlatformTimer will be called to + * restart the platform timer. + * + * It is assumed that this API is called only while on the thread which owns the CHIP System Layer object. + * + * @note + * It's harmless if this API gets called and there are no expired timers. + * + * @return CHIP_NO_ERROR on success, error code otherwise. + * + */ +CHIP_ERROR WatchableEventManager::HandlePlatformTimer() +{ + VerifyOrReturnError(mSystemLayer->State() == kLayerState_Initialized, CHIP_ERROR_INCORRECT_STATE); + + // Expire each timer in turn until an unexpired timer is reached or the timerlist is emptied. We set the current expiration + // time outside the loop; that way timers set after the current tick will not be executed within this expiration window + // regardless how long the processing of the currently expired timers took + Clock::MonotonicMilliseconds currentTime = Clock::GetMonotonicMilliseconds(); + + // limit the number of timers handled before the control is returned to the event queue. The bound is similar to + // (though not exactly same) as that on the sockets-based systems. + + // The platform timer API has MSEC resolution so expire any timer with less than 1 msec remaining. + size_t timersHandled = 0; + Timer * timer = nullptr; + while ((timersHandled < Timer::sPool.Size()) && ((timer = mTimerList.PopIfEarlier(currentTime + 1)) != nullptr)) + { + mHandlingTimerComplete = true; + timer->HandleComplete(); + mHandlingTimerComplete = false; + + timersHandled++; + } + + if (!mTimerList.Empty()) + { + // timers still exist so restart the platform timer. + uint64_t delayMilliseconds = 0ULL; + + currentTime = Clock::GetMonotonicMilliseconds(); + + // the next timer expires in the future, so set the delayMilliseconds to a non-zero value + if (currentTime < mTimerList.Earliest()->mAwakenTime) + { + delayMilliseconds = mTimerList.Earliest()->mAwakenTime - currentTime; + } + /* + * StartPlatformTimer() accepts a 32bit value in milliseconds. Timestamps are 64bit numbers. The only way in which this + * could overflow is if time went backwards (e.g. as a result of a time adjustment from time synchronization). Verify + * that the timer can still be executed (even if it is very late) and exit if that is the case. Note: if the time sync + * ever ends up adjusting the clock, we should implement a method that deals with all the timers in the system. + */ + VerifyOrDie(delayMilliseconds <= UINT32_MAX); + + StartPlatformTimer(static_cast(delayMilliseconds)); + } + + return CHIP_NO_ERROR; +} + +} // namespace System +} // namespace chip diff --git a/src/system/WatchableEventManagerLwIP.h b/src/system/WatchableEventManagerLwIP.h new file mode 100644 index 00000000000000..27132f687939bb --- /dev/null +++ b/src/system/WatchableEventManagerLwIP.h @@ -0,0 +1,89 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This file declares an implementation of WatchableEventManager using LwIP. + */ + +#pragma once + +#if !INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE +#error "This file should only be included from " +#include +#endif // !INCLUDING_CHIP_SYSTEM_WATCHABLE_EVENT_MANAGER_CONFIG_FILE + +namespace chip { +namespace System { + +class LwIPEventHandlerDelegate +{ + friend class WatchableEventManager; + +public: + typedef CHIP_ERROR (*LwIPEventHandlerFunction)(Object & aTarget, EventType aEventType, uintptr_t aArgument); + + bool IsInitialized(void) const; + void Init(LwIPEventHandlerFunction aFunction); + void Prepend(const LwIPEventHandlerDelegate *& aDelegateList); + +private: + LwIPEventHandlerFunction mFunction; + const LwIPEventHandlerDelegate * mNextDelegate; +}; + +class WatchableEventManager +{ +public: + // Core ‘overrides’. + WatchableEventManager(); + CHIP_ERROR Init(System::Layer & systemLayer); + CHIP_ERROR Shutdown(); + void Signal(); + + // Timer ‘overrides’. + CHIP_ERROR StartTimer(uint32_t delayMilliseconds, Timers::OnCompleteFunct onComplete, void * appState); + void CancelTimer(Timers::OnCompleteFunct onComplete, void * appState); + CHIP_ERROR ScheduleWork(Timers::OnCompleteFunct onComplete, void * appState); + + // Platform implementation. + // typedef CHIP_ERROR (*EventHandler)(Object & aTarget, EventType aEventType, uintptr_t aArgument); + CHIP_ERROR AddEventHandlerDelegate(LwIPEventHandlerDelegate & aDelegate); + + CHIP_ERROR PostEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument); + CHIP_ERROR DispatchEvents(void); + CHIP_ERROR HandleEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument); + CHIP_ERROR HandlePlatformTimer(void); + +private: + friend class PlatformEventing; + + static CHIP_ERROR HandleSystemLayerEvent(Object & aTarget, EventType aEventType, uintptr_t aArgument); + + CHIP_ERROR DispatchEvent(Event aEvent); + CHIP_ERROR StartPlatformTimer(uint32_t aDelayMilliseconds); + + static LwIPEventHandlerDelegate sSystemEventHandlerDelegate; + + Layer * mSystemLayer = nullptr; + Timer::MutexedList mTimerList; + bool mHandlingTimerComplete; // true while handling any timer completion + const LwIPEventHandlerDelegate * mEventDelegateList; +}; + +} // namespace System +} // namespace chip diff --git a/src/system/WatchableEventManagerSelect.cpp b/src/system/WatchableEventManagerSelect.cpp index a46fd956f7b7cc..8ffff6e637738b 100644 --- a/src/system/WatchableEventManagerSelect.cpp +++ b/src/system/WatchableEventManagerSelect.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -31,6 +32,11 @@ #define DEFAULT_MIN_SLEEP_PERIOD (60 * 60 * 24 * 30) // Month [sec] +// Choose an approximation of PTHREAD_NULL if pthread.h doesn't define one. +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING && !defined(PTHREAD_NULL) +#define PTHREAD_NULL 0 +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING && !defined(PTHREAD_NULL) + #if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ namespace chip { @@ -47,18 +53,32 @@ namespace System { CHIP_ERROR WatchableEventManager::Init(Layer & systemLayer) { + RegisterPOSIXErrorFormatter(); + mSystemLayer = &systemLayer; mMaxFd = -1; FD_ZERO(&mRequest.mReadSet); FD_ZERO(&mRequest.mWriteSet); FD_ZERO(&mRequest.mErrorSet); + ReturnErrorOnFailure(mTimerList.Init()); + +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING + mHandleSelectThread = PTHREAD_NULL; +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING + // Create an event to allow an arbitrary thread to wake the thread in the select loop. return mWakeEvent.Open(*this); } CHIP_ERROR WatchableEventManager::Shutdown() { + Timer * timer; + while ((timer = mTimerList.PopEarliest()) != nullptr) + { + timer->Clear(); + timer->Release(); + } mWakeEvent.Close(); mSystemLayer = nullptr; return CHIP_NO_ERROR; @@ -69,14 +89,14 @@ void WatchableEventManager::Signal() /* * Wake up the I/O thread by writing a single byte to the wake pipe. * - * If p WakeIOThread() is being called from within an I/O event callback, then writing to the wake pipe can be skipped, + * If this is being called from within an I/O event callback, then writing to the wake pipe can be skipped, * since the I/O thread is already awake. * * Furthermore, we don't care if this write fails as the only reasonably likely failure is that the pipe is full, in which * case the select calling thread is going to wake up anyway. */ #if CHIP_SYSTEM_CONFIG_POSIX_LOCKING - if (pthread_equal(mSystemLayer->mHandleSelectThread, pthread_self())) + if (pthread_equal(mHandleSelectThread, pthread_self())) { return; } @@ -90,6 +110,100 @@ void WatchableEventManager::Signal() } } +CHIP_ERROR WatchableEventManager::StartTimer(uint32_t delayMilliseconds, Timers::OnCompleteFunct onComplete, void * appState) +{ + CHIP_SYSTEM_FAULT_INJECT(FaultInjection::kFault_TimeoutImmediate, delayMilliseconds = 0); + + CancelTimer(onComplete, appState); + + Timer * timer = Timer::New(*mSystemLayer, delayMilliseconds, onComplete, appState); + VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY); + +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH + dispatch_queue_t dispatchQueue = GetDispatchQueue(); + if (dispatchQueue) + { + (void) mTimerList.Add(timer); + dispatch_source_t timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatchQueue); + ChipLogProgress(DeviceLayer, "XXX StartTimer using dispatch queue %p source %p", dispatchQueue, timerSource); + if (timerSource == nullptr) + { + chipDie(); + } + + timer->mTimerSource = timerSource; + dispatch_source_set_timer(timerSource, dispatch_walltime(NULL, delayMilliseconds * NSEC_PER_MSEC), 0, 100 * NSEC_PER_MSEC); + dispatch_source_set_event_handler(timerSource, ^{ + dispatch_source_cancel(timerSource); + dispatch_release(timerSource); + + this->HandleTimerComplete(timer); + }); + dispatch_resume(timerSource); + return CHIP_NO_ERROR; + } + ChipLogProgress(DeviceLayer, "XXX StartTimer NOT using dispatch queue"); +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH + + if (mTimerList.Add(timer) == timer) + { + // The new timer is the earliest, so the time until the next event has probably changed. + Signal(); + } + return CHIP_NO_ERROR; +} + +void WatchableEventManager::CancelTimer(Timers::OnCompleteFunct onComplete, void * appState) +{ + Timer * timer = mTimerList.Remove(onComplete, appState); + VerifyOrReturn(timer != nullptr); + + timer->Clear(); + +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH + if (timer->mTimerSource != nullptr) + { + ChipLogProgress(DeviceLayer, "XXX CancelTimer source %p", timer->mTimerSource); + dispatch_source_cancel(timer->mTimerSource); + dispatch_release(timer->mTimerSource); + } + else + ChipLogProgress(DeviceLayer, "XXX CancelTimer NO timer source"); +#endif + + timer->Release(); + Signal(); +} + +CHIP_ERROR WatchableEventManager::ScheduleWork(Timers::OnCompleteFunct onComplete, void * appState) +{ + CancelTimer(onComplete, appState); + + Timer * timer = Timer::New(*mSystemLayer, 0, onComplete, appState); + VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY); + +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH + dispatch_queue_t dispatchQueue = GetDispatchQueue(); + if (dispatchQueue) + { + ChipLogProgress(DeviceLayer, "XXX ScheduleWork using dispatch queue %p", dispatchQueue); + (void) mTimerList.Add(timer); + dispatch_async(dispatchQueue, ^{ + this->HandleTimerComplete(timer); + }); + return CHIP_NO_ERROR; + } + ChipLogProgress(DeviceLayer, "XXX ScheduleWork NOT using dispatch queue"); +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH + + if (mTimerList.Add(timer) == timer) + { + // The new timer is the earliest, so the time until the next event has probably changed. + Signal(); + } + return CHIP_NO_ERROR; +} + /** * Set the read, write or exception bit flags for the specified socket based on its status in * the corresponding file descriptor sets. @@ -121,12 +235,12 @@ SocketEvents WatchableEventManager::SocketEventsFromFDs(int socket, const fd_set return res; } -bool WatchableEventManager::HasAny(int fd) +bool WatchableEventManager::HasAnyRequest(int fd) { return FD_ISSET(fd, &mRequest.mReadSet) || FD_ISSET(fd, &mRequest.mWriteSet) || FD_ISSET(fd, &mRequest.mErrorSet); } -CHIP_ERROR WatchableEventManager::Set(int fd, fd_set * fds) +CHIP_ERROR WatchableEventManager::SetRequest(int fd, fd_set * fds) { FD_SET(fd, fds); if (fd > mMaxFd) @@ -138,7 +252,7 @@ CHIP_ERROR WatchableEventManager::Set(int fd, fd_set * fds) return CHIP_NO_ERROR; } -CHIP_ERROR WatchableEventManager::Clear(int fd, fd_set * fds) +CHIP_ERROR WatchableEventManager::ClearRequest(int fd, fd_set * fds) { FD_CLR(fd, fds); if (fd == mMaxFd) @@ -150,7 +264,7 @@ CHIP_ERROR WatchableEventManager::Clear(int fd, fd_set * fds) return CHIP_NO_ERROR; } -void WatchableEventManager::Reset(int fd) +void WatchableEventManager::ResetRequests(int fd) { FD_CLR(fd, &mRequest.mReadSet); FD_CLR(fd, &mRequest.mWriteSet); @@ -166,7 +280,7 @@ void WatchableEventManager::MaybeLowerMaxFd() int fd; for (fd = mMaxFd; fd >= 0; --fd) { - if (HasAny(fd)) + if (HasAnyRequest(fd)) { break; } @@ -186,8 +300,17 @@ void WatchableEventManager::PrepareEvents() void WatchableEventManager::PrepareEventsWithTimeout(struct timeval & nextTimeout) { - // TODO(#5556): Integrate timer platform details with WatchableEventManager. - mSystemLayer->GetTimeout(nextTimeout); + const Clock::MonotonicMilliseconds currentTime = Clock::GetMonotonicMilliseconds(); + Clock::MonotonicMilliseconds awakenTime = currentTime + TimevalToMilliseconds(nextTimeout); + + Timer * timer = mTimerList.Earliest(); + if (timer && Clock::IsEarlier(timer->mAwakenTime, awakenTime)) + { + awakenTime = timer->mAwakenTime; + } + + const Clock::MonotonicMilliseconds sleepTime = (awakenTime > currentTime) ? (awakenTime - currentTime) : 0; + MillisecondsToTimeval(sleepTime, nextTimeout); #if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ && !__MBED__ chip::Mdns::GetMdnsTimeout(nextTimeout); @@ -212,7 +335,19 @@ void WatchableEventManager::HandleEvents() } VerifyOrDie(mSystemLayer != nullptr); - mSystemLayer->HandleTimeout(); + +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING + mHandleSelectThread = pthread_self(); +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING + + // Obtain the list of currently expired timers. Any new timers added by timer callback are NOT handled on this pass, + // since that could result in infinite handling of new timers blocking any other progress. + Timer::List expiredTimers(mTimerList.ExtractEarlier(1 + Clock::GetMonotonicMilliseconds())); + Timer * timer = nullptr; + while ((timer = expiredTimers.PopEarliest()) != nullptr) + { + timer->HandleComplete(); + } for (WatchableSocket * watchable = mAttachedSockets; watchable != nullptr; watchable = watchable->mAttachedNext) { @@ -230,7 +365,19 @@ void WatchableEventManager::HandleEvents() #if CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ && !__MBED__ chip::Mdns::HandleMdnsTimeout(); #endif // CHIP_DEVICE_CONFIG_ENABLE_MDNS && !__ZEPHYR__ + +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING + mHandleSelectThread = PTHREAD_NULL; +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING +} + +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH +void WatchableEventManager::HandleTimerComplete(Timer * timer) +{ + mTimerList.Remove(timer); + timer->HandleComplete(); } +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH } // namespace System } // namespace chip diff --git a/src/system/WatchableEventManagerSelect.h b/src/system/WatchableEventManagerSelect.h index 2b1239777c6a0f..f3abf4161b3401 100644 --- a/src/system/WatchableEventManagerSelect.h +++ b/src/system/WatchableEventManagerSelect.h @@ -29,22 +29,37 @@ #include +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING +#include +#include +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING + #include #include +#include #include namespace chip { namespace System { +class Layer; +class Timer; class WatchableSocket; class WatchableEventManager { public: + // Core ‘overrides’. CHIP_ERROR Init(System::Layer & systemLayer); CHIP_ERROR Shutdown(); void Signal(); + // Timer ‘overrides’. + CHIP_ERROR StartTimer(uint32_t delayMilliseconds, Timers::OnCompleteFunct onComplete, void * appState); + void CancelTimer(Timers::OnCompleteFunct onComplete, void * appState); + CHIP_ERROR ScheduleWork(Timers::OnCompleteFunct onComplete, void * appState); + + // Platform implementation. void EventLoopBegins() {} void PrepareEvents(); void WaitForEvents(); @@ -56,17 +71,22 @@ class WatchableEventManager static SocketEvents SocketEventsFromFDs(int socket, const fd_set & readfds, const fd_set & writefds, const fd_set & exceptfds); +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH + void SetDispatchQueue(dispatch_queue_t dispatchQueue) { mDispatchQueue = dispatchQueue; }; + dispatch_queue_t GetDispatchQueue() { return mDispatchQueue; }; + void HandleTimerComplete(Timer * timer); +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH + protected: friend class WatchableSocket; - CHIP_ERROR Set(int fd, fd_set * fds); - CHIP_ERROR Clear(int fd, fd_set * fds); + CHIP_ERROR SetRequest(int fd, fd_set * fds); + CHIP_ERROR ClearRequest(int fd, fd_set * fds); Layer * mSystemLayer = nullptr; WatchableSocket * mAttachedSockets = nullptr; - - // TODO(#5556): Integrate timer platform details with WatchableEventManager. - struct timeval mNextTimeout; + Timer::MutexedList mTimerList; + timeval mNextTimeout; // Members for select loop struct SelectSets @@ -78,14 +98,24 @@ class WatchableEventManager SelectSets mRequest; SelectSets mSelected; int mMaxFd; - int mSelectResult; ///< return value from select() + + // Return value from select(), carried between WaitForEvents() and HandleEvents(). + int mSelectResult; WakeEvent mWakeEvent; +#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING + std::atomic mHandleSelectThread; +#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING + +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH + dispatch_queue_t mDispatchQueue; +#endif + private: - bool HasAny(int fd); + bool HasAnyRequest(int fd); void MaybeLowerMaxFd(); - void Reset(int fd); + void ResetRequests(int fd); }; } // namespace System diff --git a/src/system/WatchableSocketSelect.cpp b/src/system/WatchableSocketSelect.cpp index e418493a5d7fe1..65ce2fb71a047b 100644 --- a/src/system/WatchableSocketSelect.cpp +++ b/src/system/WatchableSocketSelect.cpp @@ -34,7 +34,7 @@ namespace System { CHIP_ERROR WatchableSocket::OnAttach() { - mSharedState->Reset(mFD); + mSharedState->ResetRequests(mFD); VerifyOrReturnError(mAttachedNext == nullptr, CHIP_ERROR_INCORRECT_STATE); mAttachedNext = mSharedState->mAttachedSockets; @@ -45,7 +45,7 @@ CHIP_ERROR WatchableSocket::OnAttach() CHIP_ERROR WatchableSocket::OnRelease() { VerifyOrReturnError(mFD >= 0, CHIP_ERROR_INCORRECT_STATE); - mSharedState->Reset(mFD); + mSharedState->ResetRequests(mFD); WatchableSocket ** pp = &mSharedState->mAttachedSockets; while (*pp != nullptr) @@ -65,22 +65,22 @@ CHIP_ERROR WatchableSocket::OnRelease() CHIP_ERROR WatchableSocket::OnRequestCallbackOnPendingRead() { - return mSharedState->Set(mFD, &mSharedState->mRequest.mReadSet); + return mSharedState->SetRequest(mFD, &mSharedState->mRequest.mReadSet); } CHIP_ERROR WatchableSocket::OnRequestCallbackOnPendingWrite() { - return mSharedState->Set(mFD, &mSharedState->mRequest.mWriteSet); + return mSharedState->SetRequest(mFD, &mSharedState->mRequest.mWriteSet); } CHIP_ERROR WatchableSocket::OnClearCallbackOnPendingRead() { - return mSharedState->Clear(mFD, &mSharedState->mRequest.mReadSet); + return mSharedState->ClearRequest(mFD, &mSharedState->mRequest.mReadSet); } CHIP_ERROR WatchableSocket::OnClearCallbackOnPendingWrite() { - return mSharedState->Clear(mFD, &mSharedState->mRequest.mWriteSet); + return mSharedState->ClearRequest(mFD, &mSharedState->mRequest.mWriteSet); } /** diff --git a/src/system/system.gni b/src/system/system.gni index 0775d1f94e8423..12e371a7c29d1b 100644 --- a/src/system/system.gni +++ b/src/system/system.gni @@ -23,9 +23,6 @@ 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 = "" @@ -42,6 +39,15 @@ declare_args() { (current_os == "mac" || current_os == "ios") } +declare_args() { + # Event loop type. + if (chip_system_config_use_lwip) { + chip_system_config_event_loop = "LwIP" + } else { + chip_system_config_event_loop = "Select" + } +} + if (chip_system_config_locking == "") { if (current_os == "freertos") { chip_system_config_locking = "freertos" diff --git a/src/system/tests/TestSystemTimer.cpp b/src/system/tests/TestSystemTimer.cpp index 8327ca3626726a..9efeb79426836e 100644 --- a/src/system/tests/TestSystemTimer.cpp +++ b/src/system/tests/TestSystemTimer.cpp @@ -63,7 +63,7 @@ static void ServiceEvents(Layer & aLayer, ::timeval & aSleepTime) { // 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(); + aLayer.WatchableEvents().HandlePlatformTimer(); } #endif // CHIP_SYSTEM_CONFIG_USE_LWIP }