pipadoc uses some bashisms
[nobug.git] / src / nobug_resources.c
blob9003595e1442251c648d57c24a786426bf2867dc
1 /*
2 This file is part of the NoBug debugging library.
4 Copyright (C) 2007, 2008, 2009, Christian Thaeter <ct@pipapo.org>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, contact Christian Thaeter <ct@pipapo.org>.
19 #include <stdlib.h>
21 #define NOBUG_LIBNOBUG_C
22 #include "nobug.h"
23 #include "llist.h"
24 #include "mpool.h"
27 //deadlock There are 2 kinds of nodes: `resource_record` hold registered resources and `resource_user` which
28 //deadlock attach to (enter) a resource.
29 //deadlock
30 //deadlock Each thread keeps stacklist of each `resource_user` it created, new enters will push on the stack,
31 //deadlock leaving a resource will remove it from this stacklist.
32 //deadlock
33 //deadlock All `resource_records` in use are linked in a global precedence list, items of equal precedence are
34 //deadlock spaned by a skip pointer. Whenever a resource is entered the deadlock checker asserts that one does
35 //deadlock not break existing precedences. By doing so the precedence list gets continously refined as the system
36 //deadlock learns about new lock patterns.
37 //deadlock
38 //deadlock As a consequence of this algorithm the deadlock checker does not only find real deadlocks but
39 //deadlock already potential deadlocks by violations of the locking order which is a lot simpler than finding
40 //deadlock actual deadlocks.
41 //deadlock
42 //deadlock This also means that the deadlock tracker currently only works with hierarchical locking policies
43 //deadlock other approaches to prevent deadlocks are not yet supported and will be added on demand.
44 //deadlock
45 //deadlock
46 //deadlock
50 How much memory to reserve for a mpool chunk, 16k by default
52 #ifndef NOBUG_RESOURCE_MPOOL_CHUNKSIZE
53 #define NOBUG_RESOURCE_MPOOL_CHUNKSIZE (4096<<(sizeof(void*)/4)) /* That is roughly 8k chunks on 32bit, 16k chunks on 64 bit machines */
54 #endif
56 #if NOBUG_USE_PTHREAD
57 pthread_mutex_t nobug_resource_mutex = PTHREAD_MUTEX_INITIALIZER;
58 #define NOBUG_RESOURCE_LOCK pthread_mutex_lock (&nobug_resource_mutex)
59 #define NOBUG_RESOURCE_UNLOCK pthread_mutex_unlock (&nobug_resource_mutex)
60 #else
61 #define NOBUG_RESOURCE_LOCK
62 #define NOBUG_RESOURCE_UNLOCK
63 #endif
65 #define nobug_resourcestates \
66 resource_state(invalid), \
67 resource_state(waiting), \
68 resource_state(exclusive), \
69 resource_state(recursive), \
70 resource_state(shared),
73 #define resource_state(name) #name
74 const char* nobug_resource_states[] =
76 nobug_resourcestates
77 NULL
79 #undef resource_state
81 const char* nobug_resource_error = NULL;
83 static llist nobug_resource_registry;
84 static mpool nobug_resource_record_pool;
85 static mpool nobug_resource_user_pool;
86 #if NOBUG_USE_PTHREAD
87 static mpool nobug_resource_node_pool;
88 #endif
90 static void nobug_resource_record_dtor (void*);
91 static void nobug_resource_user_dtor (void*);
92 #if NOBUG_USE_PTHREAD
93 static void nobug_resource_node_dtor (void*);
94 #endif
97 void
98 nobug_resource_init (void)
100 llist_init (&nobug_resource_registry);
102 mpool_init (&nobug_resource_record_pool,
103 sizeof(struct nobug_resource_record),
104 NOBUG_RESOURCE_MPOOL_CHUNKSIZE/sizeof(struct nobug_resource_record),
105 nobug_resource_record_dtor);
107 mpool_init (&nobug_resource_user_pool,
108 sizeof(struct nobug_resource_user),
109 NOBUG_RESOURCE_MPOOL_CHUNKSIZE/sizeof(struct nobug_resource_user),
110 nobug_resource_user_dtor);
112 #if NOBUG_USE_PTHREAD
113 mpool_init (&nobug_resource_node_pool,
114 sizeof(struct nobug_resource_node),
115 NOBUG_RESOURCE_MPOOL_CHUNKSIZE/sizeof(struct nobug_resource_node),
116 nobug_resource_node_dtor);
117 #endif
121 void
122 nobug_resource_destroy (void)
124 #if NOBUG_USE_PTHREAD
125 mpool_destroy (&nobug_resource_node_pool);
126 #endif
127 mpool_destroy (&nobug_resource_user_pool);
128 mpool_destroy (&nobug_resource_record_pool);
132 unsigned
133 nobug_resource_record_available (void)
135 return mpool_available (&nobug_resource_record_pool);
139 unsigned
140 nobug_resource_user_available (void)
142 return mpool_available (&nobug_resource_user_pool);
146 #if NOBUG_USE_PTHREAD
147 unsigned
148 nobug_resource_node_available (void)
150 return mpool_available (&nobug_resource_node_pool);
154 static void
155 nobug_resource_node_free (struct nobug_resource_node* self)
157 LLIST_WHILE_HEAD (&self->childs, c)
158 nobug_resource_node_free (LLIST_TO_STRUCTP(c, struct nobug_resource_node, cldnode));
160 llist_unlink_fast_ (&self->cldnode);
161 llist_unlink_fast_ (&self->node);
162 mpool_free (&nobug_resource_node_pool, self);
166 static void
167 nobug_resource_node_dtor (void* p)
169 struct nobug_resource_node* n = p;
170 llist_unlink_fast_ (&n->node);
171 /* must unlink childs, because we don't destroy the tree bottom up */
172 llist_unlink_fast_ (&n->childs);
173 llist_unlink_fast_ (&n->cldnode);
175 #endif
178 static void
179 nobug_resource_record_dtor (void* p)
181 struct nobug_resource_record* self= p;
182 llist_unlink_fast_ (&self->hdr.node);
184 #if NOBUG_USE_PTHREAD
185 /* destroy all nodes recursively */
186 LLIST_WHILE_HEAD (&self->nodes, n)
187 nobug_resource_node_free ((struct nobug_resource_node*)n);
188 #endif
192 static void
193 nobug_resource_user_dtor (void* p)
195 struct nobug_resource_user* u = p;
196 llist_unlink_fast_ (&u->hdr.node);
197 #if NOBUG_USE_PTHREAD
198 llist_unlink_fast_ (&u->res_stack);
199 #endif
203 static int
204 compare_resource_records (const_LList av, const_LList bv, void* unused)
206 (void) unused;
207 const struct nobug_resource_record* a = (const struct nobug_resource_record*)av;
208 const struct nobug_resource_record* b = (const struct nobug_resource_record*)bv;
210 return a->object_id > b->object_id ? 1 : a->object_id < b->object_id ? -1 : 0;
214 struct nobug_resource_record*
215 nobug_resource_announce (const char* type, const char* name, const void* object_id, const char* extra)
217 NOBUG_RESOURCE_LOCK;
219 struct nobug_resource_record* node = mpool_alloc (&nobug_resource_record_pool);
220 if (!node)
222 nobug_resource_error = "internal allocation error";
223 return NULL;
226 node->hdr.name = name;
227 node->object_id = object_id;
228 node->type = type;
230 /* TODO better lookup method than list search (psplay?) */
231 if (llist_ufind (&nobug_resource_registry, &node->hdr.node, compare_resource_records, NULL))
233 nobug_resource_error = "already registered";
234 return NULL;
237 llist_init (&node->users);
238 node->hdr.extra = extra;
239 #if NOBUG_USE_PTHREAD
240 llist_init (&node->nodes);
241 #endif
243 llist_insert_head (&nobug_resource_registry, llist_init (&node->hdr.node));
245 NOBUG_RESOURCE_UNLOCK;
246 return node;
251 nobug_resource_forget (struct nobug_resource_record* self)
253 NOBUG_RESOURCE_LOCK;
254 if (!llist_find (&nobug_resource_registry, &self->hdr.node, compare_resource_records, NULL))
256 nobug_resource_error = "not registered";
257 return 0;
260 if (!llist_is_empty (&self->users))
262 nobug_resource_error = "still in use";
263 return 0;
266 nobug_resource_record_dtor (self);
268 mpool_free (&nobug_resource_record_pool, self);
269 NOBUG_RESOURCE_UNLOCK;
270 return 1;
274 #if NOBUG_USE_PTHREAD
275 static int
276 nobug_resource_node_resource_cmpfn (const_LList a, const_LList b, void* extra)
278 (void) extra;
279 return ((struct nobug_resource_node*)a)->resource ==
280 ((struct nobug_resource_node*)b)->resource?0:-1;
284 struct nobug_resource_node*
285 nobug_resource_node_new (struct nobug_resource_record* resource,
286 struct nobug_resource_node* parent)
288 struct nobug_resource_node* self = mpool_alloc (&nobug_resource_node_pool);
289 if (self)
291 llist_insert_head (&resource->nodes, llist_init (&self->node));
292 self->resource = resource;
294 self->parent = parent;
296 llist_init (&self->childs);
297 llist_init (&self->cldnode);
298 if (parent)
299 llist_insert_head (&parent->childs, &self->cldnode);
301 return self;
303 #endif
306 //dlalgo HEAD~ The Resource Tracking Algorithm; deadlock_detection; how resources are tracked
307 //dlalgo
308 //dlalgo Each resource registers a global 'resource_record'.
309 //dlalgo
310 //dlalgo Every new locking path discovered is stored as 'resource_node' structures which refer to the associated
311 //dlalgo 'resource_record'.
312 //dlalgo
313 //dlalgo Threads keep a trail of 'resource_user' strcutures for each resource entered. This 'resource_user' struct
314 //dlalgo refer to the 'resource_nodes' and thus indirectly to the associated 'resource_record'.
315 //dlalgo
316 //dlalgo The deadlock checker uses this information to test if the acqusition of a new resource would yield a
317 //dlalgo potential deadlock.
318 //dlalgo
319 struct nobug_resource_user*
320 nobug_resource_enter (struct nobug_resource_record* resource,
321 const char* identifier,
322 enum nobug_resource_state state,
323 const char* extra)
325 NOBUG_RESOURCE_LOCK;
327 if (!resource)
329 nobug_resource_error = "no resource";
330 return NULL;
333 #if NOBUG_USE_PTHREAD
334 struct nobug_tls_data* tls = nobug_thread_get ();
336 //dlalgo HEAD^ Entering Resources; nobug_resource_enter; deadlock check on enter
337 //dlalgo
338 //dlalgo In multithreaded programs, whenever a thread wants to wait for a 'resource_record'
339 //dlalgo the deadlock checker jumps in.
340 //dlalgo
341 //dlalgo The deadlock checking algorithm is anticipatory as it will find and abort on conditions which may lead
342 //dlalgo to a potential deadlock by violating the locking order learned earlier.
343 //dlalgo
344 //dlalgo Each thread holds a stack (list) of each 'resource_user' it created. Leaving
345 //dlalgo a resource will remove it from this stacklist.
346 //dlalgo
347 //dlalgo Each 'resource_record' stores the trail which other 'resource_records' are already entered. This relations
348 //dlalgo are implemented with the 'resource_node' helper structure.
349 //dlalgo
350 //dlalgo ////
351 //dlalgo TODO: insert diagram here
352 //dlalgo 2-3
353 //dlalgo 1
354 //dlalgo 3-4-2
355 //dlalgo
356 //dlalgo 1-3-2-4
357 //dlalgo
358 //dlalgo 3-4-2
359 //dlalgo
360 //dlalgo 1-4-2
361 //dlalgo
362 //dlalgo ////
363 //dlalgo
364 //dlalgo First we find out if there is already a node from the to be acquired resource back to
365 //dlalgo the topmost node of the current threads user stack.
366 //dlalgo
367 //dlalgo [source,c]
368 //dlalgo ---------------------------------------------------------------------
369 struct nobug_resource_user* user = NULL; //dlalgo VERBATIM
370 struct nobug_resource_node* node = NULL; //dlalgo VERBATIM
371 //dlalgo VERBATIM
372 if (!llist_is_empty (&tls->res_stack)) //dlalgo VERBATIM
373 { //dlalgo VERBATIM
374 user = LLIST_TO_STRUCTP (llist_tail (&tls->res_stack), //dlalgo VERBATIM
375 struct nobug_resource_user, //dlalgo VERBATIM
376 res_stack); //dlalgo VERBATIM
377 //dlalgo VERBATIM
378 struct nobug_resource_node templ = //dlalgo VERBATIM
379 { //dlalgo VERBATIM
380 {NULL, NULL}, //dlalgo ...
381 user->current->resource, //dlalgo VERBATIM
382 NULL, //dlalgo ...
383 {NULL, NULL},
384 {NULL, NULL}
385 }; //dlalgo VERBATIM
386 //dlalgo VERBATIM
387 node = (struct nobug_resource_node*) //dlalgo VERBATIM
388 llist_ufind (&resource->nodes, //dlalgo VERBATIM
389 &templ.node, //dlalgo VERBATIM
390 nobug_resource_node_resource_cmpfn, //dlalgo VERBATIM
391 NULL); //dlalgo VERBATIM
392 } //dlalgo VERBATIM
393 //dlalgo ...
394 //dlalgo ---------------------------------------------------------------------
395 //dlalgo
396 #endif
398 //dlalgo Deadlock checking is only done when the node is entered in `WAITING` state and only
399 //dlalgo available in multithreaded programs.
400 //dlalgo
401 //dlalgo [source,c]
402 //dlalgo ---------------------------------------------------------------------
403 if (state == NOBUG_RESOURCE_WAITING) //dlalgo VERBATIM
404 { //dlalgo VERBATIM
405 #if NOBUG_USE_PTHREAD //dlalgo VERBATIM
406 //dlalgo ...
407 //dlalgo ---------------------------------------------------------------------
408 //dlalgo
410 //dlalgo If node was found above, then this locking path is already validated and no deadlock can happen,
411 //dlalgo else, if this stack already holds a resource (user is set) we have to go on with checking.
412 //dlalgo
413 //dlalgo [source,c]
414 //dlalgo ---------------------------------------------------------------------
415 if (!node && user) //dlalgo VERBATIM
416 { //dlalgo VERBATIM
417 //dlalgo ...
418 //dlalgo ---------------------------------------------------------------------
419 //dlalgo
420 //dlalgo If not then its checked that the resource to be entered is not on any parent trail of the current topmost resource,
421 //dlalgo if it is then this could be a deadlock which needs to be further investigated.
422 //dlalgo
423 //dlalgo [source,c]
424 //dlalgo ---------------------------------------------------------------------
425 LLIST_FOREACH (&user->current->resource->nodes, n) //dlalgo VERBATIM
426 { //dlalgo VERBATIM
427 for (struct nobug_resource_node* itr = //dlalgo VERBATIM
428 ((struct nobug_resource_node*)n)->parent; //dlalgo VERBATIM
429 itr; //dlalgo VERBATIM
430 itr = itr->parent) //dlalgo VERBATIM
431 { //dlalgo VERBATIM
432 if (itr->resource == resource) //dlalgo VERBATIM
433 { //dlalgo VERBATIM
434 //dlalgo ...
435 //dlalgo ---------------------------------------------------------------------
436 //dlalgo
437 //dlalgo if the resource was on the trail, we search if there is a common ancestor before the resource
438 //dlalgo on the trail and the threads current chain,
439 //dlalgo if yes then this ancestor protects against deadlocks and we can continue.
440 //dlalgo
441 //dlalgo [source,c]
442 //dlalgo ---------------------------------------------------------------------
443 for (struct nobug_resource_node* itr2 = itr->parent; //dlalgo VERBATIM
444 itr2; //dlalgo VERBATIM
445 itr2 = itr2->parent) //dlalgo VERBATIM
446 { //dlalgo VERBATIM
447 LLIST_FOREACH_REV (&tls->res_stack, p) //dlalgo VERBATIM
448 { //dlalgo VERBATIM
449 struct nobug_resource_user* user = //dlalgo VERBATIM
450 LLIST_TO_STRUCTP (p, //dlalgo VERBATIM
451 struct nobug_resource_user, //dlalgo VERBATIM
452 res_stack); //dlalgo VERBATIM
453 if (user->current->resource == itr2->resource) //dlalgo VERBATIM
454 goto done; //dlalgo VERBATIM
455 } //dlalgo VERBATIM
456 //dlalgo ---------------------------------------------------------------------
457 //dlalgo
458 //dlalgo If no ancestor found, we finally abort with a potential deadlock condition.
459 //dlalgo
460 //dlalgo [source,c]
461 //dlalgo ---------------------------------------------------------------------
462 nobug_resource_error = "possible deadlock detected"; //dlalgo VERBATIM
463 return NULL; //dlalgo VERBATIM
464 //dlalgo ...
465 //dlalgo ---------------------------------------------------------------------
466 //dlalgo
472 done:;
473 #endif
475 else if (state == NOBUG_RESOURCE_EXCLUSIVE)
477 /* check that everyone is waiting */
478 LLIST_FOREACH (&resource->users, n)
479 if (((struct nobug_resource_user*)n)->state != NOBUG_RESOURCE_WAITING)
481 nobug_resource_error = "invalid enter state (resource already claimed)";
482 break;
485 #if NOBUG_USE_PTHREAD
486 else if (state == NOBUG_RESOURCE_RECURSIVE)
488 /* check that everyone *else* is waiting */
489 LLIST_FOREACH (&resource->users, n)
491 struct nobug_resource_user* user = (struct nobug_resource_user*)n;
492 if (user->state != NOBUG_RESOURCE_WAITING &&
493 user->thread != tls)
495 nobug_resource_error = "invalid enter state (resource already claimed non recursive by another thread)";
496 break;
498 else if (!(user->state == NOBUG_RESOURCE_WAITING ||
499 user->state == NOBUG_RESOURCE_RECURSIVE) &&
500 user->thread == tls)
502 nobug_resource_error = "invalid enter state (resource already claimed non recursive by this thread)";
503 break;
507 #endif
508 else if (state == NOBUG_RESOURCE_SHARED)
510 /* check that every one else is waiting or hold it shared */
511 LLIST_FOREACH (&resource->users, n)
512 if (((struct nobug_resource_user*)n)->state != NOBUG_RESOURCE_WAITING
513 && ((struct nobug_resource_user*)n)->state != NOBUG_RESOURCE_SHARED)
515 nobug_resource_error = "invalid enter state (resource already claimed non shared)";
516 break;
519 else
520 nobug_resource_error = "invalid enter state";
522 if (nobug_resource_error)
523 return NULL;
525 struct nobug_resource_user* new_user = mpool_alloc (&nobug_resource_user_pool);
526 if (!new_user)
528 nobug_resource_error = "internal allocation error";
529 return NULL;
532 new_user->hdr.name = identifier;
533 new_user->hdr.extra = extra;
534 new_user->state = state;
535 llist_insert_head (&resource->users, llist_init (&new_user->hdr.node));
537 #if NOBUG_USE_PTHREAD
538 if (!node)
540 /* no node found, create a new one */
541 node = nobug_resource_node_new (resource, user?user->current:NULL);
542 if (!node)
544 nobug_resource_error = "internal allocation error";
545 return NULL;
549 new_user->current = node;
550 new_user->thread = tls;
551 llist_insert_tail (&tls->res_stack, llist_init (&new_user->res_stack));
552 #endif
554 NOBUG_RESOURCE_UNLOCK;
555 return new_user;
559 #if NOBUG_USE_PTHREAD
560 static int
561 nobug_resource_node_parent_cmpfn (const_LList a, const_LList b, void* extra)
563 (void) extra;
564 return ((struct nobug_resource_node*)a)->parent ==
565 ((struct nobug_resource_node*)b)->parent?0:-1;
567 #endif
571 nobug_resource_leave (struct nobug_resource_user* user)
573 NOBUG_RESOURCE_LOCK;
575 if (!user)
577 nobug_resource_error = "no handle";
578 return 0;
582 if (!user->current?user->current->resource:NULL)
584 nobug_resource_error = "not entered";
585 return 0;
587 else
589 //dlalgo
590 //dlalgo HEAD^ Leaving Resources; nobug_resource_leave; fix resource lists
591 //dlalgo
592 //dlalgo store the tail and next aside, we need it later
593 //dlalgo
594 //dlalgo [source,c]
595 //dlalgo ---------------------------------------------------------------------
596 #if NOBUG_USE_PTHREAD //dlalgo VERBATIM
597 struct nobug_resource_user* tail = //dlalgo VERBATIM
598 LLIST_TO_STRUCTP (llist_tail (&user->thread->res_stack), //dlalgo VERBATIM
599 struct nobug_resource_user, //dlalgo VERBATIM
600 res_stack); //dlalgo VERBATIM
602 struct nobug_resource_user* next = //dlalgo VERBATIM
603 LLIST_TO_STRUCTP (llist_next (&user->res_stack), //dlalgo VERBATIM
604 struct nobug_resource_user, //dlalgo VERBATIM
605 res_stack); //dlalgo VERBATIM
606 //dlalgo ---------------------------------------------------------------------
607 //dlalgo
608 //dlalgo remove user struct from thread stack
609 //dlalgo The res_stack is now like it is supposed to look like with the 'user' removed.
610 //dlalgo We now need to fix the node tree up to match this list.
611 //dlalgo
612 //dlalgo [source,c]
613 //dlalgo ---------------------------------------------------------------------
614 llist_unlink_fast_ (&user->res_stack); //dlalgo VERBATIM
615 //dlalgo ---------------------------------------------------------------------
616 //dlalgo
617 //dlalgo When the the user node was not the tail or only node of the thread stack, we have to check
618 //dlalgo (and possibly construct) a new node chain for it. No valdation of this chain needs to be done,
619 //dlalgo since it was already validated when entering the resources first.
620 //dlalgo
621 //dlalgo [source,c]
622 //dlalgo ---------------------------------------------------------------------
623 if (user != tail && !llist_is_empty (&user->thread->res_stack)) //dlalgo VERBATIM
624 { //dlalgo VERBATIM
625 struct nobug_resource_user* parent = NULL; //dlalgo VERBATIM
626 if (llist_head (&user->thread->res_stack) != &next->res_stack) //dlalgo VERBATIM
627 { //dlalgo VERBATIM
628 parent = //dlalgo VERBATIM
629 LLIST_TO_STRUCTP (llist_prev (&next->res_stack), //dlalgo VERBATIM
630 struct nobug_resource_user, //dlalgo VERBATIM
631 res_stack); //dlalgo VERBATIM
632 } //dlalgo VERBATIM
633 //dlalgo ---------------------------------------------------------------------
634 //dlalgo
635 //dlalgo iterate over all users following the removed node, finding nodes pointing to this users or
636 //dlalgo create new nodes.
637 //dlalgo
638 //dlalgo [source,c]
639 //dlalgo ---------------------------------------------------------------------
640 LLIST_FORRANGE (&next->res_stack, &user->thread->res_stack, n) //dlalgo VERBATIM
641 { //dlalgo VERBATIM
642 struct nobug_resource_user* cur = //dlalgo VERBATIM
643 LLIST_TO_STRUCTP (n, //dlalgo VERBATIM
644 struct nobug_resource_user, //dlalgo VERBATIM
645 res_stack); //dlalgo VERBATIM
646 //dlalgo VERBATIM
647 struct nobug_resource_record* resource = cur->current->resource;
649 //dlalgo ---------------------------------------------------------------------
650 //TODO this search could be optimized out after we creates a node once,
651 //TODO all following nodes need to be created too
652 //dlalgo
653 //dlalgo find the node pointing back to parent, create a new one if not found, rinse repeat
654 //dlalgo
655 //dlalgo [source,c]
656 //dlalgo ---------------------------------------------------------------------
657 struct nobug_resource_node templ = //dlalgo VERBATIM
658 { //dlalgo VERBATIM
659 {NULL, NULL}, //dlalgo ...
660 NULL, //dlalgo VERBATIM
661 parent?parent->current:NULL, //dlalgo ...
662 {NULL, NULL},
663 {NULL, NULL}
664 }; //dlalgo VERBATIM
665 //dlalgo VERBATIM
666 struct nobug_resource_node* node = (struct nobug_resource_node*) //dlalgo VERBATIM
667 llist_ufind (&resource->nodes, //dlalgo VERBATIM
668 &templ.node, //dlalgo VERBATIM
669 nobug_resource_node_parent_cmpfn, //dlalgo VERBATIM
670 NULL); //dlalgo VERBATIM
671 //dlalgo VERBATIM
672 if (!node) //dlalgo VERBATIM
673 { //dlalgo VERBATIM
674 node = nobug_resource_node_new (resource, //dlalgo VERBATIM
675 parent?parent->current:NULL); //dlalgo VERBATIM
676 if (!node) //dlalgo VERBATIM
677 { //dlalgo VERBATIM
678 nobug_resource_error = "internal allocation error"; //dlalgo VERBATIM
679 return 0; //dlalgo VERBATIM
680 } //dlalgo VERBATIM
681 } //dlalgo VERBATIM
682 //dlalgo VERBATIM
683 parent = cur; //dlalgo VERBATIM
684 } //dlalgo VERBATIM
685 } //dlalgo VERBATIM
686 //dlalgo ---------------------------------------------------------------------
687 //dlalgo
688 #endif
690 llist_unlink_fast_ (&user->hdr.node);
691 mpool_free (&nobug_resource_user_pool, user);
694 NOBUG_RESOURCE_UNLOCK;
695 return 1;
700 nobug_resource_state (struct nobug_resource_user* user,
701 enum nobug_resource_state state)
703 NOBUG_RESOURCE_LOCK;
704 if (!user)
706 nobug_resource_error = "no user handle";
707 return 0;
710 if (user->state == NOBUG_RESOURCE_WAITING)
712 if (state == NOBUG_RESOURCE_EXCLUSIVE)
714 /* check that every one is waiting */
715 LLIST_FOREACH (&user->current->resource->users, n)
716 if (((struct nobug_resource_user*)n)->state != NOBUG_RESOURCE_WAITING)
718 nobug_resource_error = "resource hold by another thread";
719 break;
722 #if NOBUG_USE_PTHREAD
723 else if (state == NOBUG_RESOURCE_RECURSIVE)
725 /* check that every one else is waiting */
726 LLIST_FOREACH (&user->current->resource->users, n)
727 if (((struct nobug_resource_user*)n)->state != NOBUG_RESOURCE_WAITING &&
728 ((struct nobug_resource_user*)n)->thread != nobug_thread_get ())
730 nobug_resource_error = "resource hold by another thread";
731 break;
734 #endif
735 else if (state == NOBUG_RESOURCE_SHARED)
737 /* check that every one else is waiting or shared */
738 LLIST_FOREACH (&user->current->resource->users, n)
739 if (((struct nobug_resource_user*)n)->state != NOBUG_RESOURCE_WAITING
740 && ((struct nobug_resource_user*)n)->state != NOBUG_RESOURCE_SHARED)
742 nobug_resource_error = "resource hold by another thread nonshared";
743 break;
746 else
747 nobug_resource_error = "invalid state transistion";
749 /* ok we got it */
750 if (!nobug_resource_error)
751 user->state = state;
753 else if (state == NOBUG_RESOURCE_WAITING)
754 user->state = state;
755 else
756 nobug_resource_error = "invalid state transistion";
758 if (nobug_resource_error)
759 return 0;
761 NOBUG_RESOURCE_UNLOCK;
762 return 1;
766 void
767 nobug_resource_dump (struct nobug_resource_record* resource, struct nobug_resource_dump_context* context)
769 if (!resource) return;
771 #if NOBUG_USE_PTHREAD
772 nobug_log (context->flag, context->level,
773 "%.10llu: RESOURCE_DUMP: %s:%d: %s: %s: "
774 "%s %s:%s: hold by %u entities:",
775 ++nobug_counter, context->file, context->line, nobug_thread_id_get(), context->func,
776 resource->hdr.extra, resource->type, resource->hdr.name,
777 llist_count (&resource->users));
778 #else
779 nobug_log (context->flag, context->level,
780 "%.10llu: RESOURCE_DUMP: %s:%d: %s: "
781 "%s %s:%s: hold by %u entities:",
782 ++nobug_counter, context->file, context->line, context->func,
783 resource->hdr.extra, resource->type, resource->hdr.name,
784 llist_count (&resource->users));
785 #endif
787 LLIST_FOREACH (&resource->users, n)
789 struct nobug_resource_user* node = (struct nobug_resource_user*)n;
790 #if NOBUG_USE_PTHREAD
791 nobug_log (context->flag, context->level,
792 "%.10llu: RESOURCE_DUMP:"NOBUG_TAB
793 "%s %s %s: %s",
794 ++nobug_counter,
795 node->hdr.extra, node->hdr.name, node->thread->thread_id,
796 nobug_resource_states[node->state]);
797 #else
798 nobug_log (context->flag, context->level,
799 "%.10llu: RESOURCE_DUMP: "NOBUG_TAB
800 "%s %s: %s",
801 ++nobug_counter,
802 node->hdr.extra, node->hdr.name, nobug_resource_states[node->state]);
803 #endif
808 void
809 nobug_resource_dump_all (struct nobug_resource_dump_context* context)
811 LLIST_FOREACH (&nobug_resource_registry, n)
813 struct nobug_resource_record* node = (struct nobug_resource_record*)n;
814 nobug_resource_dump (node, context);
819 void
820 nobug_resource_list (struct nobug_resource_dump_context* context)
822 #if NOBUG_USE_PTHREAD
823 nobug_log (context->flag, context->level,
824 "%.10llu: RESOURCE_LIST: %s:%d: %s: %s:",
825 ++nobug_counter, context->file, context->line, nobug_thread_id_get(), context->func);
826 #else
827 nobug_log (context->flag, context->level,
828 "%.10llu: RESOURCE_LIST: %s:%d: %s:",
829 ++nobug_counter, context->file, context->line, context->func);
830 #endif
832 if (!llist_is_empty (&nobug_resource_registry))
834 LLIST_FOREACH (&nobug_resource_registry, n)
836 struct nobug_resource_record* node = (struct nobug_resource_record*)n;
837 nobug_log (context->flag, context->level,
838 "%.10llu: RESOURCE_LIST:"NOBUG_TAB
839 "%s %s: %s: %p",
840 ++nobug_counter,
841 node->hdr.extra, node->type, node->hdr.name, node->object_id);
844 else
846 nobug_log (context->flag, context->level,
847 "%.10llu: RESOURCE_LIST:"NOBUG_TAB"No resources registered", ++nobug_counter);
852 // Local Variables:
853 // mode: C
854 // c-file-style: "gnu"
855 // indent-tabs-mode: nil
856 // End: