s390: fix crashes with CPU_FLAGS=4
[ajla.git] / obj_reg.c
blobf20d75b517752ba43aacc99e4fc42fbd48e15f2b
1 /*
2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version.
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
19 #include "ajla.h"
21 #include "tree.h"
22 #include "list.h"
23 #include "str.h"
24 #include "thread.h"
25 #include "rwlock.h"
26 #include "os.h"
28 #include "obj_reg.h"
30 #ifdef DEBUG_OBJECT_POSSIBLE
32 static int obj_registry_active = 0;
34 struct registry {
35 struct tree head;
38 struct object {
39 struct tree_entry entry;
40 obj_id id;
41 position_t position;
44 static struct registry registry[N_OBJ_TYPES];
46 static bool registry_threads_initialized;
48 static unsigned obj_registry_recursion_singlethreaded = 0;
49 static tls_decl(unsigned, obj_registry_recursion);
51 rwlock_decl(obj_registry_rwlock);
53 bool obj_registry_start_recursion(void)
55 unsigned recursion;
56 if (unlikely(!registry_threads_initialized)) {
57 return obj_registry_recursion_singlethreaded++ != 0;
59 recursion = tls_get_nocheck(unsigned, obj_registry_recursion);
60 tls_set_nocheck(unsigned, obj_registry_recursion, recursion + 1);
61 return recursion != 0;
64 void obj_registry_end_recursion(void)
66 unsigned recursion;
67 if (unlikely(!registry_threads_initialized)) {
68 if (unlikely(!obj_registry_recursion_singlethreaded))
69 internal(file_line, "obj_registry_end_recursion: obj_registry_recursion_singlethreaded underflow");
70 obj_registry_recursion_singlethreaded--;
71 return;
73 recursion = tls_get_nocheck(unsigned, obj_registry_recursion);
74 if (unlikely(!recursion))
75 internal(file_line, "obj_registry_end_recursion: obj_registry_recursion underflow");
76 tls_set_nocheck(unsigned, obj_registry_recursion, recursion - 1);
80 * warning: memory allocation or freeing must not be done when holding
81 * the object registry lock
83 static void obj_registry_lock_read(void)
85 if (unlikely(!registry_threads_initialized))
86 return;
87 rwlock_lock_read(&obj_registry_rwlock);
90 static void obj_registry_unlock_read(void)
92 if (unlikely(!registry_threads_initialized))
93 return;
94 rwlock_unlock_read(&obj_registry_rwlock);
97 static void obj_registry_lock_write(void)
99 if (unlikely(!registry_threads_initialized))
100 return;
101 rwlock_lock_write(&obj_registry_rwlock);
104 static void obj_registry_unlock_write(void)
106 if (unlikely(!registry_threads_initialized))
107 return;
108 rwlock_unlock_write(&obj_registry_rwlock);
112 static char attr_cold *print_obj_id(obj_id id)
114 #if 1
115 static char buffer[sizeof(obj_id) * 2 + 1];
116 char *b = buffer;
117 str_add_unsigned(&b, NULL, (uintbig_t)id, 16);
118 return buffer;
119 #else
120 /* this causes memory allocation and may trigger infinite recursion on some errors */
121 return str_from_unsigned(id, 16);
122 #endif
125 static struct registry *obj_registry_get(obj_id type, position_t position)
127 if (unlikely(type >= N_OBJ_TYPES))
128 internal(position_string(position), "obj_registry_get: invalid type %u", (unsigned)type);
129 return &registry[type];
133 static int object_test(const struct tree_entry *e, uintptr_t id)
135 const struct object *o = get_struct(e, struct object, entry);
136 if (o->id == id) return 0;
137 if (o->id > id) return 1;
138 return -1;
141 static struct object *obj_registry_find(struct registry *r, obj_id id)
143 struct tree_entry *e;
145 e = tree_find(&r->head, object_test, id);
146 if (likely(e != NULL))
147 return get_struct(e, struct object, entry);
148 else
149 return NULL;
152 void obj_registry_insert(obj_type type, obj_id id, position_t position)
154 struct registry *r;
155 struct tree_entry *e;
156 struct tree_insert_position ins;
157 struct object *o;
159 if (likely(!((obj_registry_active >> (int)type) & 1)))
160 return;
162 o = malloc(sizeof(struct object));
163 if (unlikely(!o))
164 fatal("unable to allocate struct object");
165 o->id = id;
166 o->position = position;
168 obj_registry_start_recursion();
169 obj_registry_lock_write();
171 r = obj_registry_get(type, position);
172 e = tree_find_for_insert(&r->head, object_test, o->id, &ins);
173 if (unlikely(e != NULL)) {
174 struct object *of = get_struct(e, struct object, entry);
175 obj_registry_unlock_write();
176 internal(position_string(position), "object already present, type %u, id %s, allocated at %s", (unsigned)type, print_obj_id(id), position_string(of->position));
178 tree_insert_after_find(&o->entry, &ins);
180 obj_registry_unlock_write();
181 obj_registry_end_recursion();
184 void obj_registry_remove(obj_type type, obj_id id, position_t position)
186 struct registry *r;
187 struct object *o;
189 if (likely(!((obj_registry_active >> (int)type) & 1)))
190 return;
192 obj_registry_start_recursion();
193 obj_registry_lock_write();
195 r = obj_registry_get(type, position);
196 if (unlikely(!(o = obj_registry_find(r, id)))) {
197 obj_registry_unlock_write();
198 internal(position_string(position), "object not found, type %u, id %s", (unsigned)type, print_obj_id(id));
200 tree_delete(&o->entry);
202 obj_registry_unlock_write();
203 obj_registry_end_recursion();
205 free(o);
208 void obj_registry_verify(obj_type type, obj_id id, position_t position)
210 struct registry *r;
212 if (likely(!((obj_registry_active >> (int)type) & 1)))
213 return;
215 if (obj_registry_start_recursion()) {
216 obj_registry_end_recursion();
217 return;
219 obj_registry_lock_read();
221 r = obj_registry_get(type, position);
222 if (unlikely(!obj_registry_find(r, id))) {
223 obj_registry_unlock_read();
224 internal(position_string(position), "object not found, type %u, id %s", (unsigned)type, print_obj_id(id));
227 obj_registry_unlock_read();
228 obj_registry_end_recursion();
231 static attr_noreturn attr_cold obj_registry_dump_leaks(void)
233 obj_id t;
234 char *s;
235 size_t sl;
236 bool first = true;
237 const char *first_pos = "";
239 str_init(&s, &sl);
240 str_add_string(&s, &sl, "object leak, list of objects: ");
242 for (t = 0; t < N_OBJ_TYPES; t++) {
243 struct tree_entry *lv;
244 for (lv = tree_first(&registry[t].head); lv; lv = tree_next(lv)) {
245 struct object *o = get_struct(lv, struct object, entry);
246 const char *pos_str = position_string(o->position);
248 if (first) first_pos = pos_str;
249 else str_add_string(&s, &sl, ", ");
250 first = false;
252 str_add_unsigned(&s, &sl, t, 10);
253 str_add_string(&s, &sl, ":");
254 str_add_string(&s, &sl, print_obj_id(o->id));
255 str_add_string(&s, &sl, " @ ");
256 str_add_string(&s, &sl, pos_str);
260 str_finish(&s, &sl);
262 internal(first_pos, "%s", s);
265 void obj_registry_init(void)
267 obj_id t;
268 registry_threads_initialized = false;
269 for (t = 0; t < N_OBJ_TYPES; t++)
270 tree_init(&registry[t].head);
273 void obj_registry_init_multithreaded(void)
275 if (unlikely(registry_threads_initialized))
276 internal(file_line, "obj_registry_init_multithreaded: registry_threads_initialized already set");
277 tls_init(unsigned, obj_registry_recursion);
278 rwlock_init(&obj_registry_rwlock);
279 registry_threads_initialized = true;
282 void obj_registry_done_multithreaded(void)
284 if (unlikely(!registry_threads_initialized))
285 internal(file_line, "obj_registry_done_multithreaded: registry_threads_initialized not set");
286 registry_threads_initialized = false;
287 rwlock_done(&obj_registry_rwlock);
288 tls_done(unsigned, obj_registry_recursion);
291 void obj_registry_done(void)
293 obj_id t;
294 if (unlikely(registry_threads_initialized))
295 internal(file_line, "obj_registry_done: registry_threads_initialized set");
296 for (t = 0; t < N_OBJ_TYPES; t++) {
297 if (!tree_is_empty(&registry[t].head))
298 obj_registry_dump_leaks();
302 #endif
304 bool obj_registry_enable_debugging_option(const char *option, size_t l)
306 #ifndef DEBUG_OBJECT_POSSIBLE
307 int obj_registry_active = 0;
308 #endif
309 if (!option)
310 obj_registry_active = -1;
311 else if (l == 5 && !strncmp(option, "mutex", l))
312 obj_registry_active |= 1 << OBJ_TYPE_MUTEX;
313 else if (l == 4 && !strncmp(option, "cond", l))
314 obj_registry_active |= 1 << OBJ_TYPE_COND;
315 else if (l == 6 && !strncmp(option, "thread", l))
316 obj_registry_active |= 1 << OBJ_TYPE_THREAD;
317 else if (l == 3 && !strncmp(option, "tls", l))
318 obj_registry_active |= 1 << OBJ_TYPE_TLS;
319 else if (l == 7 && !strncmp(option, "handles", l))
320 obj_registry_active |= 1 << OBJ_TYPE_HANDLE;
321 else if (l == 7 && !strncmp(option, "objects", l))
322 obj_registry_active = -1;
323 else
324 return false;
325 return true;