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