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
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/>.
30 #ifdef DEBUG_OBJECT_POSSIBLE
32 static int obj_registry_active
= 0;
39 struct tree_entry entry
;
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)
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)
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
--;
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
))
87 rwlock_lock_read(&obj_registry_rwlock
);
90 static void obj_registry_unlock_read(void)
92 if (unlikely(!registry_threads_initialized
))
94 rwlock_unlock_read(&obj_registry_rwlock
);
97 static void obj_registry_lock_write(void)
99 if (unlikely(!registry_threads_initialized
))
101 rwlock_lock_write(&obj_registry_rwlock
);
104 static void obj_registry_unlock_write(void)
106 if (unlikely(!registry_threads_initialized
))
108 rwlock_unlock_write(&obj_registry_rwlock
);
112 static char attr_cold
*print_obj_id(obj_id id
)
115 static char buffer
[sizeof(obj_id
) * 2 + 1];
117 str_add_unsigned(&b
, NULL
, (uintbig_t
)id
, 16);
120 /* this causes memory allocation and may trigger infinite recursion on some errors */
121 return str_from_unsigned(id
, 16);
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 ®istry
[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;
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
);
152 void obj_registry_insert(obj_type type
, obj_id id
, position_t position
)
155 struct tree_entry
*e
;
156 struct tree_insert_position ins
;
159 if (likely(!((obj_registry_active
>> (int)type
) & 1)))
162 o
= malloc(sizeof(struct object
));
164 fatal("unable to allocate struct object");
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
)
189 if (likely(!((obj_registry_active
>> (int)type
) & 1)))
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();
208 void obj_registry_verify(obj_type type
, obj_id id
, position_t position
)
212 if (likely(!((obj_registry_active
>> (int)type
) & 1)))
215 if (obj_registry_start_recursion()) {
216 obj_registry_end_recursion();
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)
237 const char *first_pos
= "";
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(®istry
[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
, ", ");
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
);
262 internal(first_pos
, "%s", s
);
265 void obj_registry_init(void)
268 registry_threads_initialized
= false;
269 for (t
= 0; t
< N_OBJ_TYPES
; t
++)
270 tree_init(®istry
[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)
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(®istry
[t
].head
))
298 obj_registry_dump_leaks();
304 bool obj_registry_enable_debugging_option(const char *option
, size_t l
)
306 #ifndef DEBUG_OBJECT_POSSIBLE
307 int obj_registry_active
= 0;
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;