/*
- Bacula® - The Network Backup Solution
-
- Copyright (C) 2008-2010 Free Software Foundation Europe e.V.
-
- The main author of Bacula is Kern Sibbald, with contributions from
- many others, a complete list can be found in the file AUTHORS.
- This program is Free Software; you can redistribute it and/or
- modify it under the terms of version two of the GNU General Public
- License as published by the Free Software Foundation, which is
- listed in the file LICENSE.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
- Bacula® is a registered trademark of Kern Sibbald.
- The licensor of Bacula is the Free Software Foundation Europe
- (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
- Switzerland, email:ftf@fsfeurope.org.
-*/
+ Bacula(R) - The Network Backup Solution
+
+ Copyright (C) 2000-2017 Kern Sibbald
+
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
+
+ You may use this file and others of this release according to the
+ license defined in the LICENSE file, which includes the Affero General
+ Public License, v3.0 ("AGPLv3") and some additional permissions and
+ terms pursuant to its AGPLv3 Section 7.
+
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+ Bacula(R) is a registered trademark of Kern Sibbald.
+*/
/*
How to use mutex with bad order usage detection
------------------------------------------------
- Note: see file mutex_list.h for current mutexes with
+ Note: see file mutex_list.h for current mutexes with
defined priorities.
Instead of using:
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
+#define LOCKMGR_COMPLIANT
#include "bacula.h"
#undef ASSERT
Pmsg3(000, _("ASSERT failed at %s:%i: %s \n"), f, l, #x); \
jcr[0] = 0; }
+#define ASSERT2_p(x,m,f,l) if (!(x)) { \
+ char *jcr = NULL; \
+ set_assert_msg(f, l, m); \
+ Pmsg4(000, _("ASSERT failed at %s:%i: %s (%s)\n"), f, l, #x, m); \
+ jcr[0] = 0; }
+
/*
- Inspired from
+ Inspired from
http://www.cs.berkeley.edu/~kamil/teaching/sp03/041403.pdf
This lock manager will replace some pthread calls. It can be
- enabled with _USE_LOCKMGR
+ enabled with USE_LOCKMGR
Some part of the code can't use this manager, for example the
rwlock object or the smartalloc lib. To disable LMGR, just add
- _LOCKMGR_COMPLIANT before the inclusion of "bacula.h"
+ LOCKMGR_COMPLIANT before the inclusion of "bacula.h"
cd build/src/tools
- g++ -g -c lockmgr.c -I.. -I../lib -D_USE_LOCKMGR -D_TEST_IT
+ g++ -g -c lockmgr.c -I.. -I../lib -DUSE_LOCKMGR -D_TEST_IT
g++ -o lockmgr lockmgr.o -lbac -L../lib/.libs -lssl -lpthread
*/
+#define DBGLEVEL_EVENT 50
/*
* pthread_mutex_lock for memory allocator and other
- * parts that are _LOCKMGR_COMPLIANT
+ * parts that are LOCKMGR_COMPLIANT
*/
void lmgr_p(pthread_mutex_t *m)
{
}
}
-#ifdef _USE_LOCKMGR
+#ifdef USE_LOCKMGR
typedef enum
{
LMGR_WHITE, /* never seen */
LMGR_BLACK, /* no loop */
- LMGR_GREY, /* seen before */
+ LMGR_GRAY /* already seen */
} lmgr_color_t;
/*
void init(void *n, void *c) {
node = n;
child = c;
- seen = LMGR_WHITE;
+ seen = LMGR_WHITE;
}
void mark_as_seen(lmgr_color_t c) {
{
public:
dlink link;
- void *lock;
+ void *lock; /* Link to the mutex (or any value) */
lmgr_state_t state;
int max_priority;
- int priority;
+ int priority; /* Current node priority */
const char *file;
int line;
};
-/*
+/*
* Get the child list, ret must be already allocated
*/
static void search_all_node(dlist *g, lmgr_node_t *v, alist *ret)
}
}
-static bool visite(dlist *g, lmgr_node_t *v)
+static bool visit(dlist *g, lmgr_node_t *v)
{
bool ret=false;
lmgr_node_t *n;
- v->mark_as_seen(LMGR_GREY);
+ v->mark_as_seen(LMGR_GRAY);
alist *d = New(alist(5, false)); /* use alist because own=false */
search_all_node(g, v, d);
//}
foreach_alist(n, d) {
- if (n->seen == LMGR_GREY) { /* already seen this node */
+ if (n->seen == LMGR_GRAY) { /* already seen this node */
ret = true;
goto bail_out;
} else if (n->seen == LMGR_WHITE) {
- if (visite(g, n)) {
+ if (visit(g, n)) {
ret = true;
goto bail_out;
}
lmgr_node_t *n;
foreach_dlist(n, g) {
if (n->seen == LMGR_WHITE) {
- if (visite(g, n)) {
+ if (visit(g, n)) {
return true;
}
}
/****************************************************************/
+/* lmgr_thread_event struct, some call can add events, and they will
+ * be dumped during a lockdump
+ */
+typedef struct
+{
+ int32_t id; /* Id of the event */
+ int32_t global_id; /* Current global id */
+ int32_t flags; /* Flags for this event */
+
+ int32_t line; /* from which line in filename */
+ const char *from; /* From where in the code (filename) */
+
+ char *comment; /* Comment */
+ intptr_t user_data; /* Optionnal user data (will print address) */
+
+} lmgr_thread_event;
+
+static int32_t global_event_id=0;
+
+static int global_int_thread_id=0; /* Keep an integer for each thread */
+
+/* Keep this number of event per thread */
+#ifdef _TEST_IT
+# define LMGR_THREAD_EVENT_MAX 15
+#else
+# define LMGR_THREAD_EVENT_MAX 1024
+#endif
+
+#define lmgr_thread_event_get_pos(x) ((x) % LMGR_THREAD_EVENT_MAX)
+
class lmgr_thread_t: public SMARTALLOC
{
public:
dlink link;
pthread_mutex_t mutex;
pthread_t thread_id;
+ intptr_t int_thread_id;
lmgr_lock_t lock_list[LMGR_MAX_LOCK];
int current;
int max;
int max_priority;
+ lmgr_thread_event events[LMGR_THREAD_EVENT_MAX];
+ int event_id;
+
lmgr_thread_t() {
int status;
if ((status = pthread_mutex_init(&mutex, NULL)) != 0) {
berrno be;
Pmsg1(000, _("pthread key create failed: ERR=%s\n"),
be.bstrerror(status));
- ASSERT(0);
+ ASSERT2(0, "pthread_mutex_init failed");
}
+ event_id = 0;
thread_id = pthread_self();
current = -1;
max = 0;
max_priority = 0;
}
+ /* Add event to the event list of the thread */
+ void add_event(const char *comment, intptr_t user_data, int32_t flags,
+ const char *from, int32_t line)
+ {
+ char *p;
+ int i = lmgr_thread_event_get_pos(event_id);
+
+ events[i].flags = LMGR_EVENT_INVALID;
+ p = events[i].comment;
+ events[i].comment = (char *)"*Freed*";
+
+ /* Shared between thread, just an indication about timing */
+ events[i].global_id = global_event_id++;
+ events[i].id = event_id;
+ events[i].line = line;
+ events[i].from = from;
+
+ /* It means we are looping over the ring, so we need
+ * to check if the memory need to be freed
+ */
+ if (event_id >= LMGR_THREAD_EVENT_MAX) {
+ if (events[i].flags & LMGR_EVENT_FREE) {
+ free(p);
+ }
+ }
+
+ /* We need to copy the memory */
+ if (flags & LMGR_EVENT_DUP) {
+ events[i].comment = bstrdup(comment);
+ flags |= LMGR_EVENT_FREE; /* force the free */
+
+ } else {
+ events[i].comment = (char *)comment;
+ }
+ events[i].user_data = user_data;
+ events[i].flags = flags; /* mark it valid */
+ event_id++;
+ }
+
+ void free_event_list() {
+ /* We first check how far we go in the event list */
+ int max = MIN(event_id, LMGR_THREAD_EVENT_MAX);
+ char *p;
+
+ for (int i = 0; i < max ; i++) {
+ if (events[i].flags & LMGR_EVENT_FREE) {
+ p = events[i].comment;
+ events[i].flags = LMGR_EVENT_INVALID;
+ events[i].comment = (char *)"*Freed*";
+ free(p);
+ }
+ }
+ }
+
+ void print_event(lmgr_thread_event *ev, FILE *fp) {
+ if (ev->flags & LMGR_EVENT_INVALID) {
+ return;
+ }
+ fprintf(fp, " %010d id=%010d %s data=%p at %s:%d\n",
+ ev->global_id,
+ ev->id,
+ NPRT(ev->comment),
+ (void *)ev->user_data,
+ ev->from,
+ ev->line);
+ }
+
void _dump(FILE *fp) {
- fprintf(fp, "threadid=%p max=%i current=%i\n",
+#ifdef HAVE_WIN32
+ fprintf(fp, "thread_id=%p int_threadid=%p max=%i current=%i\n",
+ (void *)(intptr_t)GetCurrentThreadId(), (void *)int_thread_id, max, current);
+#else
+ fprintf(fp, "threadid=%p max=%i current=%i\n",
(void *)thread_id, max, current);
+#endif
for(int i=0; i<=current; i++) {
- fprintf(fp, " lock=%p state=%s priority=%i %s:%i\n",
- lock_list[i].lock,
+ 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);
- }
+ }
+
+ if (debug_flags & DEBUG_PRINT_EVENT) {
+ /* Debug events */
+ fprintf(fp, " events:\n");
+
+ /* Display events between (event_id % LMGR_THREAD_EVENT_MAX) and LMGR_THREAD_EVENT_MAX */
+ if (event_id > LMGR_THREAD_EVENT_MAX) {
+ for (int i = event_id % LMGR_THREAD_EVENT_MAX ; i < LMGR_THREAD_EVENT_MAX ; i++)
+ {
+ print_event(&events[i], fp);
+ }
+ }
+
+ /* Display events between 0 and event_id % LMGR_THREAD_EVENT_MAX*/
+ for (int i = 0 ; i < (event_id % LMGR_THREAD_EVENT_MAX) ; i++)
+ {
+ print_event(&events[i], fp);
+ }
+ }
}
void dump(FILE *fp) {
/*
* Call before a lock operation (mark mutex as WANTED)
*/
- virtual void pre_P(void *m, int priority,
- const char *f="*unknown*", int l=0)
+ 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);
+
+ if (chk_dbglvl(DBGLEVEL_EVENT) && debug_flags & DEBUG_MUTEX_EVENT) {
+ /* Keep track of this event */
+ add_event("P()", (intptr_t)m, 0, f, l);
+ }
+
+ /* Fail if too many locks in use */
+ ASSERT2_p(current < LMGR_MAX_LOCK, "Too many locks in use", f, l);
+ /* Fail if the "current" value is out of bounds */
+ ASSERT2_p(current >= -1, "current lock value is out of bounds", f, l);
lmgr_p(&mutex);
{
current++;
max_priority = MAX(priority, max_priority);
}
lmgr_v(&mutex);
- ASSERT_p(!priority || priority >= max_prio, f, l);
+
+ /* Fail if we tried to lock a mutex with a lower priority than
+ * the current value. It means that you need to lock mutex in a
+ * different order to ensure that the priority field is always
+ * increasing. The mutex priority list is defined in mutex_list.h.
+ *
+ * Look the *.lockdump generated to get the list of all mutexes,
+ * and where they were granted to find the priority problem.
+ */
+ ASSERT2_p(!priority || priority >= max_prio,
+ "Mutex priority problem found, locking done in wrong order",
+ f, l);
}
/*
* Call after the lock operation (mark mutex as GRANTED)
*/
virtual void post_P() {
- ASSERT(current >= 0);
+ ASSERT2(current >= 0, "Lock stack when negative");
ASSERT(lock_list[current].state == LMGR_LOCK_WANTED);
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++) {
* Remove the mutex from the list
*/
virtual void do_V(void *m, const char *f="*unknown*", int l=0) {
- ASSERT_p(current >= 0, f, l);
+ int old_current = current;
+
+ /* Keep track of this event */
+ if (chk_dbglvl(DBGLEVEL_EVENT) && debug_flags & DEBUG_MUTEX_EVENT) {
+ add_event("V()", (intptr_t)m, 0, f, l);
+ }
+
+ ASSERT2_p(current >= 0, "No previous P found, the mutex list is empty", f, l);
lmgr_p(&mutex);
{
if (lock_list[current].lock == m) {
lock_list[current].state = LMGR_LOCK_EMPTY;
current--;
} else {
- ASSERT(current > 0);
- Pmsg3(0, "ERROR: wrong P/V order search lock=%p %s:%i\n", m, f, l);
- Pmsg4(000, "ERROR: wrong P/V order pos=%i lock=%p %s:%i\n",
- current, lock_list[current].lock, lock_list[current].file,
+ Pmsg3(0, "ERROR: V out of order lock=%p %s:%i dumping locks...\n", m, f, l);
+ Pmsg4(000, " wrong P/V order pos=%i lock=%p %s:%i\n",
+ current, lock_list[current].lock, lock_list[current].file,
lock_list[current].line);
for (int i=current-1; i >= 0; i--) { /* already seen current */
- Pmsg4(000, "ERROR: wrong P/V order pos=%i lock=%p %s:%i\n",
+ Pmsg4(000, " wrong P/V order pos=%i lock=%p %s:%i\n",
i, lock_list[i].lock, lock_list[i].file, lock_list[i].line);
if (lock_list[i].lock == m) {
- Pmsg3(000, "ERROR: FOUND P pos=%i %s:%i\n", i, f, l);
+ Pmsg3(000, "ERROR: FOUND P for out of order V at pos=%i %s:%i\n", i, f, l);
shift_list(i);
current--;
break;
}
/* reset max_priority to the last one */
if (current >= 0) {
- max_priority = lock_list[current].max_priority;
+ max_priority = lock_list[current].max_priority;
} else {
max_priority = 0;
}
}
lmgr_v(&mutex);
+ /* ASSERT2 should be called outside from the mutex lock */
+ ASSERT2_p(current != old_current, "V() called without a previous P()", f, l);
}
virtual ~lmgr_thread_t() {destroy();}
void destroy() {
+ free_event_list();
pthread_mutex_destroy(&mutex);
}
} ;
*
*/
-pthread_once_t key_lmgr_once = PTHREAD_ONCE_INIT;
+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 */
{
lmgr_p(&lmgr_global_mutex);
{
+ item->int_thread_id = ++global_int_thread_id;
global_mgr->prepend(item);
}
lmgr_v(&lmgr_global_mutex);
lmgr_p(&lmgr_global_mutex);
{
global_mgr->remove(item);
+#ifdef DEVELOPER
+ for(int i=0; i<=item->current; i++) {
+ lmgr_lock_t *lock = &item->lock_list[i];
+ if (lock->state == LMGR_LOCK_GRANTED) {
+ ASSERT2(0, "Thread exits with granted locks");
+ }
+ }
+#endif
}
lmgr_v(&lmgr_global_mutex);
}
+#ifdef HAVE_WIN32
+# define TID int_thread_id
+#else
+# define TID thread_id
+#endif
/*
* Search for a deadlock when it's secure to walk across
* locks list. (after lmgr_detect_deadlock or a fatal signal)
*
*/
if (lock->state == LMGR_LOCK_GRANTED) {
- node = New(lmgr_node_t((void*)lock->lock, (void*)item->thread_id));
+ node = New(lmgr_node_t((void*)lock->lock, (void*)item->TID));
} else if (lock->state == LMGR_LOCK_WANTED) {
- node = New(lmgr_node_t((void*)item->thread_id, (void*)lock->lock));
+ node = New(lmgr_node_t((void*)item->TID, (void*)lock->lock));
}
if (node) {
g->append(node);
}
}
- }
-
+ }
+
//foreach_dlist(node, g) {
// printf("g n=%p c=%p\n", node->node, node->child);
//}
if (ret) {
printf("Found a deadlock !!!!\n");
}
-
+
delete g;
return ret;
}
bool ret=false;
if (!lmgr_is_active()) {
return ret;
- }
+ }
lmgr_p(&lmgr_global_mutex);
{
}
/*
- * !!! WARNING !!!
+ * !!! WARNING !!!
* Use this function is used only after a fatal signal
* We don't use locking to display the information
*/
while (!bmicrosleep(30, 0)) {
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
if (lmgr_detect_deadlock()) {
+ /* If we have information about P()/V(), display them */
+ if (debug_flags & DEBUG_MUTEX_EVENT && chk_dbglvl(DBGLEVEL_EVENT)) {
+ debug_flags |= DEBUG_PRINT_EVENT;
+ }
lmgr_dump();
- ASSERT(0);
+ ASSERT2(0, "Lock deadlock"); /* Abort if we found a deadlock */
}
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old);
pthread_testcancel();
}
}
+/*
+ * Know if the current thread is registred (used when we
+ * do not control thread creation)
+ */
+bool lmgr_thread_is_initialized()
+{
+ return pthread_getspecific(lmgr_key) != NULL;
+}
+
+/* On windows, the thread id is a struct, and sometime (for debug or openssl),
+ * we need a int
+ */
+intptr_t bthread_get_thread_id()
+{
+ lmgr_thread_t *self = lmgr_get_thread_info();
+ return self->int_thread_id;
+}
+
/*
* launch once for all threads
*/
berrno be;
Pmsg1(000, _("pthread key create failed: ERR=%s\n"),
be.bstrerror(status));
- ASSERT(0);
+ ASSERT2(0, "pthread_key_create failed");
}
lmgr_thread_t *n=NULL;
berrno be;
Pmsg1(000, _("pthread_create failed: ERR=%s\n"),
be.bstrerror(status));
- ASSERT(0);
+ ASSERT2(0, "pthread_create failed");
}
}
}
berrno be;
Pmsg1(000, _("pthread key create failed: ERR=%s\n"),
be.bstrerror(status));
- ASSERT(0);
+ ASSERT2(0, "pthread_once failed");
}
lmgr_thread_t *l = New(lmgr_thread_t());
pthread_setspecific(lmgr_key, l);
void lmgr_cleanup_main()
{
dlist *temp;
-
+
if (!global_mgr) {
return;
}
if (use_undertaker) {
pthread_cancel(undertaker);
+#ifdef DEVELOPER
+ /* Should avoid memory leak reporting */
+ pthread_join(undertaker, NULL);
+#endif
}
lmgr_cleanup_thread();
lmgr_p(&lmgr_global_mutex);
lmgr_v(&lmgr_global_mutex);
}
-/*
+void lmgr_add_event_p(const char *comment, intptr_t user_data, int32_t flags,
+ const char *file, int32_t line)
+{
+ lmgr_thread_t *self = lmgr_get_thread_info();
+ self->add_event(comment, user_data, flags, file, line);
+}
+
+/*
* Set the priority of the lmgr mutex object
*/
void bthread_mutex_set_priority(bthread_mutex_t *m, int prio)
return pthread_mutex_destroy(&m->mutex);
}
-/*
+/*
* Replacement for pthread_kill (only with USE_LOCKMGR_SAFEKILL)
*/
-int bthread_kill(pthread_t thread, int sig,
+int bthread_kill(pthread_t thread, int sig,
const char *file, int line)
{
bool thread_found_in_process=false;
-
- /* We doesn't allow to send signal to ourself */
- ASSERT(!pthread_equal(thread, pthread_self()));
+ int ret=-1;
+ /* We dont allow to send signal to ourself */
+ if (pthread_equal(thread, pthread_self())) {
+ ASSERTD(!pthread_equal(thread, pthread_self()), "Wanted to pthread_kill ourself");
+ Dmsg3(10, "%s:%d send kill to self thread %p\n", file, line, thread);
+ errno = EINVAL;
+ return -1;
+ }
/* This loop isn't very efficient with dozens of threads but we don't use
- * signal very much, and this feature is for testing only
+ * signal very much
*/
lmgr_p(&lmgr_global_mutex);
{
lmgr_thread_t *item;
foreach_dlist(item, global_mgr) {
if (pthread_equal(thread, item->thread_id)) {
+ ret = pthread_kill(thread, sig);
thread_found_in_process=true;
break;
}
}
lmgr_v(&lmgr_global_mutex);
- /* Sending a signal to non existing thread can create problem
- * so, we can stop here.
- */
- ASSERT(thread_found_in_process == true);
-
- return pthread_kill(thread, sig);
+ /* Sending a signal to non existing thread can create problem */
+ if (!thread_found_in_process) {
+ ASSERTD(thread_found_in_process, "Wanted to pthread_kill non-existant thread");
+ Dmsg3(10, "%s:%d send kill to non-existant thread %p\n", file, line, thread);
+ errno=ECHILD;
+ }
+ return ret;
}
/*
* Replacement for pthread_mutex_lock()
- * Returns always ok
+ * Returns always ok
*/
int bthread_mutex_lock_p(bthread_mutex_t *m, const char *file, int line)
{
lmgr_thread_t *self = lmgr_get_thread_info();
self->pre_P(m, m->priority, file, line);
lmgr_p(&m->mutex);
- self->post_P();
+ self->post_P();
return 0;
}
/*
* Replacement for pthread_mutex_lock() but with real pthread_mutex_t
- * Returns always ok
+ * 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();
+ self->post_P();
return 0;
}
{
int ret;
lmgr_thread_t *self = lmgr_get_thread_info();
- self->do_V(m, file, line);
+ self->do_V(m, file, line);
ret = pthread_cond_wait(cond, m);
self->pre_P(m, 0, file, line);
self->post_P();
{
int ret;
lmgr_thread_t *self = lmgr_get_thread_info();
- self->do_V(m, file, line);
+ self->do_V(m, file, line);
ret = pthread_cond_timedwait(cond, m, abstime);
self->pre_P(m, 0, file, line);
self->post_P();
{
int ret;
lmgr_thread_t *self = lmgr_get_thread_info();
- self->do_V(m, 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();
{
int ret;
lmgr_thread_t *self = lmgr_get_thread_info();
- self->do_V(m, file, line);
+ 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;
}
-/* Test if this mutex is locked by the current thread
+/* Test if this mutex is locked by the current thread
* returns:
* 0 - unlocked
* 1 - locked by the current thread
void *arg;
} lmgr_thread_arg_t;
-extern "C"
+extern "C"
void *lmgr_thread_launcher(void *x)
{
void *ret=NULL;
void *(*start_routine)(void*), void *arg)
{
/* lmgr should be active (lmgr_init_thread() call in main()) */
- ASSERT(lmgr_is_active());
+ ASSERT2(lmgr_is_active(), "Lock manager not 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;
return pthread_create(thread, attr, lmgr_thread_launcher, a);
}
-#else /* _USE_LOCKMGR */
+#else /* USE_LOCKMGR */
+
+intptr_t bthread_get_thread_id()
+{
+# ifdef HAVE_WIN32
+ return (intptr_t)GetCurrentThreadId();
+# else
+ return (intptr_t)pthread_self();
+# endif
+}
/*
- * !!! WARNING !!!
+ * !!! WARNING !!!
* Use this function is used only after a fatal signal
* We don't use locking to display information
*/
Pmsg0(000, "lockmgr disabled\n");
}
-#endif /* _USE_LOCKMGR */
+#endif /* USE_LOCKMGR */
+
+#ifdef HAVE_LINUX_OS
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <sys/syscall.h>
+#endif
+
+/*
+ * Set the Thread Id of the current thread to limit I/O operations
+ */
+int bthread_change_uid(uid_t uid, gid_t gid)
+{
+#if defined(HAVE_WIN32) || defined(HAVE_WIN64)
+ /* TODO: Check the cygwin code for the implementation of setuid() */
+ errno = ENOSYS;
+ return -1;
+
+#elif defined(HAVE_LINUX_OS)
+ /* It can be also implemented with setfsuid() and setfsgid() */
+ int ret=0;
+ ret = syscall(SYS_setregid, getgid(), gid);
+ if (ret == -1) {
+ return -1;
+ }
+ return syscall(SYS_setreuid, getuid(), uid);
+
+#elif defined(HAVE_PTHREAD_SETUGID_NP)
+ return pthread_setugid_np(uid, gid);
+
+#endif
+ errno = ENOSYS;
+ return -1;
+}
+
#ifdef _TEST_IT
#include "lockmgr.h"
-#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) bthread_mutex_lock_p(&(x), __FILE__, __LINE__)
P(mutex1);
P(mutex1);
V(mutex1);
-
+
return NULL;
}
}
+void *thuid(void *temp)
+{
+ char buf[512];
+// if (restrict_job_permissions("eric", "users", buf, sizeof(buf)) < 0) {
+ if (bthread_change_uid(2, 100) == -1) {
+ berrno be;
+ fprintf(stderr, "Unable to change the uid err=%s\n", be.bstrerror());
+ } else {
+ fprintf(stderr, "UID set! %d:%d\n", (int)getuid(), (int)getgid());
+ mkdir("/tmp/adirectory", 0755);
+ system("touch /tmp/afile");
+ system("id");
+ fclose(fopen("/tmp/aaa", "a"));
+ }
+ if (bthread_change_uid(0, 0) == -1) {
+ berrno be;
+ fprintf(stderr, "Unable to change the uid err=%s\n", be.bstrerror());
+ } else {
+ fprintf(stderr, "UID set! %d:%d\n", (int)getuid(), (int)getgid());
+ sleep(5);
+ mkdir("/tmp/adirectory2", 0755);
+ system("touch /tmp/afile2");
+ system("id");
+ fclose(fopen("/tmp/aaa2", "a"));
+ }
+
+ return NULL;
+}
+
void *th2(void *temp)
{
P(mutex2);
P(mutex1);
-
+
lmgr_dump();
sleep(10);
P(mutex1);
sleep(2);
P(mutex2);
-
+
lmgr_dump();
sleep(10);
char buf[512];
bstrncpy(buf, my_prog, sizeof(buf));
bstrncat(buf, " priority", sizeof(buf));
- int ret = system(buf);
+ intptr_t ret = system(buf);
return (void*) ret;
}
+void *th_event1(void *a) {
+ for (int i=0; i < 10000; i++) {
+ if ((i % 7) == 0) {
+ lmgr_add_event_flag("strdup test", i, LMGR_EVENT_DUP);
+ } else {
+ lmgr_add_event("My comment", i);
+ }
+ }
+ sleep(5);
+ return NULL;
+}
+
+void *th_event2(void *a) {
+ for (int i=0; i < 10000; i++) {
+ if ((i % 2) == 0) {
+ lmgr_add_event_flag(bstrdup("free test"), i, LMGR_EVENT_FREE);
+ } else {
+ lmgr_add_event("My comment", i);
+ }
+ }
+ sleep(5);
+ return NULL;
+}
+
int err=0;
int nb=0;
void _ok(const char *file, int l, const char *op, int value, const char *label)
return err>0;
}
-/*
+void terminate(int sig)
+{
+}
+
+/*
* TODO:
* - Must detect multiple lock
* - lock/unlock in wrong order
{
void *ret=NULL;
lmgr_thread_t *self;
- pthread_t id1, id2, id3, tab[200];
+ pthread_t id1, id2, id3, id4, id5, tab[200];
bthread_mutex_t bmutex1;
pthread_mutex_t pmutex2;
+ debug_level = 10;
my_prog = argv[0];
-
+ init_signals(terminate);
use_undertaker = false;
lmgr_init_thread();
self = lmgr_get_thread_info();
return 0;
}
+ pthread_create(&id5, NULL, thuid, NULL);
+ pthread_join(id5, NULL);
+ fprintf(stderr, "UID %d:%d\n", (int)getuid(), (int)getgid());
+ exit(0);
pthread_mutex_init(&bmutex1, NULL);
bthread_mutex_set_priority(&bmutex1, 10);
lmgr_v(&mutex1.mutex); /* a bit dirty */
pthread_join(id1, NULL);
+ pthread_create(&id1, NULL, nolock, NULL);
+ sleep(2);
+ ok(bthread_kill(id1, SIGUSR2) == 0, "Kill existing thread");
+ pthread_join(id1, NULL);
+ ok(bthread_kill(id1, SIGUSR2) == -1, "Kill non-existing thread");
+ ok(bthread_kill(pthread_self(), SIGUSR2) == -1, "Kill self");
pthread_create(&id1, NULL, nolock, NULL);
sleep(2);
nok(lmgr_detect_deadlock(), "Check for multiple lock");
V(mutex1);
pthread_join(id1, NULL);
- pthread_join(id2, NULL);
+ pthread_join(id2, NULL);
pthread_join(id3, NULL);
nok(lmgr_detect_deadlock(), "Check for multiple rwlock");
pthread_join(id1, NULL);
- pthread_join(id2, NULL);
- pthread_join(id3, NULL);
+ pthread_join(id2, NULL);
+ pthread_join(id3, NULL);
rwl_writelock(&wr);
P(mutex1);
P(mutex4);
P(mutex5);
P(mutex6);
- ok(lmgr_mutex_is_locked(&mutex6) == 1, "Check if mutex is locked");
+ ok(lmgr_mutex_is_locked(&mutex6) == 1, "Check if mutex is locked");
V(mutex6);
- ok(lmgr_mutex_is_locked(&mutex6) == 0, "Check if mutex is locked");
+ ok(lmgr_mutex_is_locked(&mutex6) == 0, "Check if mutex is locked");
V(mutex5);
V(mutex4);
V(mutex_p1);
V(mutex_p2);
-// lmgr_dump();
+ for (int i=0; i < 10000; i++) {
+ if ((i % 7) == 0) {
+ lmgr_add_event_flag("xxxxxxxxxxxxxxxx strdup test xxxxxxxxxxxxxxxx", i, LMGR_EVENT_DUP);
+ } else {
+ lmgr_add_event("My comment", i);
+ }
+ }
+
+ pthread_create(&id4, NULL, th_event1, NULL);
+ pthread_create(&id5, NULL, th_event2, NULL);
+
+ sleep(2);
+
+ lmgr_dump();
+
+ pthread_join(id4, NULL);
+ pthread_join(id5, NULL);
//
// pthread_create(&id3, NULL, th3, NULL);
//