From d9de3cd38b17ad16fb6ad6f74e83f4700c5f2b49 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Bollo?= Date: Wed, 13 Feb 2019 20:14:52 +0100 Subject: [PATCH] evmgr: Isolate the event loop from jobs MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The event loop is renamed evmgr for "event manager" with the intention (1) still use evloop in jobs (2) to provide an abstract event manager/handler/loop abstraction. Change-Id: Ib1955f661f98df80e1c7be99e9fe26a1e06d78f6 Signed-off-by: José Bollo --- src/CMakeLists.txt | 1 + src/evmgr.c | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/evmgr.h | 30 ++++++++ src/jobs.c | 182 ++++++++------------------------------------ 4 files changed, 281 insertions(+), 152 deletions(-) create mode 100644 src/evmgr.c create mode 100644 src/evmgr.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3c9763d3..2985db4b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,6 +63,7 @@ SET(AFB_LIB_SOURCES afb-ws.c afb-wsj1.c afb-xreq.c + evmgr.c fdev.c fdev-epoll.c fdev-systemd.c diff --git a/src/evmgr.c b/src/evmgr.c new file mode 100644 index 00000000..2dc35e42 --- /dev/null +++ b/src/evmgr.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2016-2019 "IoT.bzh" + * Author José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "evmgr.h" +#include "verbose.h" +#include "systemd.h" + +/** Description of handled event loops */ +struct evmgr +{ + unsigned state; /**< encoded state */ + int efd; /**< event notification */ + void *holder; /**< holder of the evmgr */ + struct sd_event *sdev; /**< the systemd event loop */ +}; + +#define EVLOOP_STATE_WAIT 1U +#define EVLOOP_STATE_RUN 2U + +/** + * Run the event loop is set. + */ +void evmgr_run(struct evmgr *evmgr) +{ + int rc; + struct sd_event *se; + + evmgr->state = EVLOOP_STATE_WAIT|EVLOOP_STATE_RUN; + se = evmgr->sdev; + rc = sd_event_prepare(se); + if (rc < 0) { + errno = -rc; + CRITICAL("sd_event_prepare returned an error (state: %d): %m", sd_event_get_state(se)); + abort(); + } else { + if (rc == 0) { + rc = sd_event_wait(se, (uint64_t)(int64_t)-1); + if (rc < 0) { + errno = -rc; + ERROR("sd_event_wait returned an error (state: %d): %m", sd_event_get_state(se)); + } + } + evmgr->state = EVLOOP_STATE_RUN; + if (rc > 0) { + rc = sd_event_dispatch(se); + if (rc < 0) { + errno = -rc; + ERROR("sd_event_dispatch returned an error (state: %d): %m", sd_event_get_state(se)); + } + } + } + evmgr->state = 0; +} + +void evmgr_job_run(int signum, struct evmgr *evmgr) +{ + if (signum) + evmgr->state = 0; + else + evmgr_run(evmgr); +} + +int evmgr_can_run(struct evmgr *evmgr) +{ + return !evmgr->state; +} + +/** + * Internal callback for evmgr management. + * The effect of this function is hidden: it exits + * the waiting poll if any. + */ +static void evmgr_on_efd_event(struct evmgr *evmgr) +{ + uint64_t x; + read(evmgr->efd, &x, sizeof x); +} + +/** + * wakeup the event loop if needed by sending + * an event. + */ +void evmgr_wakeup(struct evmgr *evmgr) +{ + uint64_t x; + + if (evmgr->state & EVLOOP_STATE_WAIT) { + x = 1; + write(evmgr->efd, &x, sizeof x); + } +} + +/** + */ +void *evmgr_holder(struct evmgr *evmgr) +{ + return evmgr->holder; +} + +/** + */ +int evmgr_release_if(struct evmgr *evmgr, void *holder) +{ + if (evmgr->holder != holder) + return 0; + evmgr->holder = 0; + return 1; +} + +/** + */ +int evmgr_try_hold(struct evmgr *evmgr, void *holder) +{ + if (!evmgr->holder) + evmgr->holder = holder; + return evmgr->holder == holder; +} + +/******************************************************************************/ +/******************************************************************************/ +/****** SYSTEM D ******/ +/******************************************************************************/ +/******************************************************************************/ + +/** + * Internal callback for evmgr management. + * The effect of this function is hidden: it exits + * the waiting poll if any. Then it wakes up a thread + * awaiting the evmgr using signal. + */ +static int on_evmgr_efd(sd_event_source *s, int fd, uint32_t revents, void *userdata) +{ + struct evmgr *evmgr = userdata; + evmgr_on_efd_event(evmgr); + return 1; +} + +/** + * Gets a sd_event item for the current thread. + * @return a sd_event or NULL in case of error + */ +int evmgr_create(struct evmgr **result) +{ + int rc; + struct evmgr *evmgr; + + /* creates the evmgr on need */ + evmgr = malloc(sizeof *evmgr); + if (!evmgr) { + ERROR("out of memory"); + rc = -ENOMEM; + goto error; + } + + /* creates the eventfd for waking up polls */ + evmgr->efd = eventfd(0, EFD_CLOEXEC|EFD_SEMAPHORE); + if (evmgr->efd < 0) { + ERROR("can't make eventfd for events"); + rc = -errno; + goto error1; + } + /* create the systemd event loop */ + evmgr->sdev = systemd_get_event_loop(); + if (!evmgr->sdev) { + ERROR("can't make new event loop"); + goto error2; + } + /* put the eventfd in the event loop */ + rc = sd_event_add_io(evmgr->sdev, NULL, evmgr->efd, EPOLLIN, on_evmgr_efd, evmgr); + if (rc < 0) { + ERROR("can't register eventfd"); + goto error2; + } + + /* start the creation */ + evmgr->state = 0; + evmgr->holder = 0; + *result = evmgr; + return 0; + + +error2: + close(evmgr->efd); +error1: + free(evmgr); +error: + *result = 0; + return rc; +} + diff --git a/src/evmgr.h b/src/evmgr.h new file mode 100644 index 00000000..c78ea33f --- /dev/null +++ b/src/evmgr.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016-2019 "IoT.bzh" + * Author José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + + +struct evmgr; + +extern void evmgr_run(struct evmgr *evmgr); +extern void evmgr_job_run(int signum, struct evmgr *evmgr); +extern int evmgr_can_run(struct evmgr *evmgr); +extern void evmgr_wakeup(struct evmgr *evmgr); +extern int evmgr_release_if(struct evmgr *evmgr, void *holder); +extern int evmgr_try_hold(struct evmgr *evmgr, void *holder); +extern void *evmgr_holder(struct evmgr *evmgr); +extern int evmgr_create(struct evmgr **result); diff --git a/src/jobs.c b/src/jobs.c index 217c5391..83ca2ed4 100644 --- a/src/jobs.c +++ b/src/jobs.c @@ -32,6 +32,7 @@ #include #include "jobs.h" +#include "evmgr.h" #include "sig-monitor.h" #include "verbose.h" #include "systemd.h" @@ -56,18 +57,6 @@ struct job unsigned dropped: 1; /**< is removed ? */ }; -/** Description of handled event loops */ -struct evloop -{ - unsigned state; /**< encoded state */ - int efd; /**< event notification */ - struct sd_event *sdev; /**< the systemd event loop */ - struct thread *holder; /**< holder of the evloop */ -}; - -#define EVLOOP_STATE_WAIT 1U -#define EVLOOP_STATE_RUN 2U - /** Description of threads */ struct thread { @@ -115,7 +104,7 @@ static struct job *first_job; static struct job *free_jobs; /* event loop */ -static struct evloop evloop; +static struct evmgr *evmgr; /** * Create a new job with the given parameters @@ -253,71 +242,14 @@ static void job_cancel(int signum, void *arg) job->callback(SIGABRT, job->arg); } -/** - * Monitored normal callback for events. - * This function is called by the monitor - * to run the event loop when the safe environment - * is set. - * @param signum 0 on normal flow or the number - * of the signal that interrupted the normal - * flow - * @param arg the events to run - */ -static void evloop_run(int signum, void *arg) -{ - int rc; - struct sd_event *se; - - if (!signum) { - se = evloop.sdev; - rc = sd_event_prepare(se); - if (rc < 0) { - errno = -rc; - CRITICAL("sd_event_prepare returned an error (state: %d): %m", sd_event_get_state(se)); - abort(); - } else { - if (rc == 0) { - rc = sd_event_wait(se, (uint64_t)(int64_t)-1); - if (rc < 0) { - errno = -rc; - ERROR("sd_event_wait returned an error (state: %d): %m", sd_event_get_state(se)); - } - } - evloop.state = EVLOOP_STATE_RUN; - if (rc > 0) { - rc = sd_event_dispatch(se); - if (rc < 0) { - errno = -rc; - ERROR("sd_event_dispatch returned an error (state: %d): %m", sd_event_get_state(se)); - } - } - } - } -} - -/** - * Internal callback for evloop management. - * The effect of this function is hidden: it exits - * the waiting poll if any. - */ -static void evloop_on_efd_event() -{ - uint64_t x; - read(evloop.efd, &x, sizeof x); -} - /** * wakeup the event loop if needed by sending * an event. */ static void evloop_wakeup() { - uint64_t x; - - if (evloop.state & EVLOOP_STATE_WAIT) { - x = 1; - write(evloop.efd, &x, sizeof x); - } + if (evmgr) + evmgr_wakeup(evmgr); } /** @@ -327,11 +259,13 @@ static void evloop_release() { struct thread *nh, *ct = current_thread; - if (ct && evloop.holder == ct) { + if (ct && evmgr && evmgr_release_if(evmgr, ct)) { nh = ct->nholder; - evloop.holder = nh; - if (nh) + ct->nholder = 0; + if (nh) { + evmgr_try_hold(evmgr, nh); pthread_cond_signal(nh->cwhold); + } } } @@ -340,17 +274,7 @@ static void evloop_release() */ static int evloop_get() { - struct thread *ct = current_thread; - - if (evloop.holder) - return evloop.holder == ct; - - if (!evloop.sdev) - return 0; - - ct->nholder = NULL; - evloop.holder = ct; - return 1; + return evmgr && evmgr_try_hold(evmgr, current_thread); } /** @@ -358,7 +282,7 @@ static int evloop_get() */ static void evloop_acquire() { - struct thread **pwait, *ct; + struct thread *pwait, *ct; pthread_cond_t cond; /* try to get the evloop */ @@ -370,10 +294,10 @@ static void evloop_acquire() pthread_cond_init(&cond, NULL); /* queue current thread in holder list */ - pwait = &evloop.holder; - while (*pwait) - pwait = &(*pwait)->nholder; - *pwait = ct; + pwait = evmgr_holder(evmgr); + while (pwait->nholder) + pwait = pwait->nholder; + pwait->nholder = ct; /* wake up the evloop */ evloop_wakeup(); @@ -395,6 +319,7 @@ static void thread_enter(volatile struct thread *me) me->tid = pthread_self(); me->stop = 0; me->waits = 0; + me->nholder = 0; me->upper = current_thread; me->next = threads; threads = (struct thread*)me; @@ -454,17 +379,15 @@ static void thread_run_internal(volatile struct thread *me) job_release(job); /* no job, check event loop wait */ } else if (evloop_get()) { - if (evloop.state != 0) { + if (!evmgr_can_run(evmgr)) { /* busy ? */ CRITICAL("Can't enter dispatch while in dispatch!"); abort(); } /* run the events */ - evloop.state = EVLOOP_STATE_RUN|EVLOOP_STATE_WAIT; pthread_mutex_unlock(&mutex); - sig_monitor(0, evloop_run, NULL); + sig_monitor(0, (void(*)(int,void*))evmgr_job_run, evmgr); pthread_mutex_lock(&mutex); - evloop.state = 0; } else { /* no job and no event loop */ running--; @@ -753,60 +676,6 @@ int jobs_call( return do_sync(group, timeout, call_cb, &sync); } -/** - * Internal callback for evloop management. - * The effect of this function is hidden: it exits - * the waiting poll if any. Then it wakes up a thread - * awaiting the evloop using signal. - */ -static int on_evloop_efd(sd_event_source *s, int fd, uint32_t revents, void *userdata) -{ - evloop_on_efd_event(); - return 1; -} - -/** - * Gets a sd_event item for the current thread. - * @return a sd_event or NULL in case of error - */ -static struct sd_event *get_sd_event_locked() -{ - int rc; - - /* creates the evloop on need */ - if (!evloop.sdev) { - /* start the creation */ - evloop.state = 0; - /* creates the eventfd for waking up polls */ - evloop.efd = eventfd(0, EFD_CLOEXEC|EFD_SEMAPHORE); - if (evloop.efd < 0) { - ERROR("can't make eventfd for events"); - goto error1; - } - /* create the systemd event loop */ - evloop.sdev = systemd_get_event_loop(); - if (!evloop.sdev) { - ERROR("can't make event loop"); - goto error2; - } - /* put the eventfd in the event loop */ - rc = sd_event_add_io(evloop.sdev, NULL, evloop.efd, EPOLLIN, on_evloop_efd, NULL); - if (rc < 0) { - ERROR("can't register eventfd"); - evloop.sdev = NULL; -error2: - close(evloop.efd); -error1: - return NULL; - } - } - - /* acquire the event loop */ - evloop_acquire(); - - return evloop.sdev; -} - /** * Ensure that the current running thread can control the event loop. */ @@ -820,9 +689,18 @@ void jobs_acquire_event_manager() current_thread = < } - /* process */ + /* lock */ pthread_mutex_lock(&mutex); - get_sd_event_locked(); + + /* creates the evloop on need */ + if (!evmgr) + evmgr_create(&evmgr); + + /* acquire the event loop under lock */ + if (evmgr) + evloop_acquire(); + + /* unlock */ pthread_mutex_unlock(&mutex); /* release the faked thread environment if needed */ @@ -835,7 +713,7 @@ void jobs_acquire_event_manager() * A workaround to achieve that goal is for the caller to * require the event loop a second time after having modified it. */ - NOTICE("Requiring sd_event loop out of binder callbacks is hazardous!"); + NOTICE("Requiring event manager/loop from outside of binder's callback is hazardous!"); if (verbose_wants(Log_Level_Info)) sig_monitor_dumpstack(); evloop_release(); -- 2.16.6