[Mesa-dev] [PATCH v2 04/26] util/u_queue: add util_queue_fence_wait_timeout
Marek Olšák
maraeo at gmail.com
Mon Nov 6 16:41:06 UTC 2017
Reviewed-by: Marek Olšák <marek.olsak at amd.com>
Marek
On Mon, Nov 6, 2017 at 11:23 AM, Nicolai Hähnle <nhaehnle at gmail.com> wrote:
> From: Nicolai Hähnle <nicolai.haehnle at amd.com>
>
> v2:
> - style fixes
> - fix missing timeout handling in futex path
>
> Reviewed-by: Marek Olšák <marek.olsak at amd.com> (v1)
> ---
> src/util/futex.h | 9 ++++--
> src/util/simple_mtx.h | 2 +-
> src/util/u_queue.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++-
> src/util/u_queue.h | 54 ++++++++++++++++++++-------------
> 4 files changed, 121 insertions(+), 26 deletions(-)
>
> diff --git a/src/util/futex.h b/src/util/futex.h
> index fa42cf4cf59..330e18f03a2 100644
> --- a/src/util/futex.h
> +++ b/src/util/futex.h
> @@ -26,33 +26,36 @@
>
> #if defined(HAVE_FUTEX)
>
> #include <limits.h>
> #include <stdint.h>
> #include <unistd.h>
> #include <linux/futex.h>
> #include <sys/syscall.h>
> #include <sys/time.h>
>
> -static inline long sys_futex(void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3)
> +static inline long sys_futex(void *addr1, int op, int val1, const struct timespec *timeout, void *addr2, int val3)
> {
> return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
> }
>
> static inline int futex_wake(uint32_t *addr)
> {
> return sys_futex(addr, FUTEX_WAKE, 1, NULL, NULL, 0);
> }
>
> static inline int futex_wake_all(uint32_t *addr)
> {
> return sys_futex(addr, FUTEX_WAKE, INT_MAX, NULL, NULL, 0);
> }
>
> -static inline int futex_wait(uint32_t *addr, int32_t value)
> +static inline int futex_wait(uint32_t *addr, int32_t value, const struct timespec *timeout)
> {
> - return sys_futex(addr, FUTEX_WAIT, value, NULL, NULL, 0);
> + /* FUTEX_WAIT_BITSET with FUTEX_BITSET_MATCH_ANY is equivalent to
> + * FUTEX_WAIT, except that it treats the timeout as absolute. */
> + return sys_futex(addr, FUTEX_WAIT_BITSET, value, timeout, NULL,
> + FUTEX_BITSET_MATCH_ANY);
> }
>
> #endif
>
> #endif /* UTIL_FUTEX_H */
> diff --git a/src/util/simple_mtx.h b/src/util/simple_mtx.h
> index 0c2602d03b6..4186a2d4d64 100644
> --- a/src/util/simple_mtx.h
> +++ b/src/util/simple_mtx.h
> @@ -72,21 +72,21 @@ simple_mtx_destroy(simple_mtx_t *mtx)
> static inline void
> simple_mtx_lock(simple_mtx_t *mtx)
> {
> uint32_t c;
>
> c = __sync_val_compare_and_swap(&mtx->val, 0, 1);
> if (__builtin_expect(c != 0, 0)) {
> if (c != 2)
> c = __sync_lock_test_and_set(&mtx->val, 2);
> while (c != 0) {
> - futex_wait(&mtx->val, 2);
> + futex_wait(&mtx->val, 2, NULL);
> c = __sync_lock_test_and_set(&mtx->val, 2);
> }
> }
> }
>
> static inline void
> simple_mtx_unlock(simple_mtx_t *mtx)
> {
> uint32_t c;
>
> diff --git a/src/util/u_queue.c b/src/util/u_queue.c
> index 706ee8b04d9..43c28ac6ef8 100644
> --- a/src/util/u_queue.c
> +++ b/src/util/u_queue.c
> @@ -19,20 +19,23 @@
> * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
> * USE OR OTHER DEALINGS IN THE SOFTWARE.
> *
> * The above copyright notice and this permission notice (including the
> * next paragraph) shall be included in all copies or substantial portions
> * of the Software.
> */
>
> #include "u_queue.h"
>
> +#include <time.h>
> +
> +#include "util/os_time.h"
> #include "util/u_string.h"
> #include "util/u_thread.h"
>
> static void util_queue_killall_and_wait(struct util_queue *queue);
>
> /****************************************************************************
> * Wait for all queues to assert idle when exit() is called.
> *
> * Otherwise, C++ static variable destructors can be called while threads
> * are using the static variables.
> @@ -84,39 +87,116 @@ remove_from_atexit_list(struct util_queue *queue)
> break;
> }
> }
> mtx_unlock(&exit_mutex);
> }
>
> /****************************************************************************
> * util_queue_fence
> */
>
> +#ifdef UTIL_QUEUE_FENCE_FUTEX
> +static bool
> +do_futex_fence_wait(struct util_queue_fence *fence,
> + bool timeout, int64_t abs_timeout)
> +{
> + uint32_t v = fence->val;
> + struct timespec ts;
> + ts.tv_sec = abs_timeout / (1000*1000*1000);
> + ts.tv_nsec = abs_timeout % (1000*1000*1000);
> +
> + while (v != 0) {
> + if (v != 2) {
> + v = p_atomic_cmpxchg(&fence->val, 1, 2);
> + if (v == 0)
> + return true;
> + }
> +
> + int r = futex_wait(&fence->val, 2, timeout ? &ts : NULL);
> + if (timeout && r < 0) {
> + if (errno == -ETIMEDOUT)
> + return false;
> + }
> +
> + v = fence->val;
> + }
> +
> + return true;
> +}
> +
> +void
> +_util_queue_fence_wait(struct util_queue_fence *fence)
> +{
> + do_futex_fence_wait(fence, false, 0);
> +}
> +
> +bool
> +_util_queue_fence_wait_timeout(struct util_queue_fence *fence,
> + int64_t abs_timeout)
> +{
> + return do_futex_fence_wait(fence, true, abs_timeout);
> +}
> +
> +#endif
> +
> #ifdef UTIL_QUEUE_FENCE_STANDARD
> void
> util_queue_fence_signal(struct util_queue_fence *fence)
> {
> mtx_lock(&fence->mutex);
> fence->signalled = true;
> cnd_broadcast(&fence->cond);
> mtx_unlock(&fence->mutex);
> }
>
> void
> -util_queue_fence_wait(struct util_queue_fence *fence)
> +_util_queue_fence_wait(struct util_queue_fence *fence)
> {
> mtx_lock(&fence->mutex);
> while (!fence->signalled)
> cnd_wait(&fence->cond, &fence->mutex);
> mtx_unlock(&fence->mutex);
> }
>
> +bool
> +_util_queue_fence_wait_timeout(struct util_queue_fence *fence,
> + int64_t abs_timeout)
> +{
> + /* This terrible hack is made necessary by the fact that we really want an
> + * internal interface consistent with os_time_*, but cnd_timedwait is spec'd
> + * to be relative to the TIME_UTC clock.
> + */
> + int64_t rel = abs_timeout - os_time_get_nano();
> +
> + if (rel > 0) {
> + struct timespec ts;
> +
> + timespec_get(&ts, TIME_UTC);
> +
> + ts.tv_sec += abs_timeout / (1000*1000*1000);
> + ts.tv_nsec += abs_timeout % (1000*1000*1000);
> + if (ts.tv_nsec >= (1000*1000*1000)) {
> + ts.tv_sec++;
> + ts.tv_nsec -= (1000*1000*1000);
> + }
> +
> + mtx_lock(&fence->mutex);
> + while (!fence->signalled) {
> + if (cnd_timedwait(&fence->cond, &fence->mutex, &ts) != thrd_success)
> + break;
> + }
> + mtx_unlock(&fence->mutex);
> + }
> +
> + return fence->signalled;
> +}
> +
> void
> util_queue_fence_init(struct util_queue_fence *fence)
> {
> memset(fence, 0, sizeof(*fence));
> (void) mtx_init(&fence->mutex, mtx_plain);
> cnd_init(&fence->cond);
> fence->signalled = true;
> }
>
> void
> diff --git a/src/util/u_queue.h b/src/util/u_queue.h
> index 9c911fe6b4d..7f9b43d72ff 100644
> --- a/src/util/u_queue.h
> +++ b/src/util/u_queue.h
> @@ -74,40 +74,20 @@ util_queue_fence_init(struct util_queue_fence *fence)
> }
>
> static inline void
> util_queue_fence_destroy(struct util_queue_fence *fence)
> {
> assert(fence->val == 0);
> /* no-op */
> }
>
> static inline void
> -util_queue_fence_wait(struct util_queue_fence *fence)
> -{
> - uint32_t v = fence->val;
> -
> - if (likely(v == 0))
> - return;
> -
> - do {
> - if (v != 2) {
> - v = p_atomic_cmpxchg(&fence->val, 1, 2);
> - if (v == 0)
> - return;
> - }
> -
> - futex_wait(&fence->val, 2);
> - v = fence->val;
> - } while(v != 0);
> -}
> -
> -static inline void
> util_queue_fence_signal(struct util_queue_fence *fence)
> {
> uint32_t val = p_atomic_xchg(&fence->val, 0);
>
> assert(val != 0);
>
> if (val == 2)
> futex_wake_all(&fence->val);
> }
>
> @@ -140,21 +120,20 @@ util_queue_fence_is_signalled(struct util_queue_fence *fence)
> * Put this into your job structure.
> */
> struct util_queue_fence {
> mtx_t mutex;
> cnd_t cond;
> int signalled;
> };
>
> void util_queue_fence_init(struct util_queue_fence *fence);
> void util_queue_fence_destroy(struct util_queue_fence *fence);
> -void util_queue_fence_wait(struct util_queue_fence *fence);
> void util_queue_fence_signal(struct util_queue_fence *fence);
>
> /**
> * Move \p fence back into unsignalled state.
> *
> * \warning The caller must ensure that no other thread may currently be
> * waiting (or about to wait) on the fence.
> */
> static inline void
> util_queue_fence_reset(struct util_queue_fence *fence)
> @@ -163,20 +142,53 @@ util_queue_fence_reset(struct util_queue_fence *fence)
> fence->signalled = 0;
> }
>
> static inline bool
> util_queue_fence_is_signalled(struct util_queue_fence *fence)
> {
> return fence->signalled != 0;
> }
> #endif
>
> +void
> +_util_queue_fence_wait(struct util_queue_fence *fence);
> +
> +static inline void
> +util_queue_fence_wait(struct util_queue_fence *fence)
> +{
> + if (unlikely(!util_queue_fence_is_signalled(fence)))
> + _util_queue_fence_wait(fence);
> +}
> +
> +bool
> +_util_queue_fence_wait_timeout(struct util_queue_fence *fence,
> + int64_t abs_timeout);
> +
> +/**
> + * Wait for the fence to be signaled with a timeout.
> + *
> + * \param fence the fence
> + * \param abs_timeout the absolute timeout in nanoseconds, relative to the
> + * clock provided by os_time_get_nano.
> + *
> + * \return true if the fence was signaled, false if the timeout occurred.
> + */
> +static inline bool
> +util_queue_fence_wait_timeout(struct util_queue_fence *fence,
> + int64_t abs_timeout)
> +{
> + if (util_queue_fence_is_signalled(fence))
> + return true;
> +
> + return _util_queue_fence_wait_timeout(fence, abs_timeout);
> +}
> +
> typedef void (*util_queue_execute_func)(void *job, int thread_index);
>
> struct util_queue_job {
> void *job;
> struct util_queue_fence *fence;
> util_queue_execute_func execute;
> util_queue_execute_func cleanup;
> };
>
> /* Put this into your context. */
> --
> 2.11.0
>
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/mesa-dev
More information about the mesa-dev
mailing list