1 /*
2 * Copyright (c) 2013, Google, Inc. All rights reserved
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #ifndef __REFLIST_H
25 #define __REFLIST_H
26
27 #include <assert.h>
28 #include <lk/compiler.h>
29 #include <lk/list.h>
30
31 struct obj_ref {
32 struct list_node ref_node;
33 };
34
35 struct obj {
36 struct list_node ref_list;
37 };
38
39 typedef void (*obj_destroy_func)(struct obj* obj);
40
41 #define OBJ_REF_INITIAL_VALUE(r) \
42 { .ref_node = LIST_INITIAL_CLEARED_VALUE }
43
obj_ref_init(struct obj_ref * ref)44 static inline __ALWAYS_INLINE void obj_ref_init(struct obj_ref* ref) {
45 *ref = (struct obj_ref)OBJ_REF_INITIAL_VALUE(*ref);
46 }
47
obj_ref_active(struct obj_ref * ref)48 static inline __ALWAYS_INLINE bool obj_ref_active(struct obj_ref* ref) {
49 return list_in_list(&ref->ref_node);
50 }
51
obj_init(struct obj * obj,struct obj_ref * ref)52 static inline __ALWAYS_INLINE void obj_init(struct obj* obj,
53 struct obj_ref* ref) {
54 list_initialize(&obj->ref_list);
55 list_add_tail(&obj->ref_list, &ref->ref_node);
56 }
57
obj_has_ref(struct obj * obj)58 static inline __ALWAYS_INLINE bool obj_has_ref(struct obj* obj) {
59 return !list_is_empty(&obj->ref_list);
60 }
61
obj_has_only_ref(struct obj * obj,struct obj_ref * ref)62 static inline __ALWAYS_INLINE bool obj_has_only_ref(struct obj* obj,
63 struct obj_ref* ref) {
64 assert(obj_has_ref(obj));
65 assert(list_in_list(&ref->ref_node));
66 struct list_node* head = list_peek_head(&obj->ref_list);
67 struct list_node* tail = list_peek_tail(&obj->ref_list);
68 if (head == tail) {
69 assert(head == &ref->ref_node);
70 return head == &ref->ref_node;
71 }
72 return false;
73 }
74
75 /*
76 * Only use if you are intentionally reusing a possibly unreferenced
77 * object. A cache is an example of this use case, where the destroy
78 * callback may not actually free the object, and the code may wish to
79 * reuse it by adding a reference after it hits zero.
80 */
obj_add_ref_allow_unreferenced_obj(struct obj * obj,struct obj_ref * ref)81 static inline __ALWAYS_INLINE void obj_add_ref_allow_unreferenced_obj(
82 struct obj* obj, struct obj_ref* ref) {
83 assert(!list_in_list(&ref->ref_node));
84 list_add_tail(&obj->ref_list, &ref->ref_node);
85 }
86
obj_add_ref(struct obj * obj,struct obj_ref * ref)87 static inline __ALWAYS_INLINE void obj_add_ref(struct obj* obj,
88 struct obj_ref* ref) {
89 assert(obj_has_ref(obj));
90 obj_add_ref_allow_unreferenced_obj(obj, ref);
91 }
92
obj_del_ref(struct obj * obj,struct obj_ref * ref,obj_destroy_func destroy)93 static inline __ALWAYS_INLINE bool obj_del_ref(struct obj* obj,
94 struct obj_ref* ref,
95 obj_destroy_func destroy) {
96 bool dead;
97
98 assert(list_in_list(&ref->ref_node));
99
100 list_delete(&ref->ref_node);
101 dead = list_is_empty(&obj->ref_list);
102 if (dead && destroy)
103 destroy(obj);
104 return dead;
105 }
106
obj_ref_transfer(struct obj_ref * dst,struct obj_ref * src)107 static inline __ALWAYS_INLINE void obj_ref_transfer(struct obj_ref* dst,
108 struct obj_ref* src) {
109 struct list_node* prev;
110
111 assert(!list_in_list(&dst->ref_node));
112 assert(list_in_list(&src->ref_node));
113
114 prev = src->ref_node.prev;
115 list_delete(&src->ref_node);
116 list_add_after(prev, &dst->ref_node);
117 }
118
119 #endif
120