#include #include #include #include struct mtx { int lock; char func[32]; /* calling funcion */ }; struct nick { int id; struct mtx mtx; /* mutex */ }; #define MUTEX_LOCKED(x) ((x)->lock == 1) #define MUTEX_OWNER(x) ((x)->func) #define mtx_lock(x) __mtx_lock((x), (char *)__func__) #define MUTEX_EVENT_LOCK 1 #define MUTEX_EVENT_UNLOCK 2 /* like printf() but display the function name */ #define dprintf(x, ...) __dprintf((char *)__func__, (x), ##__VA_ARGS__) struct mtx_event { struct mtx *mtx; /* mutex on which we want to check for its state */ int event; /* event type (see above) */ void (*callback)(); /* callback function */ void *args; /* arguments */ struct mtx_event *prev, *next; }; struct mtx_event *evhead; void test (struct nick *); void __dprintf (char *func, char *msg, ...) { char buf[1024]; va_list val; va_start(val, msg); vsnprintf(buf, 1023, msg, val); va_end(val); printf("%s(): %s",func, buf); } /* event adding function */ void mtx_event_add (struct mtx *mtx, int event, void (*callback)(), void *args) { struct mtx_event *tmp = (struct mtx_event *)malloc(sizeof(struct mtx_event)); memset(tmp, 0, sizeof(struct mtx_event)); tmp->mtx = mtx; tmp->event = event; tmp->callback = callback; tmp->args = args; if (evhead != NULL) { evhead->prev = tmp; tmp->next = evhead; } evhead = tmp; } /* event deleting function */ void mtx_event_del (struct mtx_event *event) { if (event == evhead) { evhead = event->next; if (evhead) evhead->prev = NULL; } else if (event->next == NULL) { event->prev->next = NULL; } else { event->next->prev = event->prev; event->prev->next = event->next; } free(event); } /* Call pending events and remove them. Only one event at the same time for the same mutex and the same type. Otherwise we'd have to add a mutex for this list... */ void mtx_event_call (struct mtx *mtx, int event) { struct mtx_event *tmp; for (tmp = evhead; tmp; tmp = tmp->next) { if (tmp->mtx == mtx && tmp->event == event) break; } if (tmp == NULL) return; tmp->callback(tmp->args); mtx_event_del(tmp); } /* Lock a mutex */ void __mtx_lock (struct mtx *mtx, char *func) { if (mtx->lock == 1) return; mtx->lock = 1; strncpy(mtx->func, func, 32); mtx_event_call(mtx, MUTEX_EVENT_LOCK); } /* Unlock a mutex */ void mtx_unlock (struct mtx *mtx) { if (mtx->lock == 0) return; mtx->lock = 0; memset(mtx->func, 0, 32); mtx_event_call(mtx, MUTEX_EVENT_UNLOCK); } /* Arguments for callback function */ struct test_args { struct nick *nick; }; /* The ones who had ever read the freebsd kernel source will recognize something :p */ void __test_callback (struct test_args *uap) { test(uap->nick); free(uap); } /* test function. If the mutex is locked, add an event. Otherwise, display the nick id. */ void test (struct nick *nick) { if (MUTEX_LOCKED(&nick->mtx)) { dprintf("Cannot access nick: mutex locked by %s()\n",MUTEX_OWNER(&nick->mtx)); struct test_args *uap = (struct test_args *)malloc(sizeof(struct test_args)); uap->nick = nick; mtx_event_add(&nick->mtx, MUTEX_EVENT_UNLOCK, &__test_callback, uap); return; } dprintf("Id: %d\n",nick->id); } int main() { struct nick a; memset(&a, 0, sizeof(a)); dprintf("locking mutex\n"); mtx_lock(&a.mtx); dprintf("mutex locked\n"); a.id = 42; dprintf("nick id changed to %d\n",a.id); dprintf("calling test\n"); test(&a); dprintf("test called\n"); dprintf("unlocking mutex\n"); mtx_unlock(&a.mtx); dprintf("mutex unlocked\n"); dprintf("recalling test\n"); test(&a); dprintf("test called\n"); return 0; }