From 807998c0d33fc9ce0c219a09a4454319b7385033 Mon Sep 17 00:00:00 2001 From: Arnaud de Turckheim Date: Mon, 4 May 2026 17:22:16 +0200 Subject: [PATCH] upump-ev: use ev_async to notify remote event loop Use ev_async to notify remote event loop when an event is started from another loop. From man ev: ev_set_loop_release_cb [...] While event loop modifications are allowed between invocations of "release" and "acquire" (that's their only purpose after all), no modifications done will affect the event loop, i.e. adding watchers will have no effect on the set of file descriptors being watched, or the time waited. Use an "ev_async" watcher to wake up "ev_run" when you want it to take note of any changes you made. [...] Calling a control command on a worker pipe may freeze the remote pipe thread and forward the control command to it (for instance when calling upipe_set_uri on a worker wrapping a source pipe). This control command may need to add an upump in the remote event loop (for instance an idler for initialization). If so, when thawing the remote pipe, the remote event loop do not take account for this upump and the upump callback will not be triggered when needed. Using ev_async will force the remote event loop to wake up and take account for this new upump. --- lib/upump-ev/upump_ev.c | 51 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/lib/upump-ev/upump_ev.c b/lib/upump-ev/upump_ev.c index 4f1af0f39..ce3a2b596 100644 --- a/lib/upump-ev/upump_ev.c +++ b/lib/upump-ev/upump_ev.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2012-2018 OpenHeadend S.A.R.L. + * Copyright (C) 2026 EasyTools * * Authors: Christophe Massiot * @@ -30,9 +31,16 @@ struct upump_ev_mgr { /** ev private structure */ struct ev_loop *ev_loop; + /** watcher to wake up the event loop from a remote event loop */ + struct ev_async ev_async; /** true if the loop has to be destroyed at the end */ bool destroy; + /** current mutex used if any */ + struct umutex *umutex; + /** current ev_async configured if any */ + struct ev_async *async; + /** common structure */ struct upump_common_mgr common_mgr; @@ -215,6 +223,10 @@ static void upump_ev_real_start(struct upump *upump, bool status) } if (!status) ev_unref(ev_mgr->ev_loop); + + if (ev_mgr->async) + /* wake up remote event loop to take account for this event */ + ev_async_send(ev_mgr->ev_loop, ev_mgr->async); } /** @This stops a pump. @@ -373,8 +385,9 @@ static int upump_ev_control(struct upump *upump, int command, va_list args) **/ static void upump_ev_mgr_lock(struct ev_loop *loop) { - struct umutex *mutex = ev_userdata(loop); - umutex_lock(mutex); + struct upump_ev_mgr *ev_mgr = ev_userdata(loop); + umutex_lock(ev_mgr->umutex); + ev_mgr->async = NULL; } /** @internal @This is called when the event loop goes to sleep. @@ -383,8 +396,24 @@ static void upump_ev_mgr_lock(struct ev_loop *loop) **/ static void upump_ev_mgr_unlock(struct ev_loop *loop) { - struct umutex *mutex = ev_userdata(loop); - umutex_unlock(mutex); + struct upump_ev_mgr *ev_mgr = ev_userdata(loop); + /* configure async event so remote event loops can wake us up if they start + * events in this event loop. + * + * From man ev: + * + * ev_set_loop_release_cb + * [...] + * While event loop modifications are allowed between invocations of + * "release" and "acquire" (that's their only purpose after all), no + * modifications done will affect the event loop, i.e. adding watchers + * will have no effect on the set of file descriptors being watched, or + * the time waited. Use an "ev_async" watcher to wake up "ev_run" when you + * want it to take note of any changes you made. + * [...] + */ + ev_mgr->async = &ev_mgr->ev_async; + umutex_unlock(ev_mgr->umutex); } /** @internal @This runs an event loop. @@ -397,9 +426,10 @@ static void upump_ev_mgr_unlock(struct ev_loop *loop) static int upump_ev_mgr_run(struct upump_mgr *mgr, struct umutex *mutex) { struct upump_ev_mgr *ev_mgr = upump_ev_mgr_from_upump_mgr(mgr); + ev_mgr->umutex = mutex; if (mutex != NULL) { - ev_set_userdata(ev_mgr->ev_loop, mutex); + ev_set_userdata(ev_mgr->ev_loop, ev_mgr); ev_set_loop_release_cb(ev_mgr->ev_loop, upump_ev_mgr_unlock, upump_ev_mgr_lock); @@ -456,6 +486,13 @@ static void upump_ev_mgr_free(struct urefcount *urefcount) free(ev_mgr); } +/** @internal @This is called when an async ev is raised. */ +static void upump_ev_mgr_async_cb(struct ev_loop *loop, struct ev_async *w, + int revents) +{ + /* nothing to do */ +} + /** @This allocates and initializes a upump_ev_mgr structure. * * @param ev_loop pointer to an ev loop @@ -490,6 +527,10 @@ struct upump_mgr *upump_ev_mgr_alloc(struct ev_loop *ev_loop, ev_mgr->ev_loop = ev_loop; ev_mgr->destroy = false; + ev_mgr->async = NULL; + ev_async_init(&ev_mgr->ev_async, upump_ev_mgr_async_cb); + ev_async_start(ev_loop, &ev_mgr->ev_async); + ev_unref(ev_loop); return mgr; }