1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <lib/sync/completion.h>
6
7 #include <limits.h>
8 #include <stdatomic.h>
9 #include <zircon/syscalls.h>
10
11 enum {
12 UNSIGNALED = 0,
13 SIGNALED = 1,
14 };
15
sync_completion_wait(sync_completion_t * completion,zx_duration_t timeout)16 zx_status_t sync_completion_wait(sync_completion_t* completion, zx_duration_t timeout) {
17 zx_time_t deadline =
18 (timeout == ZX_TIME_INFINITE) ? ZX_TIME_INFINITE : zx_deadline_after(timeout);
19 return sync_completion_wait_deadline(completion, deadline);
20 }
21
sync_completion_wait_deadline(sync_completion_t * completion,zx_time_t deadline)22 zx_status_t sync_completion_wait_deadline(sync_completion_t* completion, zx_time_t deadline) {
23 // TODO(kulakowski): With a little more state (a waiters count),
24 // this could optimistically spin before entering the kernel.
25
26 atomic_int* futex = &completion->futex;
27
28 for (;;) {
29 int32_t current_value = atomic_load(futex);
30 if (current_value == SIGNALED) {
31 return ZX_OK;
32 }
33 switch (_zx_futex_wait(futex, current_value, ZX_HANDLE_INVALID, deadline)) {
34 case ZX_OK:
35 continue;
36 case ZX_ERR_BAD_STATE:
37 // If we get ZX_ERR_BAD_STATE, the value of the futex changed between
38 // our load and the wait. This could only have happened if we
39 // were signaled.
40 return ZX_OK;
41 case ZX_ERR_TIMED_OUT:
42 return ZX_ERR_TIMED_OUT;
43 case ZX_ERR_INVALID_ARGS:
44 default:
45 __builtin_trap();
46 }
47 }
48 }
49
sync_completion_signal(sync_completion_t * completion)50 void sync_completion_signal(sync_completion_t* completion) {
51 atomic_int* futex = &completion->futex;
52 atomic_store(futex, SIGNALED);
53 _zx_futex_wake(futex, UINT32_MAX);
54 }
55
sync_completion_signal_requeue(sync_completion_t * completion,zx_futex_t * futex)56 void sync_completion_signal_requeue(sync_completion_t* completion, zx_futex_t* futex) {
57 atomic_store(&completion->futex, SIGNALED);
58 // Note that _zx_futex_requeue() will check the value of &completion->futex
59 // and return ZX_ERR_BAD_STATE if it is not SIGNALED. The only way that could
60 // happen is racing with sync_completion_reset(). This is not an intended use
61 // case for this function: we only expect it to be used internally by libsync
62 // and without sync_completion_reset().
63 //
64 // However, if this theoretical scenario actually occurs, we can still safely
65 // ignore the error: there is no point in waking up the waiters since they
66 // would find an UNSIGNALED value and go back to sleep.
67 _zx_futex_requeue(&completion->futex, 0, SIGNALED, futex, UINT32_MAX, ZX_HANDLE_INVALID);
68 }
69
sync_completion_reset(sync_completion_t * completion)70 void sync_completion_reset(sync_completion_t* completion) {
71 atomic_store(&completion->futex, UNSIGNALED);
72 }
73