Skip to content

Commit 36f0c50

Browse files
Add the ability for EventLoopHandlers to participate in a Select-based event loop (#34433)
* Darwin: Make it possible to build with chip_system_config_use_dispatch=false Note: This is for unit testing / development purposes only. * Add the ability for EventLoopHandlers to participate in the event loop * Avoid leaking the fallback timer in the test * Expand documentation * Fix GN logic so fake platform tests can run on Darwin * Disable TestEventLoopHandler when chip_device_platform == "fake" The fake PlatformManagerImpl doesn't drive the SystemLayer event loop.
1 parent 693b82b commit 36f0c50

11 files changed

+316
-28
lines changed

src/lib/support/BUILD.gn

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import("${chip_root}/build/chip/chip_version.gni")
2323
import("${chip_root}/build/chip/java/config.gni")
2424
import("${chip_root}/build/chip/tests.gni")
2525
import("${chip_root}/src/lib/core/core.gni")
26+
import("${chip_root}/src/platform/device.gni")
2627

2728
declare_args() {
2829
# Set to true to run the PersistentStorageDelegate API compliance audit
@@ -255,7 +256,7 @@ static_library("support") {
255256
"verhoeff/Verhoeff10.cpp",
256257
]
257258

258-
if (current_os == "android" || matter_enable_java_compilation) {
259+
if (chip_device_platform == "android" || matter_enable_java_compilation) {
259260
if (matter_enable_java_compilation) {
260261
include_dirs = java_matter_controller_dependent_paths
261262
}
@@ -304,7 +305,7 @@ static_library("support") {
304305
# Platforms that utilize CHIP_SYSTEM_CONFIG_PLATFORM_LOG need to
305306
# be pulled in here as public_deps since they hook into logging at
306307
# the macro level rather than just providing a LogV implementation.
307-
if (current_os == "mac" || current_os == "ios") {
308+
if (chip_device_platform == "darwin") {
308309
public_deps += [ "${chip_root}/src/platform/Darwin:logging" ]
309310
}
310311

src/lib/support/IntrusiveList.h

+10
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,11 @@ class IntrusiveList : public IntrusiveListBase
414414
ConstIterator(IntrusiveListBase::ConstIteratorBase && base) : IntrusiveListBase::ConstIteratorBase(std::move(base)) {}
415415
const T * operator->() { return Hook::ToObject(mCurrent); }
416416
const T & operator*() { return *Hook::ToObject(mCurrent); }
417+
418+
ConstIterator & operator++() { return static_cast<ConstIterator &>(IntrusiveListBase::ConstIteratorBase::operator++()); }
419+
ConstIterator operator++(int) { return IntrusiveListBase::ConstIteratorBase::operator++(1); }
420+
ConstIterator & operator--() { return static_cast<ConstIterator &>(IntrusiveListBase::ConstIteratorBase::operator--()); }
421+
ConstIterator operator--(int) { return IntrusiveListBase::ConstIteratorBase::operator--(1); }
417422
};
418423

419424
class Iterator : public IntrusiveListBase::IteratorBase
@@ -426,6 +431,11 @@ class IntrusiveList : public IntrusiveListBase
426431
Iterator(IntrusiveListBase::IteratorBase && base) : IntrusiveListBase::IteratorBase(std::move(base)) {}
427432
T * operator->() { return Hook::ToObject(mCurrent); }
428433
T & operator*() { return *Hook::ToObject(mCurrent); }
434+
435+
Iterator & operator++() { return static_cast<Iterator &>(IntrusiveListBase::IteratorBase::operator++()); }
436+
Iterator operator++(int) { return IntrusiveListBase::IteratorBase::operator++(1); }
437+
Iterator & operator--() { return static_cast<Iterator &>(IntrusiveListBase::IteratorBase::operator--()); }
438+
Iterator operator--(int) { return IntrusiveListBase::IteratorBase::operator--(1); }
429439
};
430440

431441
ConstIterator begin() const { return IntrusiveListBase::begin(); }

src/platform/Darwin/PlatformManagerImpl.cpp

+7-7
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@
3535
#include <platform/PlatformManager.h>
3636

3737
// Include the non-inline definitions for the GenericPlatformManagerImpl<> template,
38+
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
3839
#include <platform/internal/GenericPlatformManagerImpl.ipp>
40+
#else
41+
#include <platform/internal/GenericPlatformManagerImpl_POSIX.ipp>
42+
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
3943

4044
#include <CoreFoundation/CoreFoundation.h>
4145
#include <tracing/metric_event.h>
@@ -64,7 +68,7 @@ CHIP_ERROR PlatformManagerImpl::_InitChipStack()
6468
ReturnErrorOnFailure(Internal::PosixConfig::Init());
6569
#endif // CHIP_DISABLE_PLATFORM_KVS
6670

67-
#if !CHIP_SYSTEM_CONFIG_USE_LIBEV
71+
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
6872
// Ensure there is a dispatch queue available
6973
static_cast<System::LayerSocketsLoop &>(DeviceLayer::SystemLayer()).SetDispatchQueue(GetWorkQueue());
7074
#endif
@@ -83,6 +87,7 @@ CHIP_ERROR PlatformManagerImpl::_InitChipStack()
8387
return CHIP_NO_ERROR;
8488
}
8589

90+
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
8691
CHIP_ERROR PlatformManagerImpl::_StartEventLoopTask()
8792
{
8893
auto expected = WorkQueueState::kSuspended;
@@ -128,12 +133,6 @@ void PlatformManagerImpl::_RunEventLoop()
128133
mRunLoopSem = nullptr;
129134
}
130135

131-
void PlatformManagerImpl::_Shutdown()
132-
{
133-
// Call up to the base class _Shutdown() to perform the bulk of the shutdown.
134-
GenericPlatformManagerImpl<ImplClass>::_Shutdown();
135-
}
136-
137136
CHIP_ERROR PlatformManagerImpl::_PostEvent(const ChipDeviceEvent * event)
138137
{
139138
const ChipDeviceEvent eventCopy = *event;
@@ -142,6 +141,7 @@ CHIP_ERROR PlatformManagerImpl::_PostEvent(const ChipDeviceEvent * event)
142141
});
143142
return CHIP_NO_ERROR;
144143
}
144+
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
145145

146146
#if CHIP_STACK_LOCK_TRACKING_ENABLED
147147
bool PlatformManagerImpl::_IsChipStackLockedByCurrentThread() const

src/platform/Darwin/PlatformManagerImpl.h

+13-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@
2525

2626
#include <lib/core/Global.h>
2727
#include <platform/Darwin/BleScannerDelegate.h>
28+
29+
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
2830
#include <platform/internal/GenericPlatformManagerImpl.h>
31+
#else
32+
#include <platform/internal/GenericPlatformManagerImpl_POSIX.h>
33+
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
2934

3035
#include <atomic>
3136
#include <dispatch/dispatch.h>
@@ -38,7 +43,12 @@ class BleScannerDelegate;
3843
/**
3944
* Concrete implementation of the PlatformManager singleton object for Darwin platforms.
4045
*/
41-
class PlatformManagerImpl final : public PlatformManager, public Internal::GenericPlatformManagerImpl<PlatformManagerImpl>
46+
class PlatformManagerImpl final : public PlatformManager,
47+
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
48+
public Internal::GenericPlatformManagerImpl<PlatformManagerImpl>
49+
#else
50+
public Internal::GenericPlatformManagerImpl_POSIX<PlatformManagerImpl>
51+
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
4252
{
4353
// Allow the PlatformManager interface class to delegate method calls to
4454
// the implementation methods provided by this class.
@@ -58,8 +68,8 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener
5868
private:
5969
// ===== Methods that implement the PlatformManager abstract interface.
6070
CHIP_ERROR _InitChipStack();
61-
void _Shutdown();
6271

72+
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
6373
CHIP_ERROR _StartChipTimer(System::Clock::Timeout delay) { return CHIP_ERROR_NOT_IMPLEMENTED; };
6474
CHIP_ERROR _StartEventLoopTask();
6575
CHIP_ERROR _StopEventLoopTask();
@@ -69,6 +79,7 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener
6979
bool _TryLockChipStack() { return false; };
7080
void _UnlockChipStack(){};
7181
CHIP_ERROR _PostEvent(const ChipDeviceEvent * event);
82+
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
7283

7384
#if CHIP_STACK_LOCK_TRACKING_ENABLED
7485
bool _IsChipStackLockedByCurrentThread() const;

src/platform/Darwin/SystemPlatformConfig.h

-8
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,6 @@ struct ChipDeviceEvent;
3434

3535
// ==================== Platform Adaptations ====================
3636

37-
#if !CHIP_SYSTEM_CONFIG_USE_LIBEV
38-
// FIXME: these should not be hardcoded here, it is set via build config
39-
// Need to exclude these for now in libev case
40-
#define CHIP_SYSTEM_CONFIG_POSIX_LOCKING 0
41-
#define CHIP_SYSTEM_CONFIG_FREERTOS_LOCKING 0
42-
#define CHIP_SYSTEM_CONFIG_NO_LOCKING 1
43-
#endif
44-
4537
#define CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME 1
4638
#define CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS 1
4739
#define CHIP_SYSTEM_CONFIG_POOL_USE_HEAP 1

src/system/SystemLayer.h

+49
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include <system/SystemEvent.h>
4242

4343
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS
44+
#include <lib/support/IntrusiveList.h>
4445
#include <system/SocketEvents.h>
4546
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS
4647

@@ -243,6 +244,7 @@ class LayerSockets : public Layer
243244
* Initialize watching for events on a file descriptor.
244245
*
245246
* Returns an opaque token through @a tokenOut that must be passed to subsequent operations for this file descriptor.
247+
* Multiple calls to start watching the same file descriptor will return the same token.
246248
* StopWatchingSocket() must be called before closing the file descriptor.
247249
*/
248250
virtual CHIP_ERROR StartWatchingSocket(int fd, SocketWatchToken * tokenOut) = 0;
@@ -288,6 +290,44 @@ class LayerSockets : public Layer
288290
virtual SocketWatchToken InvalidSocketWatchToken() = 0;
289291
};
290292

293+
class LayerSocketsLoop;
294+
295+
/**
296+
* EventLoopHandlers can be registered with a LayerSocketsLoop instance to enable
297+
* participation of those handlers in the processing cycle of the event loop. This makes
298+
* it possible to implement adapters that allow components utilizing a third-party event
299+
* loop API to participate in the Matter event loop, instead of having to run an entirely
300+
* separate event loop on another thread.
301+
*
302+
* Specifically, the `PrepareEvents` and `HandleEvents` methods of registered event loop
303+
* handlers will be called from the LayerSocketsLoop methods of the same names.
304+
*
305+
* @see LayerSocketsLoop::PrepareEvents
306+
* @see LayerSocketsLoop::HandleEvents
307+
*/
308+
class EventLoopHandler : public chip::IntrusiveListNodeBase<>
309+
{
310+
public:
311+
virtual ~EventLoopHandler() {}
312+
313+
/**
314+
* Prepares events and returns the next requested wake time.
315+
*/
316+
virtual Clock::Timestamp PrepareEvents(Clock::Timestamp now) { return Clock::Timestamp::max(); }
317+
318+
/**
319+
* Handles / dispatches pending events.
320+
* Every call to this method will have been preceded by a call to `PrepareEvents`.
321+
*/
322+
virtual void HandleEvents() = 0;
323+
324+
private:
325+
// mState is provided exclusively for use by the LayerSocketsLoop implementation
326+
// sub-class and can be accessed by it via the LayerSocketsLoop::LoopHandlerState() helper.
327+
friend class LayerSocketsLoop;
328+
intptr_t mState = 0;
329+
};
330+
291331
class LayerSocketsLoop : public LayerSockets
292332
{
293333
public:
@@ -298,13 +338,22 @@ class LayerSocketsLoop : public LayerSockets
298338
virtual void HandleEvents() = 0;
299339
virtual void EventLoopEnds() = 0;
300340

341+
#if !CHIP_SYSTEM_CONFIG_USE_DISPATCH
342+
virtual void AddLoopHandler(EventLoopHandler & handler) = 0;
343+
virtual void RemoveLoopHandler(EventLoopHandler & handler) = 0;
344+
#endif // !CHIP_SYSTEM_CONFIG_USE_DISPATCH
345+
301346
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
302347
virtual void SetDispatchQueue(dispatch_queue_t dispatchQueue) = 0;
303348
virtual dispatch_queue_t GetDispatchQueue() = 0;
304349
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV
305350
virtual void SetLibEvLoop(struct ev_loop * aLibEvLoopP) = 0;
306351
virtual struct ev_loop * GetLibEvLoop() = 0;
307352
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV
353+
354+
protected:
355+
// Expose EventLoopHandler.mState as a non-const reference to sub-classes
356+
decltype(EventLoopHandler::mState) & LoopHandlerState(EventLoopHandler & handler) { return handler.mState; }
308357
};
309358

310359
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS

src/system/SystemLayerImplSelect.cpp

+72-9
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <system/SystemLayer.h>
2929
#include <system/SystemLayerImplSelect.h>
3030

31+
#include <algorithm>
3132
#include <errno.h>
3233

3334
// Choose an approximation of PTHREAD_NULL if pthread.h doesn't define one.
@@ -370,8 +371,9 @@ CHIP_ERROR LayerImplSelect::StartWatchingSocket(int fd, SocketWatchToken * token
370371
{
371372
if (w.mFD == fd)
372373
{
373-
// Duplicate registration is an error.
374-
return CHIP_ERROR_INVALID_ARGUMENT;
374+
// Already registered, return the existing token
375+
*tokenOut = reinterpret_cast<SocketWatchToken>(&w);
376+
return CHIP_NO_ERROR;
375377
}
376378
if ((w.mFD == kInvalidFd) && (watch == nullptr))
377379
{
@@ -608,6 +610,32 @@ SocketEvents LayerImplSelect::SocketEventsFromFDs(int socket, const fd_set & rea
608610
return res;
609611
}
610612

613+
#if !CHIP_SYSTEM_CONFIG_USE_DISPATCH
614+
enum : intptr_t
615+
{
616+
kLoopHandlerInactive = 0, // default value for EventLoopHandler::mState
617+
kLoopHandlerPending,
618+
kLoopHandlerActive,
619+
};
620+
621+
void LayerImplSelect::AddLoopHandler(EventLoopHandler & handler)
622+
{
623+
// Add the handler as pending because this method can be called at any point
624+
// in a PrepareEvents() / WaitForEvents() / HandleEvents() sequence.
625+
// It will be marked active when we call PrepareEvents() on it for the first time.
626+
auto & state = LoopHandlerState(handler);
627+
VerifyOrDie(state == kLoopHandlerInactive);
628+
state = kLoopHandlerPending;
629+
mLoopHandlers.PushBack(&handler);
630+
}
631+
632+
void LayerImplSelect::RemoveLoopHandler(EventLoopHandler & handler)
633+
{
634+
mLoopHandlers.Remove(&handler);
635+
LoopHandlerState(handler) = kLoopHandlerInactive;
636+
}
637+
#endif // !CHIP_SYSTEM_CONFIG_USE_DISPATCH
638+
611639
void LayerImplSelect::PrepareEvents()
612640
{
613641
assertChipStackLockedByCurrentThread();
@@ -616,10 +644,28 @@ void LayerImplSelect::PrepareEvents()
616644
Clock::Timestamp awakenTime = currentTime + kDefaultMinSleepPeriod;
617645

618646
TimerList::Node * timer = mTimerList.Earliest();
619-
if (timer && timer->AwakenTime() < awakenTime)
647+
if (timer)
648+
{
649+
awakenTime = std::min(awakenTime, timer->AwakenTime());
650+
}
651+
652+
#if !CHIP_SYSTEM_CONFIG_USE_DISPATCH
653+
// Activate added EventLoopHandlers and call PrepareEvents on active handlers.
654+
auto loopIter = mLoopHandlers.begin();
655+
while (loopIter != mLoopHandlers.end())
620656
{
621-
awakenTime = timer->AwakenTime();
657+
auto & loop = *loopIter++; // advance before calling out, in case a list modification clobbers the `next` pointer
658+
switch (auto & state = LoopHandlerState(loop))
659+
{
660+
case kLoopHandlerPending:
661+
state = kLoopHandlerActive;
662+
[[fallthrough]];
663+
case kLoopHandlerActive:
664+
awakenTime = std::min(awakenTime, loop.PrepareEvents(currentTime));
665+
break;
666+
}
622667
}
668+
#endif // !CHIP_SYSTEM_CONFIG_USE_DISPATCH
623669

624670
const Clock::Timestamp sleepTime = (awakenTime > currentTime) ? (awakenTime - currentTime) : Clock::kZero;
625671
Clock::ToTimeval(sleepTime, mNextTimeout);
@@ -683,18 +729,35 @@ void LayerImplSelect::HandleEvents()
683729
mTimerPool.Invoke(timer);
684730
}
685731

686-
for (auto & w : mSocketWatchPool)
732+
// Process socket events, if any
733+
if (mSelectResult > 0)
687734
{
688-
if (w.mFD != kInvalidFd)
735+
for (auto & w : mSocketWatchPool)
689736
{
690-
SocketEvents events = SocketEventsFromFDs(w.mFD, mSelected.mReadSet, mSelected.mWriteSet, mSelected.mErrorSet);
691-
if (events.HasAny() && w.mCallback != nullptr)
737+
if (w.mFD != kInvalidFd && w.mCallback != nullptr)
692738
{
693-
w.mCallback(events, w.mCallbackData);
739+
SocketEvents events = SocketEventsFromFDs(w.mFD, mSelected.mReadSet, mSelected.mWriteSet, mSelected.mErrorSet);
740+
if (events.HasAny())
741+
{
742+
w.mCallback(events, w.mCallbackData);
743+
}
694744
}
695745
}
696746
}
697747

748+
#if !CHIP_SYSTEM_CONFIG_USE_DISPATCH
749+
// Call HandleEvents for active loop handlers
750+
auto loopIter = mLoopHandlers.begin();
751+
while (loopIter != mLoopHandlers.end())
752+
{
753+
auto & loop = *loopIter++; // advance before calling out, in case a list modification clobbers the `next` pointer
754+
if (LoopHandlerState(loop) == kLoopHandlerActive)
755+
{
756+
loop.HandleEvents();
757+
}
758+
}
759+
#endif // !CHIP_SYSTEM_CONFIG_USE_DISPATCH
760+
698761
#if CHIP_SYSTEM_CONFIG_POSIX_LOCKING
699762
mHandleSelectThread = PTHREAD_NULL;
700763
#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING

src/system/SystemLayerImplSelect.h

+9
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ class LayerImplSelect : public LayerSocketsLoop
8787
void HandleEvents() override;
8888
void EventLoopEnds() override {}
8989

90+
#if !CHIP_SYSTEM_CONFIG_USE_DISPATCH
91+
void AddLoopHandler(EventLoopHandler & handler) override;
92+
void RemoveLoopHandler(EventLoopHandler & handler) override;
93+
#endif // !CHIP_SYSTEM_CONFIG_USE_DISPATCH
94+
9095
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
9196
void SetDispatchQueue(dispatch_queue_t dispatchQueue) override { mDispatchQueue = dispatchQueue; };
9297
dispatch_queue_t GetDispatchQueue() override { return mDispatchQueue; };
@@ -135,6 +140,10 @@ class LayerImplSelect : public LayerSocketsLoop
135140
TimerList mExpiredTimers;
136141
timeval mNextTimeout;
137142

143+
#if !CHIP_SYSTEM_CONFIG_USE_DISPATCH
144+
IntrusiveList<EventLoopHandler> mLoopHandlers;
145+
#endif
146+
138147
// Members for select loop
139148
struct SelectSets
140149
{

0 commit comments

Comments
 (0)