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