From: Jérémie Galarneau Date: Wed, 17 May 2017 15:16:33 +0000 (-0400) Subject: Add lttng_waiter utils X-Git-Tag: v2.10.0-rc2~58 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=141204e233051639b7d8d9d1858ac440853cb835;p=lttng-tools.git Add lttng_waiter utils This utils is adapted from userspace-rcu's urcu-wait.h Signed-off-by: Jérémie Galarneau --- diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 2cf497262..bdba28f19 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -78,7 +78,8 @@ libcommon_la_SOURCES = error.h error.c utils.c utils.h runas.c runas.h \ action.c notify.c condition.c buffer-usage.c \ evaluation.c notification.c trigger.c endpoint.c \ dynamic-buffer.h dynamic-buffer.c \ - buffer-view.h buffer-view.c + buffer-view.h buffer-view.c \ + waiter.h waiter.c libcommon_la_LIBADD = \ $(top_builddir)/src/common/config/libconfig.la diff --git a/src/common/waiter.c b/src/common/waiter.c new file mode 100644 index 000000000..c5a282940 --- /dev/null +++ b/src/common/waiter.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 - Mathieu Desnoyers + * 2017 - Jérémie Galarneau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * This code is originally adapted from userspace-rcu's urcu-wait.h + */ + +#include "waiter.h" +#include +#include +#include +#include "error.h" +#include + +/* + * Number of busy-loop attempts before waiting on futex. + */ +#define WAIT_ATTEMPTS 1000 + +enum waiter_state { + /* WAITER_WAITING is compared directly (futex compares it). */ + WAITER_WAITING = 0, + /* non-zero are used as masks. */ + WAITER_WOKEN_UP = (1 << 0), + WAITER_RUNNING = (1 << 1), + WAITER_TEARDOWN = (1 << 2), +}; + +LTTNG_HIDDEN +void lttng_waiter_init(struct lttng_waiter *waiter) +{ + cds_wfs_node_init(&waiter->wait_queue_node); + uatomic_set(&waiter->state, WAITER_WAITING); + cmm_smp_mb(); +} + +/* + * User must init "waiter" before passing its memory to waker thread. + */ +LTTNG_HIDDEN +void lttng_waiter_wait(struct lttng_waiter *waiter) +{ + unsigned int i; + + DBG("Beginning of waiter wait period"); + /* Load and test condition before read state */ + cmm_smp_rmb(); + for (i = 0; i < WAIT_ATTEMPTS; i++) { + if (uatomic_read(&waiter->state) != WAITER_WAITING) { + goto skip_futex_wait; + } + caa_cpu_relax(); + } + while (futex_noasync(&waiter->state, FUTEX_WAIT, WAITER_WAITING, + NULL, NULL, 0)) { + switch (errno) { + case EWOULDBLOCK: + /* Value already changed. */ + goto skip_futex_wait; + case EINTR: + /* Retry if interrupted by signal. */ + break; /* Get out of switch. */ + default: + /* Unexpected error. */ + PERROR("futex_noasync"); + abort(); + } + } +skip_futex_wait: + + /* Tell waker thread than we are running. */ + uatomic_or(&waiter->state, WAITER_RUNNING); + + /* + * Wait until waker thread lets us know it's ok to tear down + * memory allocated for struct lttng_waiter. + */ + for (i = 0; i < WAIT_ATTEMPTS; i++) { + if (uatomic_read(&waiter->state) & WAITER_TEARDOWN) { + break; + } + caa_cpu_relax(); + } + while (!(uatomic_read(&waiter->state) & WAITER_TEARDOWN)) { + poll(NULL, 0, 10); + } + assert(uatomic_read(&waiter->state) & WAITER_TEARDOWN); + DBG("End of waiter wait period"); +} + +/* + * Note: lttng_waiter_wake needs waiter to stay allocated throughout its + * execution. In this scheme, the waiter owns the node memory, and we only allow + * it to free this memory when it sees the WAITER_TEARDOWN flag. + */ +LTTNG_HIDDEN +void lttng_waiter_wake_up(struct lttng_waiter *waiter) +{ + cmm_smp_mb(); + assert(uatomic_read(&waiter->state) == WAITER_WAITING); + uatomic_set(&waiter->state, WAITER_WOKEN_UP); + if (!(uatomic_read(&waiter->state) & WAITER_RUNNING)) { + if (futex_noasync(&waiter->state, FUTEX_WAKE, 1, + NULL, NULL, 0) < 0) { + PERROR("futex_noasync"); + abort(); + } + } + /* Allow teardown of struct urcu_wait memory. */ + uatomic_or(&waiter->state, WAITER_TEARDOWN); +} diff --git a/src/common/waiter.h b/src/common/waiter.h new file mode 100644 index 000000000..30eb38f83 --- /dev/null +++ b/src/common/waiter.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 - Mathieu Desnoyers + * 2017 - Jérémie Galarneau + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * This code is originally adapted from userspace-rcu's urcu-wait.h + */ + +#ifndef LTTNG_WAITER_H +#define LTTNG_WAITER_H + +#define _LGPL_SOURCE + +#include +#include +#include +#include "macros.h" + +struct lttng_waiter { + struct cds_wfs_node wait_queue_node; + int32_t state; +}; + +LTTNG_HIDDEN +void lttng_waiter_init(struct lttng_waiter *waiter); + +LTTNG_HIDDEN +void lttng_waiter_wait(struct lttng_waiter *waiter); + +/* + * lttng_waiter_wake_up must only be called by a single waker. + * It is invalid for multiple "wake" operations to be invoked + * on a single waiter without re-initializing it before. + */ +LTTNG_HIDDEN +void lttng_waiter_wake_up(struct lttng_waiter *waiter); + +#endif /* LTTNG_WAITER_H */