Skip to content

Commit 1e1d426

Browse files
committed
SystemLayerImplSelect: add libev support when CHIP_SYSTEM_CONFIG_USE_LIBEV=1
When CHIP_SYSTEM_CONFIG_USE_LIBEV is set, SystemLayerImplSelect expects a *libev* mainloop to be present to schedule timers and socket watches (similar to CHIP_SYSTEM_CONFIG_USE_DISPATCH for Darwin). A libev mainloop must be passed to SystemLayer using `SetLibEvLoop()` before any timers or socket watches are used - otherwise, `chipDie()` is invoked. # Usage The entire project needs to be build with `CHIP_SYSTEM_CONFIG_USE_LIBEV=1` (this can be done via invoking a project-specific extra config via the `default_configs_extra` argument in args.gni) Setting up the libev mainloop and handing it over to SystemLayer must be done in application specific code, outside the code provided by chip examples. Also adding libev as a dependency must be done in the application's BUILD.gn. # Background *libev* is a multi-platform event library often used in embedded linux context to handle events, and builds the basis for various libraries with non-blocking APIs. This changeset allows using the *connectedhomeip* stack with libev based applications. # Example The opensource bridge project p44mbrd (https://github.com/plan44/p44mbrd) is based on libev and makes use of this changeset.
1 parent 9a1c31c commit 1e1d426

8 files changed

Lines changed: 213 additions & 14 deletions

File tree

src/platform/Darwin/PlatformManagerImpl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@ CHIP_ERROR PlatformManagerImpl::_InitChipStack()
5252
SuccessOrExit(err);
5353
#endif // CHIP_DISABLE_PLATFORM_KVS
5454

55+
#if !CHIP_SYSTEM_CONFIG_USE_LIBEV
5556
// Ensure there is a dispatch queue available
5657
static_cast<System::LayerSocketsLoop &>(DeviceLayer::SystemLayer()).SetDispatchQueue(GetWorkQueue());
58+
#endif
5759

5860
// Call _InitChipStack() on the generic implementation base class
5961
// to finish the initialization process.

src/platform/Darwin/SystemPlatformConfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,14 @@ 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
3740
#define CHIP_SYSTEM_CONFIG_POSIX_LOCKING 0
3841
#define CHIP_SYSTEM_CONFIG_FREERTOS_LOCKING 0
3942
#define CHIP_SYSTEM_CONFIG_NO_LOCKING 1
43+
#endif
44+
4045
#define CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME 1
4146
#define CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS 1
4247
#define CHIP_SYSTEM_CONFIG_POOL_USE_HEAP 1

src/system/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ buildconfig_header("system_buildconfig") {
7777
"CHIP_SYSTEM_CONFIG_TEST=${chip_build_tests}",
7878
"CHIP_WITH_NLFAULTINJECTION=${chip_with_nlfaultinjection}",
7979
"CHIP_SYSTEM_CONFIG_USE_DISPATCH=${chip_system_config_use_dispatch}",
80+
"CHIP_SYSTEM_CONFIG_USE_LIBEV=${chip_system_config_use_libev}",
8081
"CHIP_SYSTEM_CONFIG_USE_LWIP=${chip_system_config_use_lwip}",
8182
"CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT=${chip_system_config_use_open_thread_inet_endpoints}",
8283
"CHIP_SYSTEM_CONFIG_USE_SOCKETS=${chip_system_config_use_sockets}",

src/system/SystemLayer.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@
4343

4444
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
4545
#include <dispatch/dispatch.h>
46-
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
46+
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV
47+
#include <ev.h>
48+
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV
4749

4850
#include <utility>
4951

@@ -250,7 +252,9 @@ class LayerSocketsLoop : public LayerSockets
250252
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
251253
virtual void SetDispatchQueue(dispatch_queue_t dispatchQueue) = 0;
252254
virtual dispatch_queue_t GetDispatchQueue() = 0;
253-
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
255+
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV
256+
virtual void SetLibEvLoop(struct ev_loop * aLibEvLoopP) = 0;
257+
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV
254258
};
255259

256260
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS

src/system/SystemLayerImplSelect.cpp

Lines changed: 169 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@
3535
#define PTHREAD_NULL 0
3636
#endif // CHIP_SYSTEM_CONFIG_POSIX_LOCKING && !defined(PTHREAD_NULL)
3737

38+
#if CHIP_SYSTEM_CONFIG_USE_LIBEV
39+
// older libev do not yet have ev_io_modify
40+
#ifndef ev_io_modify
41+
#define ev_io_modify(ev, events_) \
42+
do \
43+
{ \
44+
(ev)->events = ((ev)->events & EV__IOFDSET) | (events_); \
45+
} while (0)
46+
#endif // ev_io_modify
47+
#endif // CHIP_SYSTEM_CONFIG_USE_LIBEV
48+
3849
namespace chip {
3950
namespace System {
4051

@@ -82,11 +93,25 @@ void LayerImplSelect::Shutdown()
8293
{
8394
w.DisableAndClear();
8495
}
96+
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV
97+
TimerList::Node * timer;
98+
while ((timer = mTimerList.PopEarliest()) != nullptr)
99+
{
100+
if (ev_is_active(&timer->mLibEvTimer))
101+
{
102+
ev_timer_stop(mLibEvLoopP, &timer->mLibEvTimer);
103+
}
104+
}
105+
mTimerPool.ReleaseAll();
85106

86-
#else // CHIP_SYSTEM_CONFIG_USE_DISPATCH
107+
for (auto & w : mSocketWatchPool)
108+
{
109+
w.DisableAndClear();
110+
}
111+
#else
87112
mTimerList.Clear();
88113
mTimerPool.ReleaseAll();
89-
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
114+
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV
90115

91116
mWakeEvent.Close(*this);
92117

@@ -155,14 +180,28 @@ CHIP_ERROR LayerImplSelect::StartTimer(Clock::Timeout delay, TimerCompleteCallba
155180
dispatch_resume(timerSource);
156181
return CHIP_NO_ERROR;
157182
}
158-
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
159-
183+
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV
184+
if (mLibEvLoopP == nullptr)
185+
{
186+
chipDie();
187+
}
188+
ev_timer_init(&timer->mLibEvTimer, &LayerImplSelect::HandleLibEvTimer, 1, 0);
189+
timer->mLibEvTimer.data = timer;
190+
auto t = Clock::Milliseconds64(delay).count();
191+
ev_timer_set(&timer->mLibEvTimer, static_cast<double>(t) / 1E3, 0.);
192+
(void) mTimerList.Add(timer);
193+
ev_timer_start(mLibEvLoopP, &timer->mLibEvTimer);
194+
return CHIP_NO_ERROR;
195+
#endif
196+
#if !CHIP_SYSTEM_CONFIG_USE_LIBEV
197+
// Note: dispatch based implementation needs this as fallback, but not LIBEV (and dead code is not allowed with -Werror)
160198
if (mTimerList.Add(timer) == timer)
161199
{
162200
// The new timer is the earliest, so the time until the next event has probably changed.
163201
Signal();
164202
}
165203
return CHIP_NO_ERROR;
204+
#endif // !CHIP_SYSTEM_CONFIG_USE_LIBEV
166205
}
167206

168207
void LayerImplSelect::CancelTimer(TimerCompleteCallback onComplete, void * appState)
@@ -186,7 +225,13 @@ void LayerImplSelect::CancelTimer(TimerCompleteCallback onComplete, void * appSt
186225
dispatch_source_cancel(timer->mTimerSource);
187226
dispatch_release(timer->mTimerSource);
188227
}
189-
#endif
228+
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV
229+
if (mLibEvLoopP == nullptr)
230+
{
231+
chipDie();
232+
}
233+
ev_timer_stop(mLibEvLoopP, &timer->mLibEvTimer);
234+
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV
190235

191236
mTimerPool.Release(timer);
192237
Signal();
@@ -205,7 +250,12 @@ CHIP_ERROR LayerImplSelect::ScheduleWork(TimerCompleteCallback onComplete, void
205250
});
206251
return CHIP_NO_ERROR;
207252
}
208-
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
253+
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV
254+
// just a timer with no delay
255+
return StartTimer(Clock::Timeout(0), onComplete, appState);
256+
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV
257+
#if !CHIP_SYSTEM_CONFIG_USE_LIBEV
258+
// Note: dispatch based implementation needs this as fallback, but not LIBEV (and dead code is not allowed with -Werror)
209259

210260
// Ideally we would not use a timer here at all, but if we try to just
211261
// ScheduleLambda the lambda needs to capture the following:
@@ -241,6 +291,7 @@ CHIP_ERROR LayerImplSelect::ScheduleWork(TimerCompleteCallback onComplete, void
241291
Signal();
242292
}
243293
return CHIP_NO_ERROR;
294+
#endif // !CHIP_SYSTEM_CONFIG_USE_LIBEV
244295
}
245296

246297
CHIP_ERROR LayerImplSelect::StartWatchingSocket(int fd, SocketWatchToken * tokenOut)
@@ -262,6 +313,11 @@ CHIP_ERROR LayerImplSelect::StartWatchingSocket(int fd, SocketWatchToken * token
262313
VerifyOrReturnError(watch != nullptr, CHIP_ERROR_ENDPOINT_POOL_FULL);
263314

264315
watch->mFD = fd;
316+
#if CHIP_SYSTEM_CONFIG_USE_LIBEV
317+
ev_io_init(&watch->mIoWatcher, &LayerImplSelect::HandleLibEvIoWatcher, 0, 0);
318+
watch->mIoWatcher.data = watch;
319+
watch->mLayerImplSelectP = this;
320+
#endif
265321

266322
*tokenOut = reinterpret_cast<SocketWatchToken>(watch);
267323
return CHIP_NO_ERROR;
@@ -314,6 +370,27 @@ CHIP_ERROR LayerImplSelect::RequestCallbackOnPendingRead(SocketWatchToken token)
314370
dispatch_activate(watch->mRdSource);
315371
}
316372
}
373+
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV
374+
if (mLibEvLoopP == nullptr)
375+
{
376+
chipDie();
377+
}
378+
int evs = (watch->mPendingIO.Has(SocketEventFlags::kRead) ? EV_READ : 0) |
379+
(watch->mPendingIO.Has(SocketEventFlags::kWrite) ? EV_WRITE : 0);
380+
if (!ev_is_active(&watch->mIoWatcher))
381+
{
382+
// First time actually using that watch
383+
ev_io_set(&watch->mIoWatcher, watch->mFD, evs);
384+
ev_io_start(mLibEvLoopP, &watch->mIoWatcher);
385+
}
386+
else
387+
{
388+
// already active, just change flags
389+
// Note: changing flags only reliably works when the watcher is stopped
390+
ev_io_stop(mLibEvLoopP, &watch->mIoWatcher);
391+
ev_io_modify(&watch->mIoWatcher, evs);
392+
ev_io_start(mLibEvLoopP, &watch->mIoWatcher);
393+
}
317394
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
318395

319396
return CHIP_NO_ERROR;
@@ -353,10 +430,30 @@ CHIP_ERROR LayerImplSelect::RequestCallbackOnPendingWrite(SocketWatchToken token
353430
}
354431
});
355432
// only now we are sure the source exists and can become active
356-
watch->mPendingIO.Set(SocketEventFlags::kWrite);
357433
dispatch_activate(watch->mWrSource);
358434
}
359435
}
436+
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV
437+
if (mLibEvLoopP == nullptr)
438+
{
439+
chipDie();
440+
}
441+
int evs = (watch->mPendingIO.Has(SocketEventFlags::kRead) ? EV_READ : 0) |
442+
(watch->mPendingIO.Has(SocketEventFlags::kWrite) ? EV_WRITE : 0);
443+
if (!ev_is_active(&watch->mIoWatcher))
444+
{
445+
// First time actually using that watch
446+
ev_io_set(&watch->mIoWatcher, watch->mFD, evs);
447+
ev_io_start(mLibEvLoopP, &watch->mIoWatcher);
448+
}
449+
else
450+
{
451+
// already active, just change flags
452+
// Note: changing flags only reliably works when the watcher is stopped
453+
ev_io_stop(mLibEvLoopP, &watch->mIoWatcher);
454+
ev_io_modify(&watch->mIoWatcher, evs);
455+
ev_io_start(mLibEvLoopP, &watch->mIoWatcher);
456+
}
360457
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
361458

362459
return CHIP_NO_ERROR;
@@ -369,6 +466,14 @@ CHIP_ERROR LayerImplSelect::ClearCallbackOnPendingRead(SocketWatchToken token)
369466

370467
watch->mPendingIO.Clear(SocketEventFlags::kRead);
371468

469+
#if CHIP_SYSTEM_CONFIG_USE_LIBEV
470+
if (ev_is_active(&watch->mIoWatcher) && watch->mPendingIO.Raw() == 0)
471+
{
472+
// all flags cleared now, stop watching
473+
ev_io_stop(mLibEvLoopP, &watch->mIoWatcher);
474+
}
475+
#endif
476+
372477
return CHIP_NO_ERROR;
373478
}
374479

@@ -379,6 +484,14 @@ CHIP_ERROR LayerImplSelect::ClearCallbackOnPendingWrite(SocketWatchToken token)
379484

380485
watch->mPendingIO.Clear(SocketEventFlags::kWrite);
381486

487+
#if CHIP_SYSTEM_CONFIG_USE_LIBEV
488+
if (ev_is_active(&watch->mIoWatcher) && watch->mPendingIO.Raw() == 0)
489+
{
490+
// all flags cleared now, stop watching
491+
ev_io_stop(mLibEvLoopP, &watch->mIoWatcher);
492+
}
493+
#endif
494+
382495
return CHIP_NO_ERROR;
383496
}
384497

@@ -390,7 +503,7 @@ CHIP_ERROR LayerImplSelect::StopWatchingSocket(SocketWatchToken * tokenInOut)
390503
VerifyOrReturnError(watch != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
391504
VerifyOrReturnError(watch->mFD >= 0, CHIP_ERROR_INCORRECT_STATE);
392505

393-
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
506+
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH || CHIP_SYSTEM_CONFIG_USE_LIBEV
394507
watch->DisableAndClear();
395508
#else
396509
watch->Clear();
@@ -526,12 +639,47 @@ void LayerImplSelect::HandleEvents()
526639
}
527640

528641
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
642+
529643
void LayerImplSelect::HandleTimerComplete(TimerList::Node * timer)
530644
{
531645
mTimerList.Remove(timer);
532646
mTimerPool.Invoke(timer);
533647
}
534-
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
648+
649+
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV
650+
651+
void LayerImplSelect::HandleLibEvTimer(EV_P_ struct ev_timer * t, int revents)
652+
{
653+
TimerList::Node * timer = static_cast<TimerList::Node *>(t->data);
654+
VerifyOrDie(timer);
655+
LayerImplSelect * layerP = dynamic_cast<LayerImplSelect *>(timer->mCallback.mSystemLayer);
656+
VerifyOrDie(layerP);
657+
layerP->mTimerList.Remove(timer);
658+
layerP->mTimerPool.Invoke(timer);
659+
}
660+
661+
void LayerImplSelect::HandleLibEvIoWatcher(EV_P_ struct ev_io * i, int revents)
662+
{
663+
SocketWatch * watch = static_cast<SocketWatch *>(i->data);
664+
if (watch != nullptr && watch->mCallback != nullptr && watch->mLayerImplSelectP != nullptr)
665+
{
666+
SocketEvents events;
667+
if (revents & EV_READ)
668+
{
669+
events.Set(SocketEventFlags::kRead);
670+
}
671+
if (revents & EV_WRITE)
672+
{
673+
events.Set(SocketEventFlags::kWrite);
674+
}
675+
if (events.HasAny())
676+
{
677+
watch->mCallback(events, watch->mCallbackData);
678+
}
679+
}
680+
}
681+
682+
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV
535683

536684
void LayerImplSelect::SocketWatch::Clear()
537685
{
@@ -542,6 +690,8 @@ void LayerImplSelect::SocketWatch::Clear()
542690
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
543691
mRdSource = nullptr;
544692
mWrSource = nullptr;
693+
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV
694+
mLayerImplSelectP = nullptr;
545695
#endif
546696
}
547697

@@ -560,7 +710,16 @@ void LayerImplSelect::SocketWatch::DisableAndClear()
560710
}
561711
Clear();
562712
}
563-
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
713+
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV
714+
void LayerImplSelect::SocketWatch::DisableAndClear()
715+
{
716+
if (mLayerImplSelectP != nullptr && mLayerImplSelectP->mLibEvLoopP != nullptr)
717+
{
718+
ev_io_stop(mLayerImplSelectP->mLibEvLoopP, &mIoWatcher);
719+
}
720+
Clear();
721+
}
722+
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV
564723

565724
} // namespace System
566725
} // namespace chip

0 commit comments

Comments
 (0)