Switzerland, email:ftf@fsfeurope.org.
*/
+/*
+ How to use mutex with bad order usage detection
+ ------------------------------------------------
+
+ Instead of using:
+ pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+ P(mutex);
+ ..
+ V(mutex);
+
+ use:
+ bthread_mutex_t mutex = BTHREAD_MUTEX_PRIORITY(1);
+ P(mutex);
+ ...
+ V(mutex);
+
+ Mutex that doesn't need this extra check can be declared as pthread_mutex_t.
+ You can use this object on pthread_mutex_lock/unlock/cond_wait/cond_timewait.
+
+ With dynamic creation, you can use:
+ bthread_mutex_t mutex;
+ pthread_mutex_init(&mutex);
+ bthread_mutex_set_priority(&mutex, 10);
+ pthread_mutex_destroy(&mutex);
+
+ */
+
#define _LOCKMGR_COMPLIANT
-#include "lockmgr.h"
+#include "bacula.h"
#undef ASSERT
#define ASSERT(x) if (!(x)) { \
char *jcr = NULL; \
- Pmsg3(000, _("%s:%i Failed ASSERT: %s\n"), __FILE__, __LINE__, #x); \
+ Pmsg3(000, _("Failed ASSERT: %s\n"), __FILE__, __LINE__, #x); \
+ jcr[0] = 0; }
+
+#define ASSERT_p(x,f,l) if (!(x)) { \
+ char *jcr = NULL; \
+ Pmsg3(000, _("from %s:%i Failed ASSERT: %s\n"), f, l, #x); \
jcr[0] = 0; }
/*
dlink link;
void *lock;
lmgr_state_t state;
-
+ int max_priority;
+ int priority;
+
const char *file;
int line;
lmgr_lock_t() {
lock = NULL;
state = LMGR_LOCK_EMPTY;
+ priority = max_priority = 0;
}
lmgr_lock_t(void *l) {
lmgr_lock_t lock_list[LMGR_MAX_LOCK];
int current;
int max;
+ int max_priority;
lmgr_thread_t() {
int status;
thread_id = pthread_self();
current = -1;
max = 0;
+ max_priority = 0;
}
void _dump(FILE *fp) {
fprintf(fp, "threadid=%p max=%i current=%i\n",
(void *)thread_id, max, current);
for(int i=0; i<=current; i++) {
- fprintf(fp, " lock=%p state=%c %s:%i\n",
- lock_list[i].lock, lock_list[i].state,
- lock_list[i].file, lock_list[i].line);
- }
+ fprintf(fp, " lock=%p state=%s priority=%i %s:%i\n",
+ lock_list[i].lock,
+ (lock_list[i].state=='W')?"Wanted ":"Granted",
+ lock_list[i].priority,
+ lock_list[i].file, lock_list[i].line);
+ }
}
void dump(FILE *fp) {
- pthread_mutex_lock(&mutex);
+ lmgr_p(&mutex);
{
_dump(fp);
}
- pthread_mutex_unlock(&mutex);
+ lmgr_v(&mutex);
}
/*
* Call before a lock operation (mark mutex as WANTED)
*/
- virtual void pre_P(void *m, const char *f="*unknown*", int l=0) {
- ASSERT(current < LMGR_MAX_LOCK);
- ASSERT(current >= -1);
- pthread_mutex_lock(&mutex);
+ virtual void pre_P(void *m, int priority,
+ const char *f="*unknown*", int l=0)
+ {
+ int max_prio = max_priority;
+ ASSERT_p(current < LMGR_MAX_LOCK, f, l);
+ ASSERT_p(current >= -1, f, l);
+ lmgr_p(&mutex);
{
current++;
lock_list[current].lock = m;
lock_list[current].state = LMGR_LOCK_WANTED;
lock_list[current].file = f;
lock_list[current].line = l;
+ lock_list[current].priority = priority;
+ lock_list[current].max_priority = MAX(priority, max_priority);
max = MAX(current, max);
+ max_priority = MAX(priority, max_priority);
}
- pthread_mutex_unlock(&mutex);
+ lmgr_v(&mutex);
+ ASSERT_p(!priority || priority >= max_prio, f, l);
}
/*
lock_list[current].state = LMGR_LOCK_GRANTED;
}
+ /* Using this function is some sort of bug */
void shift_list(int i) {
for(int j=i+1; j<=current; j++) {
lock_list[i] = lock_list[j];
lock_list[current].lock = NULL;
lock_list[current].state = LMGR_LOCK_EMPTY;
}
+ /* rebuild the priority list */
+ max_priority = 0;
+ for(int j=0; j< current; j++) {
+ max_priority = MAX(lock_list[j].priority, max_priority);
+ lock_list[j].max_priority = max_priority;
+ }
}
/*
* Remove the mutex from the list
*/
virtual void do_V(void *m, const char *f="*unknown*", int l=0) {
- ASSERT(current >= 0);
- pthread_mutex_lock(&mutex);
+ ASSERT_p(current >= 0, f, l);
+ lmgr_p(&mutex);
{
if (lock_list[current].lock == m) {
lock_list[current].lock = NULL;
}
}
}
+ /* reset max_priority to the last one */
+ if (current >= 0) {
+ max_priority = lock_list[current].max_priority;
+ } else {
+ max_priority = 0;
+ }
}
- pthread_mutex_unlock(&mutex);
+ lmgr_v(&mutex);
}
virtual ~lmgr_thread_t() {destroy();}
{
void do_V(void *m, const char *file, int l) {}
void post_P() {}
- void pre_P(void *m, const char *file, int l) {}
+ void pre_P(void *m, int priority, const char *file, int l) {}
};
/*
pthread_once_t key_lmgr_once = PTHREAD_ONCE_INIT;
static pthread_key_t lmgr_key; /* used to get lgmr_thread_t object */
-static dlist *global_mgr=NULL; /* used to store all lgmr_thread_t objects */
+static dlist *global_mgr = NULL; /* used to store all lgmr_thread_t objects */
static pthread_mutex_t lmgr_global_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_t undertaker;
+static bool use_undertaker=true;
#define lmgr_is_active() (global_mgr != NULL)
*/
void lmgr_register_thread(lmgr_thread_t *item)
{
- pthread_mutex_lock(&lmgr_global_mutex);
+ lmgr_p(&lmgr_global_mutex);
{
global_mgr->prepend(item);
}
- pthread_mutex_unlock(&lmgr_global_mutex);
+ lmgr_v(&lmgr_global_mutex);
}
/*
if (!lmgr_is_active()) {
return;
}
- pthread_mutex_lock(&lmgr_global_mutex);
+ lmgr_p(&lmgr_global_mutex);
{
global_mgr->remove(item);
}
- pthread_mutex_unlock(&lmgr_global_mutex);
+ lmgr_v(&lmgr_global_mutex);
}
/*
return ret;
}
- pthread_mutex_lock(&lmgr_global_mutex);
+ lmgr_p(&lmgr_global_mutex);
{
lmgr_thread_t *item;
foreach_dlist(item, global_mgr) {
- pthread_mutex_lock(&item->mutex);
+ lmgr_p(&item->mutex);
}
ret = lmgr_detect_deadlock_unlocked();
foreach_dlist(item, global_mgr) {
- pthread_mutex_unlock(&item->mutex);
+ lmgr_v(&item->mutex);
}
}
- pthread_mutex_unlock(&lmgr_global_mutex);
+ lmgr_v(&lmgr_global_mutex);
return ret;
}
*/
void lmgr_dump()
{
- pthread_mutex_lock(&lmgr_global_mutex);
+ lmgr_p(&lmgr_global_mutex);
{
lmgr_thread_t *item;
foreach_dlist(item, global_mgr) {
item->dump(stderr);
}
}
- pthread_mutex_unlock(&lmgr_global_mutex);
+ lmgr_v(&lmgr_global_mutex);
}
void cln_hdl(void *a)
}
/* This object is used when LMGR is not initialized */
-lmgr_dummy_thread_t dummy_lmgr;
+static lmgr_dummy_thread_t dummy_lmgr;
/*
* Retrieve the lmgr_thread_t object from the stack
lmgr_thread_t *n=NULL;
global_mgr = New(dlist(n, &n->link));
- if (pthread_create(&undertaker, NULL, check_deadlock, NULL) != 0) {
- berrno be;
- Pmsg1(000, _("pthread_create failed: ERR=%s\n"),
- be.bstrerror(status));
- ASSERT(0);
+ if (use_undertaker) {
+ status = pthread_create(&undertaker, NULL, check_deadlock, NULL);
+ if (status != 0) {
+ berrno be;
+ Pmsg1(000, _("pthread_create failed: ERR=%s\n"),
+ be.bstrerror(status));
+ ASSERT(0);
+ }
}
}
if (!global_mgr) {
return;
}
- pthread_cancel(undertaker);
+ if (use_undertaker) {
+ pthread_cancel(undertaker);
+ }
lmgr_cleanup_thread();
- pthread_mutex_lock(&lmgr_global_mutex);
+ lmgr_p(&lmgr_global_mutex);
{
temp = global_mgr;
- global_mgr=NULL;
+ global_mgr = NULL;
delete temp;
}
- pthread_mutex_unlock(&lmgr_global_mutex);
+ lmgr_v(&lmgr_global_mutex);
+}
+
+/*
+ * Set the priority of the lmgr mutex object
+ */
+void bthread_mutex_set_priority(bthread_mutex_t *m, int prio)
+{
+ m->priority = prio;
+}
+
+/*
+ * Replacement for pthread_mutex_init()
+ */
+int pthread_mutex_init(bthread_mutex_t *m, const pthread_mutexattr_t *attr)
+{
+ m->priority = 0;
+ return pthread_mutex_init(&m->mutex, attr);
+}
+
+/*
+ * Replacement for pthread_mutex_destroy()
+ */
+int pthread_mutex_destroy(bthread_mutex_t *m)
+{
+ return pthread_mutex_destroy(&m->mutex);
}
/*
* Replacement for pthread_mutex_lock()
+ * Returns always ok
*/
-int lmgr_mutex_lock(pthread_mutex_t *m, const char *file, int line)
+int bthread_mutex_lock_p(bthread_mutex_t *m, const char *file, int line)
{
- int ret;
lmgr_thread_t *self = lmgr_get_thread_info();
- self->pre_P(m, file, line);
- ret = pthread_mutex_lock(m);
+ self->pre_P(m, m->priority, file, line);
+ lmgr_p(&m->mutex);
self->post_P();
- return ret;
+ return 0;
}
/*
* Replacement for pthread_mutex_unlock()
+ * Returns always ok
+ */
+int bthread_mutex_unlock_p(bthread_mutex_t *m, const char *file, int line)
+{
+ lmgr_thread_t *self = lmgr_get_thread_info();
+ self->do_V(m, file, line);
+ lmgr_v(&m->mutex);
+ return 0;
+}
+
+/*
+ * Replacement for pthread_mutex_lock() but with real pthread_mutex_t
+ * Returns always ok
+ */
+int bthread_mutex_lock_p(pthread_mutex_t *m, const char *file, int line)
+{
+ lmgr_thread_t *self = lmgr_get_thread_info();
+ self->pre_P(m, 0, file, line);
+ lmgr_p(m);
+ self->post_P();
+ return 0;
+}
+
+/*
+ * Replacement for pthread_mutex_unlock() but with real pthread_mutex_t
+ * Returns always ok
*/
-int lmgr_mutex_unlock(pthread_mutex_t *m, const char *file, int line)
+int bthread_mutex_unlock_p(pthread_mutex_t *m, const char *file, int line)
{
lmgr_thread_t *self = lmgr_get_thread_info();
self->do_V(m, file, line);
- return pthread_mutex_unlock(m);
+ lmgr_v(m);
+ return 0;
+}
+
+
+/* TODO: check this
+ */
+int bthread_cond_wait_p(pthread_cond_t *cond,
+ pthread_mutex_t *m,
+ const char *file, int line)
+{
+ int ret;
+ lmgr_thread_t *self = lmgr_get_thread_info();
+ self->do_V(m, file, line);
+ ret = pthread_cond_wait(cond, m);
+ self->pre_P(m, 0, file, line);
+ self->post_P();
+ return ret;
+}
+
+/* TODO: check this
+ */
+int bthread_cond_timedwait_p(pthread_cond_t *cond,
+ pthread_mutex_t *m,
+ const struct timespec * abstime,
+ const char *file, int line)
+{
+ int ret;
+ lmgr_thread_t *self = lmgr_get_thread_info();
+ self->do_V(m, file, line);
+ ret = pthread_cond_timedwait(cond, m, abstime);
+ self->pre_P(m, 0, file, line);
+ self->post_P();
+ return ret;
}
/* TODO: check this
*/
-int lmgr_cond_wait(pthread_cond_t *cond,
- pthread_mutex_t *mutex,
- const char *file, int line)
+int bthread_cond_wait_p(pthread_cond_t *cond,
+ bthread_mutex_t *m,
+ const char *file, int line)
{
int ret;
lmgr_thread_t *self = lmgr_get_thread_info();
- self->do_V(mutex, file, line);
- ret = pthread_cond_wait(cond, mutex);
- self->pre_P(mutex, file, line);
+ self->do_V(m, file, line);
+ ret = pthread_cond_wait(cond, &m->mutex);
+ self->pre_P(m, m->priority, file, line);
+ self->post_P();
+ return ret;
+}
+
+/* TODO: check this
+ */
+int bthread_cond_timedwait_p(pthread_cond_t *cond,
+ bthread_mutex_t *m,
+ const struct timespec * abstime,
+ const char *file, int line)
+{
+ int ret;
+ lmgr_thread_t *self = lmgr_get_thread_info();
+ self->do_V(m, file, line);
+ ret = pthread_cond_timedwait(cond, &m->mutex, abstime);
+ self->pre_P(m, m->priority, file, line);
self->post_P();
return ret;
}
/*
* Use this function when the caller handle the mutex directly
*
- * lmgr_pre_lock(m);
+ * lmgr_pre_lock(m, 10);
* pthread_mutex_lock(m);
* lmgr_post_lock(m);
*/
-void lmgr_pre_lock(void *m, const char *file, int line)
+void lmgr_pre_lock(void *m, int prio, const char *file, int line)
{
lmgr_thread_t *self = lmgr_get_thread_info();
- self->pre_P(m, file, line);
+ self->pre_P(m, prio, file, line);
}
/*
/*
* Do directly pre_P and post_P (used by trylock)
*/
-void lmgr_do_lock(void *m, const char *file, int line)
+void lmgr_do_lock(void *m, int prio, const char *file, int line)
{
lmgr_thread_t *self = lmgr_get_thread_info();
- self->pre_P(m, file, line);
+ self->pre_P(m, prio, file, line);
self->post_P();
}
const pthread_attr_t *attr,
void *(*start_routine)(void*), void *arg)
{
+ /* lmgr should be active (lmgr_init_thread() call in main()) */
+ ASSERT(lmgr_is_active());
/* Will be freed by the child */
lmgr_thread_arg_t *a = (lmgr_thread_arg_t*) malloc(sizeof(lmgr_thread_arg_t));
a->start_routine = start_routine;
a->arg = arg;
- return pthread_create(thread, attr, lmgr_thread_launcher, a);
+ return pthread_create(thread, attr, lmgr_thread_launcher, a);
}
#else /* _USE_LOCKMGR */
#ifdef _TEST_IT
#include "lockmgr.h"
-#define pthread_mutex_lock(x) lmgr_mutex_lock(x)
-#define pthread_mutex_unlock(x) lmgr_mutex_unlock(x)
-#define pthread_cond_wait(x,y) lmgr_cond_wait(x,y)
-#define pthread_create(a, b, c, d) lmgr_thread_create(a,b,c,d)
+#define BTHREAD_MUTEX_NO_PRIORITY {PTHREAD_MUTEX_INITIALIZER, 0}
+#define BTHREAD_MUTEX_PRIORITY(p) {PTHREAD_MUTEX_INITIALIZER, p}
#undef P
#undef V
-#define P(x) lmgr_mutex_lock(&(x), __FILE__, __LINE__)
-#define V(x) lmgr_mutex_unlock(&(x), __FILE__, __LINE__)
-
-pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
-pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
-pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;
-pthread_mutex_t mutex4 = PTHREAD_MUTEX_INITIALIZER;
-pthread_mutex_t mutex5 = PTHREAD_MUTEX_INITIALIZER;
-pthread_mutex_t mutex6 = PTHREAD_MUTEX_INITIALIZER;
+#define P(x) bthread_mutex_lock_p(&(x), __FILE__, __LINE__)
+#define V(x) bthread_mutex_unlock_p(&(x), __FILE__, __LINE__)
+#define pthread_create(a, b, c, d) lmgr_thread_create(a,b,c,d)
+
+bthread_mutex_t mutex1 = BTHREAD_MUTEX_NO_PRIORITY;
+bthread_mutex_t mutex2 = BTHREAD_MUTEX_NO_PRIORITY;
+bthread_mutex_t mutex3 = BTHREAD_MUTEX_NO_PRIORITY;
+bthread_mutex_t mutex4 = BTHREAD_MUTEX_NO_PRIORITY;
+bthread_mutex_t mutex5 = BTHREAD_MUTEX_NO_PRIORITY;
+bthread_mutex_t mutex6 = BTHREAD_MUTEX_NO_PRIORITY;
+bthread_mutex_t mutex_p1 = BTHREAD_MUTEX_PRIORITY(1);
+bthread_mutex_t mutex_p2 = BTHREAD_MUTEX_PRIORITY(2);
+bthread_mutex_t mutex_p3 = BTHREAD_MUTEX_PRIORITY(3);
+static const char *my_prog;
void *self_lock(void *temp)
{
void *locker(void *temp)
{
- pthread_mutex_t *m = (pthread_mutex_t*) temp;
- pthread_mutex_lock(m);
- pthread_mutex_unlock(m);
+ bthread_mutex_t *m = (bthread_mutex_t*) temp;
+ P(*m);
+ V(*m);
return NULL;
}
return NULL;
}
+void *th_prio(void *a) {
+ char buf[512];
+ bstrncpy(buf, my_prog, sizeof(buf));
+ bstrncat(buf, " priority", sizeof(buf));
+ int ret = system(buf);
+ return (void*) ret;
+}
+
int err=0;
int nb=0;
void _ok(const char *file, int l, const char *op, int value, const char *label)
* - lock/unlock in wrong order
* - deadlock with 2 or 3 threads
*/
-int main()
+int main(int argc, char **argv)
{
+ void *ret=NULL;
+ lmgr_thread_t *self;
pthread_t id1, id2, id3, tab[200];
+ bthread_mutex_t bmutex1;
+ pthread_mutex_t pmutex2;
+ my_prog = argv[0];
+
+ use_undertaker = false;
lmgr_init_thread();
+ self = lmgr_get_thread_info();
+
+ if (argc == 2) { /* do priority check */
+ P(mutex_p2); /* not permited */
+ P(mutex_p1);
+ V(mutex_p1); /* never goes here */
+ V(mutex_p2);
+ return 0;
+ }
+
+ pthread_mutex_init(&bmutex1, NULL);
+ bthread_mutex_set_priority(&bmutex1, 10);
+
+ pthread_mutex_init(&pmutex2, NULL);
+ P(bmutex1);
+ ok(self->max_priority == 10, "Check self max_priority");
+ P(pmutex2);
+ ok(bmutex1.priority == 10, "Check bmutex_set_priority()");
+ V(pmutex2);
+ V(bmutex1);
+ ok(self->max_priority == 0, "Check self max_priority");
pthread_create(&id1, NULL, self_lock, NULL);
sleep(2);
ok(lmgr_detect_deadlock(), "Check self deadlock");
- lmgr_v(&mutex1); /* a bit dirty */
+ lmgr_v(&mutex1.mutex); /* a bit dirty */
pthread_join(id1, NULL);
sleep(1);
ok(lmgr_detect_deadlock(), "Check for deadlock");
+ pthread_create(&id3, NULL, th_prio, NULL);
+ pthread_join(id3, &ret);
+ ok(ret != 0, "Check for priority segfault");
+
+ P(mutex_p1);
+ ok(self->max_priority == 1, "Check max_priority 1/4");
+ P(mutex_p2);
+ ok(self->max_priority == 2, "Check max_priority 2/4");
+ P(mutex_p3);
+ ok(self->max_priority == 3, "Check max_priority 3/4");
+ P(mutex6);
+ ok(self->max_priority == 3, "Check max_priority 4/4");
+ V(mutex6);
+ ok(self->max_priority == 3, "Check max_priority 1/5");
+ V(mutex_p3);
+ ok(self->max_priority == 2, "Check max_priority 4/5");
+ V(mutex_p2);
+ ok(self->max_priority == 1, "Check max_priority 4/5");
+ V(mutex_p1);
+ ok(self->max_priority == 0, "Check max_priority 5/5");
+
+
+ P(mutex_p1);
+ P(mutex_p2);
+ P(mutex_p3);
+ P(mutex6);
+ ok(self->max_priority == 3, "Check max_priority mixed");
+ V(mutex_p2);
+ ok(self->max_priority == 3, "Check max_priority mixed");
+ V(mutex_p1);
+ ok(self->max_priority == 3, "Check max_priority mixed");
+ V(mutex_p3);
+ ok(self->max_priority == 0, "Check max_priority mixed");
+ V(mutex6);
+ ok(self->max_priority == 0, "Check max_priority mixed");
+
+ P(mutex_p1);
+ P(mutex_p2);
+ V(mutex_p1);
+ V(mutex_p2);
+
// lmgr_dump();
//
// pthread_create(&id3, NULL, th3, NULL);