1 // © 2021 Qualcomm Innovation Center, Inc. All rights reserved.
2 //
3 // SPDX-License-Identifier: BSD-3-Clause
4
5 #include <assert.h>
6 #include <hyptypes.h>
7 #include <stdbool.h>
8
9 #include <atomic.h>
10 #include <compiler.h>
11 #include <refcount.h>
12
13 // Initialise a reference count, with a single reference held.
14 void
refcount_init(refcount_t * ref)15 refcount_init(refcount_t *ref)
16 {
17 atomic_init(&ref->count, 1);
18 }
19
20 // Get a reference, assuming that the count is nonzero. This must only be used
21 // in cases where the the caller already knows that there is at least one
22 // reference that cannot be concurrently released by another thread, hence the
23 // name. No memory barrier is implied; adequate barriers should be provided by
24 // whatever other mechanism is used to guarantee that the count is nonzero,
25 // e.g. RCU.
26 void
refcount_get_additional(refcount_t * ref)27 refcount_get_additional(refcount_t *ref)
28 {
29 uint32_t count =
30 atomic_fetch_add_explicit(&ref->count, 1, memory_order_relaxed);
31
32 assert(count > 0U);
33 (void)count;
34 }
35
36 // Get a reference, without assuming that the count is nonzero. The caller
37 // must check the result; if it is false, the count had already reached zero
38 // and the reference could not be token. An acquire memory barrier is implied.
39 bool
refcount_get_safe(refcount_t * ref)40 refcount_get_safe(refcount_t *ref)
41 {
42 uint32_t count = atomic_load_relaxed(&ref->count);
43 bool success = false;
44
45 while (count > 0U) {
46 assert(count < (uint32_t)UINT32_MAX);
47 if (atomic_compare_exchange_weak_explicit(
48 &ref->count, &count, count + 1U,
49 memory_order_acquire, memory_order_relaxed)) {
50 success = true;
51 break;
52 }
53 }
54
55 return success;
56 }
57
58 // Release a reference. The caller must check the result; if it is true, the
59 // count has now reached zero and the caller must take action to free the
60 // underlying resource. This is always a release operation. If this reduces the
61 // count to zero (and returns true), it is also an acquire operation.
62 bool
refcount_put(refcount_t * ref)63 refcount_put(refcount_t *ref)
64 {
65 uint32_t count = atomic_fetch_sub_explicit(&ref->count, 1U,
66 memory_order_release);
67 assert(count > 0U);
68 if (compiler_expected(count > 1U)) {
69 return false;
70 } else {
71 atomic_thread_fence(memory_order_acquire);
72 return true;
73 }
74 }
75