source: mainline/kernel/generic/include/lib/refcount.h@ b2aaaa0

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b2aaaa0 was 78de83de, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 7 years ago

Improve kernel spinlock and AS refcount.

  • Property mode set to 100644
File size: 3.9 KB
Line 
1/*
2 * Copyright (c) 2018 CZ.NIC, z.s.p.o.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * Authors:
31 * Jiří Zárevúcky (jzr) <zarevucky.jiri@gmail.com>
32 */
33
34/*
35 * Using atomics for reference counting efficiently is a little tricky,
36 * so we define a unified API for this.
37 */
38
39#ifndef LIBC_REFCOUNT_H_
40#define LIBC_REFCOUNT_H_
41
42#include <assert.h>
43#include <stdatomic.h>
44#include <stdbool.h>
45
46/* Wrapped in a structure to prevent direct manipulation. */
47typedef struct atomic_refcount {
48 volatile atomic_int __cnt;
49} atomic_refcount_t;
50
51static inline void refcount_init(atomic_refcount_t *rc)
52{
53 atomic_store_explicit(&rc->__cnt, 0, memory_order_relaxed);
54}
55
56/**
57 * Increment a reference count.
58 *
59 * Calling this without already owning a reference is undefined behavior.
60 * E.g. acquiring a reference through a shared mutable pointer requires that
61 * the caller first locks the pointer itself (thereby acquiring the reference
62 * inherent to the shared variable), and only then may call refcount_up().
63 */
64static inline void refcount_up(atomic_refcount_t *rc)
65{
66 // XXX: We can use relaxed operation because acquiring a reference
67 // implies no ordering relationships. A reference-counted object
68 // still needs to be synchronized independently of the refcount.
69
70 int old = atomic_fetch_add_explicit(&rc->__cnt, 1,
71 memory_order_relaxed);
72
73 /* old < 0 indicates that the function is used incorrectly. */
74 assert(old >= 0);
75}
76
77static inline bool refcount_unique(atomic_refcount_t *rc)
78{
79 int val = atomic_load_explicit(&rc->__cnt, memory_order_acquire);
80 if (val < 0) {
81 assert(val == -1);
82 }
83
84 return val <= 0;
85}
86
87/**
88 * Decrement a reference count. Caller must own the reference.
89 *
90 * If the function returns `false`, the caller no longer owns the reference and
91 * must not access the reference counted object.
92 *
93 * If the function returns `true`, the caller is now the sole owner of the
94 * reference counted object, and must deallocate it.
95 */
96static inline bool refcount_down(atomic_refcount_t *rc)
97{
98 // XXX: The decrementers don't need to synchronize with each other,
99 // but they do need to synchronize with the one doing deallocation.
100 int old = atomic_fetch_sub_explicit(&rc->__cnt, 1,
101 memory_order_release);
102
103 assert(old >= 0);
104
105 if (old == 0) {
106 // XXX: We are holding the last reference, so we must now
107 // synchronize with all the other decrementers.
108
109 int val = atomic_load_explicit(&rc->__cnt,
110 memory_order_acquire);
111 assert(val == -1);
112
113 /*
114 * The compiler probably wouldn't optimize the memory barrier
115 * away, but better safe than sorry.
116 */
117 return val < 0;
118 }
119
120 return false;
121}
122
123#endif
124
Note: See TracBrowser for help on using the repository browser.