-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SystemLayerImplSelect: add libev support: CHIP_SYSTEM_CONFIG_USE_LIBEV #22043
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
|
||
|
@@ -155,14 +180,28 @@ 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 | ||
if (mLibEvLoopP == nullptr) | ||
{ | ||
chipDie(); | ||
} | ||
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<double>(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) | ||
|
@@ -178,7 +217,13 @@ void LayerImplSelect::CancelTimer(TimerCompleteCallback onComplete, void * appSt | |
dispatch_source_cancel(timer->mTimerSource); | ||
dispatch_release(timer->mTimerSource); | ||
} | ||
#endif | ||
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV | ||
if (mLibEvLoopP == nullptr) | ||
{ | ||
chipDie(); | ||
} | ||
ev_timer_stop(mLibEvLoopP, &timer->mLibEvTimer); | ||
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH/LIBEV | ||
|
||
mTimerPool.Release(timer); | ||
Signal(); | ||
|
@@ -197,8 +242,12 @@ CHIP_ERROR LayerImplSelect::ScheduleWork(TimerCompleteCallback onComplete, void | |
}); | ||
return CHIP_NO_ERROR; | ||
} | ||
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH | ||
|
||
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV | ||
// just a timer with no delay | ||
return StartTimer(Clock::Timeout(0), onComplete, appState); | ||
Comment on lines
+246
to
+247
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will be buggy if someone calls ScheduleWork twice with the same args: it should schedule twice, but this will cancel the previous one, right? The Darwin impl gets this right. The select-based impl will get it right once #22375 merges. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It's beginning to dawn on me that this is more complicated - I wasn't aware that PostEvent also runs on dispatch, directly.
I guess you're right. I'll remove the things related to that Darwin/libev Frankenstein's monster type of setup from the PR ;-) After all, the libev support does not aim at replacing dispatch when it is available, but is useful for embedded linux targets like openwrt. Running the hybrid setup on the mac is just convenient for my develop-in-Xcode workflow, I can easily keep a few private patches for that.
Right. I'll have a look at #22375 and find a way to make the libev variant working in the same way. |
||
#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) | ||
CancelTimer(onComplete, appState); | ||
|
||
TimerList::Node * timer = mTimerPool.Create(*this, SystemClock().GetMonotonicTimestamp(), onComplete, appState); | ||
|
@@ -210,6 +259,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) | ||
|
@@ -231,6 +281,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<SocketWatchToken>(watch); | ||
return CHIP_NO_ERROR; | ||
|
@@ -283,6 +338,24 @@ CHIP_ERROR LayerImplSelect::RequestCallbackOnPendingRead(SocketWatchToken token) | |
dispatch_activate(watch->mRdSource); | ||
} | ||
} | ||
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV | ||
if (mLibEvLoopP == nullptr) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes I think we should stick to them being mutually exclusive and static_asserting that as needed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree. I started with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See new commit, now changed to #if/#elif throughout. |
||
{ | ||
chipDie(); | ||
} | ||
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 | ||
ev_io_modify(&watch->mIoWatcher, evs); | ||
} | ||
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH | ||
|
||
return CHIP_NO_ERROR; | ||
|
@@ -322,10 +395,27 @@ CHIP_ERROR LayerImplSelect::RequestCallbackOnPendingWrite(SocketWatchToken token | |
} | ||
}); | ||
// only now we are sure the source exists and can become active | ||
watch->mPendingIO.Set(SocketEventFlags::kWrite); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this being removed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, that was a functionally irrelevant leftover I found after #21349. Does not belong here, but was not worth a separate PR, and then I forgot. It originated in a then abandoned version of the code that tried to suspend/resume the source watching, and needed the flag to set late to avoid race conditions. But in the version accepted the flag is being set already at the top of the method. Same in |
||
dispatch_activate(watch->mWrSource); | ||
} | ||
} | ||
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV | ||
if (mLibEvLoopP == nullptr) | ||
{ | ||
chipDie(); | ||
} | ||
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 | ||
ev_io_modify(&watch->mIoWatcher, evs); | ||
} | ||
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH | ||
|
||
return CHIP_NO_ERROR; | ||
|
@@ -338,6 +428,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; | ||
} | ||
|
||
|
@@ -348,6 +446,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; | ||
} | ||
|
||
|
@@ -359,7 +465,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(); | ||
|
@@ -494,12 +600,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<TimerList::Node *>(t->data); | ||
VerifyOrDie(timer); | ||
LayerImplSelect * layerP = dynamic_cast<LayerImplSelect *>(timer->mCallback.mSystemLayer); | ||
VerifyOrDie(layerP); | ||
layerP->mTimerList.Remove(timer); | ||
layerP->mTimerPool.Invoke(timer); | ||
} | ||
|
||
void LayerImplSelect::HandleLibEvIoWatcher(EV_P_ struct ev_io * i, int revents) | ||
{ | ||
SocketWatch * watch = static_cast<SocketWatch *>(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() | ||
{ | ||
|
@@ -510,6 +651,8 @@ void LayerImplSelect::SocketWatch::Clear() | |
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH | ||
mRdSource = nullptr; | ||
mWrSource = nullptr; | ||
#elif CHIP_SYSTEM_CONFIG_USE_LIBEV | ||
mLayerImplSelectP = nullptr; | ||
#endif | ||
} | ||
|
||
|
@@ -528,7 +671,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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part is still not clear to me. If we are here (or more to the point if the System::Layer will look for this queue), then CHIP_SYSTEM_CONFIG_USE_DISPATCH is defined, right? But that's mutually exclusive with CHIP_SYSTEM_CONFIG_USE_LIBEV. So why do we need the conditioning here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we get here, we're on Darwin, which does have dispatch and could provide a queue to the system layer. But when we're compiling with
CHIP_SYSTEM_CONFIG_USE_LIBEV
(which is possible even under Darwin), the system layer does not have queue support, and does not have theSetDispatchQueue
method.For clarity, I guess it would be better to just use
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
here instead of#if !CHIP_SYSTEM_CONFIG_USE_LIBEV
. Under normal Darwin builds, that conditional is redundant, but it allows to use other mainloops such as the libev based one. Agree?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW: I fully agree this be better a "POSTPONED past 1.0" thing, that's why I posted it as a draft in the first place. I'm happy though about you looking at it right now, and I'm using and testing it daily in my setup right now, so by the time it gets a regular PR it should be reasonably mature :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that would be much more clear, since that's the thing we really care about: that the
SetDispatchQueue
method exists. OK.