diff --git a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.h b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.h index acdd2a4d3b762c..e4e5255d5b13bd 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.h +++ b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.h @@ -65,11 +65,14 @@ class GenericPlatformManagerImpl_POSIX : public GenericPlatformManagerImpl mState{ State::kStopped }; + +#if !CHIP_SYSTEM_CONFIG_USE_LIBEV pthread_cond_t mEventQueueStoppedCond; pthread_mutex_t mStateLock; pthread_attr_t mChipTaskAttr; struct sched_param mChipTaskSchedParam; +#endif #if CHIP_STACK_LOCK_TRACKING_ENABLED bool mChipStackIsLocked = false; @@ -101,16 +104,25 @@ class GenericPlatformManagerImpl_POSIX : public GenericPlatformManagerImpl(this); } - void ProcessDeviceEvents(); +#if CHIP_SYSTEM_CONFIG_USE_LIBEV + static void _DispatchEventViaScheduleWork(System::Layer * aLayer, void * appState); +#else DeviceSafeQueue mChipEventQueue; std::atomic mShouldRunEventLoop{ true }; static void * EventLoopTaskMain(void * arg); +#endif + void ProcessDeviceEvents(); }; // Instruct the compiler to instantiate the template only when explicitly told to do so. extern template class GenericPlatformManagerImpl_POSIX; +#if CHIP_SYSTEM_CONFIG_USE_LIBEV +// with external libev mainloop, this should be implemented externally to terminate the mainloop cleanly +// (Note that there is a weak default implementation that just calls chipDie() when the external implementation is missing) +extern void ExitExternalMainLoop(); +#endif } // namespace Internal } // namespace DeviceLayer } // namespace chip diff --git a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp index 5c9061a901a3f2..ee87cfb7726989 100644 --- a/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp +++ b/src/include/platform/internal/GenericPlatformManagerImpl_POSIX.ipp @@ -46,12 +46,14 @@ namespace chip { namespace DeviceLayer { namespace Internal { +#if !CHIP_SYSTEM_CONFIG_USE_LIBEV namespace { System::LayerSocketsLoop & SystemLayerSocketsLoop() { return static_cast(DeviceLayer::SystemLayer()); } } // anonymous namespace +#endif template CHIP_ERROR GenericPlatformManagerImpl_POSIX::_InitChipStack() @@ -59,6 +61,8 @@ CHIP_ERROR GenericPlatformManagerImpl_POSIX::_InitChipStack() // Call up to the base class _InitChipStack() to perform the bulk of the initialization. ReturnErrorOnFailure(GenericPlatformManagerImpl::_InitChipStack()); +#if !CHIP_SYSTEM_CONFIG_USE_LIBEV + mShouldRunEventLoop.store(true, std::memory_order_relaxed); int ret = pthread_cond_init(&mEventQueueStoppedCond, nullptr); @@ -66,6 +70,7 @@ CHIP_ERROR GenericPlatformManagerImpl_POSIX::_InitChipStack() ret = pthread_mutex_init(&mStateLock, nullptr); VerifyOrReturnError(ret == 0, CHIP_ERROR_POSIX(ret)); +#endif return CHIP_NO_ERROR; } @@ -130,15 +135,44 @@ CHIP_ERROR GenericPlatformManagerImpl_POSIX::_StartChipTimer(System:: return CHIP_NO_ERROR; } +#if CHIP_SYSTEM_CONFIG_USE_LIBEV +template +void GenericPlatformManagerImpl_POSIX::_DispatchEventViaScheduleWork(System::Layer * aLayer, void * appState) +{ + auto * event = static_cast(appState); + PlatformMgrImpl().DispatchEvent(event); + delete event; +} +#endif // CHIP_SYSTEM_CONFIG_USE_LIBEV + template CHIP_ERROR GenericPlatformManagerImpl_POSIX::_PostEvent(const ChipDeviceEvent * event) { +#if CHIP_SYSTEM_CONFIG_USE_LIBEV + // Note: PostEvent() is documented to allow being called "from any thread". + // In the libev mainloop case however, calling from another thread is NOT supported. + // Introducing this restriction is OK because the very goal of using libev is to avoid + // multiple threads by running matter and all application code in the same thread on the + // libev mainloop. So getting called from another thread here is very likely a + // application design error. + VerifyOrDieWithMsg(_IsChipStackLockedByCurrentThread(), DeviceLayer, "PostEvent() not allowed from outside chip stack lock"); + + // Schedule dispatching this event via System Layer's ScheduleWork + ChipDeviceEvent * eventCopyP = new ChipDeviceEvent; + VerifyOrDie(eventCopyP != nullptr); + *eventCopyP = *event; + SystemLayer().ScheduleWork(&_DispatchEventViaScheduleWork, eventCopyP); + return CHIP_NO_ERROR; +#else mChipEventQueue.Push(*event); SystemLayerSocketsLoop().Signal(); // Trigger wake select on CHIP thread return CHIP_NO_ERROR; +#endif // CHIP_SYSTEM_CONFIG_USE_LIBEV } +#if !CHIP_SYSTEM_CONFIG_USE_LIBEV + template void GenericPlatformManagerImpl_POSIX::ProcessDeviceEvents() { @@ -152,6 +186,12 @@ void GenericPlatformManagerImpl_POSIX::ProcessDeviceEvents() template void GenericPlatformManagerImpl_POSIX::_RunEventLoop() { +#if CHIP_SYSTEM_CONFIG_USE_LIBEV + + VerifyOrDieWithMsg(false, DeviceLayer, "libev based app should never try to run a separate event loop"); + +#else + pthread_mutex_lock(&mStateLock); // @@ -203,6 +243,7 @@ void GenericPlatformManagerImpl_POSIX::_RunEventLoop() // Shutdown() method. // mState.store(State::kStopped, std::memory_order_relaxed); +#endif // CHIP_SYSTEM_CONFIG_USE_LIBEV } template @@ -213,9 +254,22 @@ void * GenericPlatformManagerImpl_POSIX::EventLoopTaskMain(void * arg return nullptr; } +#endif // !CHIP_SYSTEM_CONFIG_USE_LIBEV + template CHIP_ERROR GenericPlatformManagerImpl_POSIX::_StartEventLoopTask() { + +#if CHIP_SYSTEM_CONFIG_USE_LIBEV + // Note: With libev, we dont need our own mainloop. + // Still, we set State::kRunning to activate lock checking, because + // calls to ScheduleWork and some System Layer methods may not + // occur from other threads (which usually don't exist in a + // libev app) + mState.store(State::kRunning, std::memory_order_relaxed); + return CHIP_NO_ERROR; +#else + int err; err = pthread_attr_init(&mChipTaskAttr); VerifyOrReturnError(err == 0, CHIP_ERROR_POSIX(err)); @@ -247,11 +301,30 @@ CHIP_ERROR GenericPlatformManagerImpl_POSIX::_StartEventLoopTask() pthread_mutex_unlock(&mStateLock); return CHIP_ERROR_POSIX(err); +#endif // CHIP_SYSTEM_CONFIG_USE_LIBEV +} + +#if CHIP_SYSTEM_CONFIG_USE_LIBEV +// fallback implementation +void __attribute__((weak)) ExitExternalMainLoop() +{ + // FIXME: implement better exit + VerifyOrDieWithMsg(false, DeviceLayer, "Missing custom ExitExternalMainLoop() implementation for clean shutdown -> just die"); } +#endif // CHIP_SYSTEM_CONFIG_USE_LIBEV template CHIP_ERROR GenericPlatformManagerImpl_POSIX::_StopEventLoopTask() { + +#if CHIP_SYSTEM_CONFIG_USE_LIBEV + // with libev, the mainloop is set up and managed externally + mState.store(State::kStopping, std::memory_order_relaxed); + ExitExternalMainLoop(); // this callback needs to be implemented. + mState.store(State::kStopped, std::memory_order_relaxed); + return CHIP_NO_ERROR; +#else + int err = 0; // @@ -304,6 +377,7 @@ CHIP_ERROR GenericPlatformManagerImpl_POSIX::_StopEventLoopTask() exit: return CHIP_ERROR_POSIX(err); +#endif // CHIP_SYSTEM_CONFIG_USE_LIBEV } template @@ -316,8 +390,10 @@ void GenericPlatformManagerImpl_POSIX::_Shutdown() // VerifyOrDie(mState.load(std::memory_order_relaxed) == State::kStopped); +#if !CHIP_SYSTEM_CONFIG_USE_LIBEV pthread_mutex_destroy(&mStateLock); pthread_cond_destroy(&mEventQueueStoppedCond); +#endif // // Call up to the base class _Shutdown() to perform the actual stack de-initialization diff --git a/src/platform/Darwin/PlatformManagerImpl.cpp b/src/platform/Darwin/PlatformManagerImpl.cpp index f1d5e4841e761b..bf2dde3d4fe360 100644 --- a/src/platform/Darwin/PlatformManagerImpl.cpp +++ b/src/platform/Darwin/PlatformManagerImpl.cpp @@ -52,8 +52,10 @@ CHIP_ERROR PlatformManagerImpl::_InitChipStack() SuccessOrExit(err); #endif // CHIP_DISABLE_PLATFORM_KVS +#if !CHIP_SYSTEM_CONFIG_USE_LIBEV // Ensure there is a dispatch queue available static_cast(DeviceLayer::SystemLayer()).SetDispatchQueue(GetWorkQueue()); +#endif // Call _InitChipStack() on the generic implementation base class // to finish the initialization process. diff --git a/src/platform/Darwin/SystemPlatformConfig.h b/src/platform/Darwin/SystemPlatformConfig.h index 76995250738a69..5311b538ed7ea2 100644 --- a/src/platform/Darwin/SystemPlatformConfig.h +++ b/src/platform/Darwin/SystemPlatformConfig.h @@ -34,9 +34,14 @@ struct ChipDeviceEvent; // ==================== Platform Adaptations ==================== +#if !CHIP_SYSTEM_CONFIG_USE_LIBEV +// FIXME: these should not be hardcoded here, it is set via build config +// Need to exclude these for now in libev case #define CHIP_SYSTEM_CONFIG_POSIX_LOCKING 0 #define CHIP_SYSTEM_CONFIG_FREERTOS_LOCKING 0 #define CHIP_SYSTEM_CONFIG_NO_LOCKING 1 +#endif + #define CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME 1 #define CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS 1 #define CHIP_SYSTEM_CONFIG_POOL_USE_HEAP 1 diff --git a/src/system/BUILD.gn b/src/system/BUILD.gn index acc7f790e3471d..7ce311d6499f19 100644 --- a/src/system/BUILD.gn +++ b/src/system/BUILD.gn @@ -85,6 +85,7 @@ buildconfig_header("system_buildconfig") { "CHIP_SYSTEM_CONFIG_TEST=${chip_build_tests}", "CHIP_WITH_NLFAULTINJECTION=${chip_with_nlfaultinjection}", "CHIP_SYSTEM_CONFIG_USE_DISPATCH=${chip_system_config_use_dispatch}", + "CHIP_SYSTEM_CONFIG_USE_LIBEV=${chip_system_config_use_libev}", "CHIP_SYSTEM_CONFIG_USE_LWIP=${chip_system_config_use_lwip}", "CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT=${chip_system_config_use_open_thread_inet_endpoints}", "CHIP_SYSTEM_CONFIG_USE_SOCKETS=${chip_system_config_use_sockets}", diff --git a/src/system/SystemLayer.h b/src/system/SystemLayer.h index 4321edeb02b883..279ac057b05ff7 100644 --- a/src/system/SystemLayer.h +++ b/src/system/SystemLayer.h @@ -43,7 +43,9 @@ #if CHIP_SYSTEM_CONFIG_USE_DISPATCH #include -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH +#elif CHIP_SYSTEM_CONFIG_USE_LIBEV +#include +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV #include @@ -250,7 +252,10 @@ class LayerSocketsLoop : public LayerSockets #if CHIP_SYSTEM_CONFIG_USE_DISPATCH virtual void SetDispatchQueue(dispatch_queue_t dispatchQueue) = 0; virtual dispatch_queue_t GetDispatchQueue() = 0; -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH +#elif CHIP_SYSTEM_CONFIG_USE_LIBEV + virtual void SetLibEvLoop(struct ev_loop * aLibEvLoopP) = 0; + virtual struct ev_loop * GetLibEvLoop() = 0; +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV }; #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS diff --git a/src/system/SystemLayerImplSelect.cpp b/src/system/SystemLayerImplSelect.cpp index 171f14f8a6cfb7..513cfff4860f5a 100644 --- a/src/system/SystemLayerImplSelect.cpp +++ b/src/system/SystemLayerImplSelect.cpp @@ -35,6 +35,17 @@ #define PTHREAD_NULL 0 #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING && !defined(PTHREAD_NULL) +#if CHIP_SYSTEM_CONFIG_USE_LIBEV +// older libev do not yet have ev_io_modify +#ifndef ev_io_modify +#define ev_io_modify(ev, events_) \ + do \ + { \ + (ev)->events = ((ev)->events & EV__IOFDSET) | (events_); \ + } while (0) +#endif // ev_io_modify +#endif // CHIP_SYSTEM_CONFIG_USE_LIBEV + namespace chip { namespace System { @@ -82,11 +93,25 @@ void LayerImplSelect::Shutdown() { w.DisableAndClear(); } +#elif CHIP_SYSTEM_CONFIG_USE_LIBEV + TimerList::Node * timer; + while ((timer = mTimerList.PopEarliest()) != nullptr) + { + if (ev_is_active(&timer->mLibEvTimer)) + { + ev_timer_stop(mLibEvLoopP, &timer->mLibEvTimer); + } + } + mTimerPool.ReleaseAll(); -#else // CHIP_SYSTEM_CONFIG_USE_DISPATCH + for (auto & w : mSocketWatchPool) + { + w.DisableAndClear(); + } +#else mTimerList.Clear(); mTimerPool.ReleaseAll(); -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV mWakeEvent.Close(*this); @@ -139,10 +164,7 @@ CHIP_ERROR LayerImplSelect::StartTimer(Clock::Timeout delay, TimerCompleteCallba { (void) mTimerList.Add(timer); dispatch_source_t timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, DISPATCH_TIMER_STRICT, dispatchQueue); - if (timerSource == nullptr) - { - chipDie(); - } + VerifyOrDie(timerSource != nullptr); timer->mTimerSource = timerSource; dispatch_source_set_timer( @@ -157,14 +179,25 @@ CHIP_ERROR LayerImplSelect::StartTimer(Clock::Timeout delay, TimerCompleteCallba dispatch_resume(timerSource); return CHIP_NO_ERROR; } -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH - +#elif CHIP_SYSTEM_CONFIG_USE_LIBEV + VerifyOrDie(mLibEvLoopP != nullptr); + ev_timer_init(&timer->mLibEvTimer, &LayerImplSelect::HandleLibEvTimer, 1, 0); + timer->mLibEvTimer.data = timer; + auto t = Clock::Milliseconds64(delay).count(); + ev_timer_set(&timer->mLibEvTimer, static_cast(t) / 1E3, 0.); + (void) mTimerList.Add(timer); + ev_timer_start(mLibEvLoopP, &timer->mLibEvTimer); + return CHIP_NO_ERROR; +#endif +#if !CHIP_SYSTEM_CONFIG_USE_LIBEV + // Note: dispatch based implementation needs this as fallback, but not LIBEV (and dead code is not allowed with -Werror) 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; +#endif // !CHIP_SYSTEM_CONFIG_USE_LIBEV } void LayerImplSelect::CancelTimer(TimerCompleteCallback onComplete, void * appState) @@ -190,7 +223,10 @@ void LayerImplSelect::CancelTimer(TimerCompleteCallback onComplete, void * appSt dispatch_source_cancel(timer->mTimerSource); dispatch_release(timer->mTimerSource); } -#endif +#elif CHIP_SYSTEM_CONFIG_USE_LIBEV + VerifyOrDie(mLibEvLoopP != nullptr); + ev_timer_stop(mLibEvLoopP, &timer->mLibEvTimer); +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV mTimerPool.Release(timer); Signal(); @@ -211,8 +247,21 @@ CHIP_ERROR LayerImplSelect::ScheduleWork(TimerCompleteCallback onComplete, void }); return CHIP_NO_ERROR; } -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH - +#elif CHIP_SYSTEM_CONFIG_USE_LIBEV + // schedule as timer with no delay, but do NOT cancel previous timers with same onComplete/appState! + TimerList::Node * timer = mTimerPool.Create(*this, SystemClock().GetMonotonicTimestamp(), onComplete, appState); + VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY); + VerifyOrDie(mLibEvLoopP != nullptr); + ev_timer_init(&timer->mLibEvTimer, &LayerImplSelect::HandleLibEvTimer, 1, 0); + timer->mLibEvTimer.data = timer; + auto t = Clock::Milliseconds64(0).count(); + ev_timer_set(&timer->mLibEvTimer, static_cast(t) / 1E3, 0.); + (void) mTimerList.Add(timer); + ev_timer_start(mLibEvLoopP, &timer->mLibEvTimer); + return CHIP_NO_ERROR; +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV +#if !CHIP_SYSTEM_CONFIG_USE_LIBEV + // Note: dispatch based implementation needs this as fallback, but not LIBEV (and dead code is not allowed with -Werror) // Ideally we would not use a timer here at all, but if we try to just // ScheduleLambda the lambda needs to capture the following: // 1) onComplete @@ -247,6 +296,7 @@ CHIP_ERROR LayerImplSelect::ScheduleWork(TimerCompleteCallback onComplete, void Signal(); } return CHIP_NO_ERROR; +#endif // !CHIP_SYSTEM_CONFIG_USE_LIBEV } CHIP_ERROR LayerImplSelect::StartWatchingSocket(int fd, SocketWatchToken * tokenOut) @@ -268,6 +318,11 @@ CHIP_ERROR LayerImplSelect::StartWatchingSocket(int fd, SocketWatchToken * token VerifyOrReturnError(watch != nullptr, CHIP_ERROR_ENDPOINT_POOL_FULL); watch->mFD = fd; +#if CHIP_SYSTEM_CONFIG_USE_LIBEV + ev_io_init(&watch->mIoWatcher, &LayerImplSelect::HandleLibEvIoWatcher, 0, 0); + watch->mIoWatcher.data = watch; + watch->mLayerImplSelectP = this; +#endif *tokenOut = reinterpret_cast(watch); return CHIP_NO_ERROR; @@ -320,6 +375,24 @@ CHIP_ERROR LayerImplSelect::RequestCallbackOnPendingRead(SocketWatchToken token) dispatch_activate(watch->mRdSource); } } +#elif CHIP_SYSTEM_CONFIG_USE_LIBEV + VerifyOrDie(mLibEvLoopP != nullptr); + int evs = (watch->mPendingIO.Has(SocketEventFlags::kRead) ? EV_READ : 0) | + (watch->mPendingIO.Has(SocketEventFlags::kWrite) ? EV_WRITE : 0); + if (!ev_is_active(&watch->mIoWatcher)) + { + // First time actually using that watch + ev_io_set(&watch->mIoWatcher, watch->mFD, evs); + ev_io_start(mLibEvLoopP, &watch->mIoWatcher); + } + else + { + // already active, just change flags + // Note: changing flags only reliably works when the watcher is stopped + ev_io_stop(mLibEvLoopP, &watch->mIoWatcher); + ev_io_modify(&watch->mIoWatcher, evs); + ev_io_start(mLibEvLoopP, &watch->mIoWatcher); + } #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH return CHIP_NO_ERROR; @@ -359,10 +432,27 @@ CHIP_ERROR LayerImplSelect::RequestCallbackOnPendingWrite(SocketWatchToken token } }); // only now we are sure the source exists and can become active - watch->mPendingIO.Set(SocketEventFlags::kWrite); dispatch_activate(watch->mWrSource); } } +#elif CHIP_SYSTEM_CONFIG_USE_LIBEV + VerifyOrDie(mLibEvLoopP != nullptr); + int evs = (watch->mPendingIO.Has(SocketEventFlags::kRead) ? EV_READ : 0) | + (watch->mPendingIO.Has(SocketEventFlags::kWrite) ? EV_WRITE : 0); + if (!ev_is_active(&watch->mIoWatcher)) + { + // First time actually using that watch + ev_io_set(&watch->mIoWatcher, watch->mFD, evs); + ev_io_start(mLibEvLoopP, &watch->mIoWatcher); + } + else + { + // already active, just change flags + // Note: changing flags only reliably works when the watcher is stopped + ev_io_stop(mLibEvLoopP, &watch->mIoWatcher); + ev_io_modify(&watch->mIoWatcher, evs); + ev_io_start(mLibEvLoopP, &watch->mIoWatcher); + } #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH return CHIP_NO_ERROR; @@ -375,6 +465,14 @@ CHIP_ERROR LayerImplSelect::ClearCallbackOnPendingRead(SocketWatchToken token) watch->mPendingIO.Clear(SocketEventFlags::kRead); +#if CHIP_SYSTEM_CONFIG_USE_LIBEV + if (ev_is_active(&watch->mIoWatcher) && watch->mPendingIO.Raw() == 0) + { + // all flags cleared now, stop watching + ev_io_stop(mLibEvLoopP, &watch->mIoWatcher); + } +#endif + return CHIP_NO_ERROR; } @@ -385,6 +483,14 @@ CHIP_ERROR LayerImplSelect::ClearCallbackOnPendingWrite(SocketWatchToken token) watch->mPendingIO.Clear(SocketEventFlags::kWrite); +#if CHIP_SYSTEM_CONFIG_USE_LIBEV + if (ev_is_active(&watch->mIoWatcher) && watch->mPendingIO.Raw() == 0) + { + // all flags cleared now, stop watching + ev_io_stop(mLibEvLoopP, &watch->mIoWatcher); + } +#endif + return CHIP_NO_ERROR; } @@ -396,7 +502,7 @@ CHIP_ERROR LayerImplSelect::StopWatchingSocket(SocketWatchToken * tokenInOut) VerifyOrReturnError(watch != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(watch->mFD >= 0, CHIP_ERROR_INCORRECT_STATE); -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH || CHIP_SYSTEM_CONFIG_USE_LIBEV watch->DisableAndClear(); #else watch->Clear(); @@ -532,12 +638,47 @@ void LayerImplSelect::HandleEvents() } #if CHIP_SYSTEM_CONFIG_USE_DISPATCH + void LayerImplSelect::HandleTimerComplete(TimerList::Node * timer) { mTimerList.Remove(timer); mTimerPool.Invoke(timer); } -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH + +#elif CHIP_SYSTEM_CONFIG_USE_LIBEV + +void LayerImplSelect::HandleLibEvTimer(EV_P_ struct ev_timer * t, int revents) +{ + TimerList::Node * timer = static_cast(t->data); + VerifyOrDie(timer != nullptr); + LayerImplSelect * layerP = dynamic_cast(timer->mCallback.mSystemLayer); + VerifyOrDie(layerP != nullptr); + layerP->mTimerList.Remove(timer); + layerP->mTimerPool.Invoke(timer); +} + +void LayerImplSelect::HandleLibEvIoWatcher(EV_P_ struct ev_io * i, int revents) +{ + SocketWatch * watch = static_cast(i->data); + if (watch != nullptr && watch->mCallback != nullptr && watch->mLayerImplSelectP != nullptr) + { + SocketEvents events; + if (revents & EV_READ) + { + events.Set(SocketEventFlags::kRead); + } + if (revents & EV_WRITE) + { + events.Set(SocketEventFlags::kWrite); + } + if (events.HasAny()) + { + watch->mCallback(events, watch->mCallbackData); + } + } +} + +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV void LayerImplSelect::SocketWatch::Clear() { @@ -548,6 +689,8 @@ void LayerImplSelect::SocketWatch::Clear() #if CHIP_SYSTEM_CONFIG_USE_DISPATCH mRdSource = nullptr; mWrSource = nullptr; +#elif CHIP_SYSTEM_CONFIG_USE_LIBEV + mLayerImplSelectP = nullptr; #endif } @@ -566,7 +709,16 @@ void LayerImplSelect::SocketWatch::DisableAndClear() } Clear(); } -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH +#elif CHIP_SYSTEM_CONFIG_USE_LIBEV +void LayerImplSelect::SocketWatch::DisableAndClear() +{ + if (mLayerImplSelectP != nullptr && mLayerImplSelectP->mLibEvLoopP != nullptr) + { + ev_io_stop(mLayerImplSelectP->mLibEvLoopP, &mIoWatcher); + } + Clear(); +} +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV } // namespace System } // namespace chip diff --git a/src/system/SystemLayerImplSelect.h b/src/system/SystemLayerImplSelect.h index 4243d2aee9f5e1..0eb930eeb86c8d 100644 --- a/src/system/SystemLayerImplSelect.h +++ b/src/system/SystemLayerImplSelect.h @@ -29,6 +29,13 @@ #include #endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING +#if CHIP_SYSTEM_CONFIG_USE_LIBEV +#include +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH +#error "CHIP_SYSTEM_CONFIG_USE_LIBEV and CHIP_SYSTEM_CONFIG_USE_DISPATCH are mutually exclusive" +#endif +#endif // CHIP_SYSTEM_CONFIG_USE_LIBEV + #include #include #include @@ -73,7 +80,12 @@ class LayerImplSelect : public LayerSocketsLoop void SetDispatchQueue(dispatch_queue_t dispatchQueue) override { mDispatchQueue = dispatchQueue; }; dispatch_queue_t GetDispatchQueue() override { return mDispatchQueue; }; void HandleTimerComplete(TimerList::Node * timer); -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH +#elif CHIP_SYSTEM_CONFIG_USE_LIBEV + virtual void SetLibEvLoop(struct ev_loop * aLibEvLoopP) override { mLibEvLoopP = aLibEvLoopP; }; + virtual struct ev_loop * GetLibEvLoop() override { return mLibEvLoopP; }; + static void HandleLibEvTimer(EV_P_ struct ev_timer * t, int revents); + static void HandleLibEvIoWatcher(EV_P_ struct ev_io * i, int revents); +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV // Expose the result of WaitForEvents() for non-blocking socket implementations. bool IsSelectResultValid() const { return mSelectResult >= 0; } @@ -95,6 +107,12 @@ class LayerImplSelect : public LayerSocketsLoop dispatch_source_t mWrSource; void DisableAndClear(); #endif +#if CHIP_SYSTEM_CONFIG_USE_LIBEV + struct ev_io mIoWatcher; + LayerImplSelect * mLayerImplSelectP; + void DisableAndClear(); +#endif + intptr_t mCallbackData; }; SocketWatch mSocketWatchPool[kSocketWatchMax]; @@ -128,6 +146,8 @@ class LayerImplSelect : public LayerSocketsLoop #if CHIP_SYSTEM_CONFIG_USE_DISPATCH dispatch_queue_t mDispatchQueue = nullptr; +#elif CHIP_SYSTEM_CONFIG_USE_LIBEV + struct ev_loop * mLibEvLoopP; #endif }; diff --git a/src/system/SystemTimer.h b/src/system/SystemTimer.h index 54b07f8c092f49..cd27fefcf11ef2 100644 --- a/src/system/SystemTimer.h +++ b/src/system/SystemTimer.h @@ -64,6 +64,9 @@ class DLL_EXPORT TimerData Layer * GetSystemLayer() const { return mSystemLayer; } private: +#if CHIP_SYSTEM_CONFIG_USE_LIBEV + friend class LayerImplSelect; +#endif Layer * mSystemLayer; TimerCompleteCallback mOnComplete; void * mAppState; @@ -91,6 +94,9 @@ class DLL_EXPORT TimerData #if CHIP_SYSTEM_CONFIG_USE_DISPATCH friend class LayerImplSelect; dispatch_source_t mTimerSource = nullptr; +#elif CHIP_SYSTEM_CONFIG_USE_LIBEV + friend class LayerImplSelect; + struct ev_timer mLibEvTimer; #endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH // Not defined diff --git a/src/system/system.gni b/src/system/system.gni index e91dd0dee460d3..67bf9b61b8f881 100644 --- a/src/system/system.gni +++ b/src/system/system.gni @@ -37,7 +37,10 @@ declare_args() { } declare_args() { - # use the dispatch library + # do not use libev by default + chip_system_config_use_libev = false + + # use the dispatch library on darwin targets chip_system_config_use_dispatch = chip_system_config_use_sockets && (current_os == "mac" || current_os == "ios") }