]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/lockmgr.c
Make out of freespace non-fatal for removable devices -- i.e. behaves like tape
[bacula/bacula] / bacula / src / lib / lockmgr.c
index 5e27b224a0ba46d274710ca642ee8241794611bf..d53aec863ecc61fb3b676d7185eb2726f7b145c0 100644 (file)
@@ -1,36 +1,26 @@
 /*
-   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)
 {
@@ -112,13 +109,13 @@ void lmgr_v(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;
 
 /*
@@ -149,7 +146,7 @@ public:
    void init(void *n, void *c) {
       node = n;
       child = c;
-      seen = LMGR_WHITE;      
+      seen = LMGR_WHITE;
    }
 
    void mark_as_seen(lmgr_color_t c) {
@@ -172,10 +169,10 @@ class lmgr_lock_t: public SMARTALLOC
 {
 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;
@@ -199,7 +196,7 @@ public:
 
 };
 
-/* 
+/*
  * Get the child list, ret must be already allocated
  */
 static void search_all_node(dlist *g, lmgr_node_t *v, alist *ret)
@@ -212,11 +209,11 @@ 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);
@@ -226,11 +223,11 @@ static bool visite(dlist *g, lmgr_node_t *v)
    //}
 
    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;
          }
@@ -247,7 +244,7 @@ static bool contains_cycle(dlist *g)
    lmgr_node_t *n;
    foreach_dlist(n, g) {
       if (n->seen == LMGR_WHITE) {
-         if (visite(g, n)) {
+         if (visit(g, n)) {
             return true;
          }
       }
@@ -257,41 +254,167 @@ static bool contains_cycle(dlist *g)
 
 /****************************************************************/
 
+/* 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) {
@@ -305,12 +428,20 @@ public:
    /*
     * 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++;
@@ -324,18 +455,29 @@ public:
          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++) {
@@ -357,7 +499,14 @@ public:
     * 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) {
@@ -365,16 +514,15 @@ public:
             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;
@@ -383,17 +531,20 @@ public:
          }
          /* 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);
    }
 } ;
@@ -412,7 +563,7 @@ class lmgr_dummy_thread_t: public lmgr_thread_t
  *
  */
 
-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 */
@@ -429,6 +580,7 @@ void lmgr_register_thread(lmgr_thread_t *item)
 {
    lmgr_p(&lmgr_global_mutex);
    {
+      item->int_thread_id = ++global_int_thread_id;
       global_mgr->prepend(item);
    }
    lmgr_v(&lmgr_global_mutex);
@@ -445,10 +597,23 @@ void lmgr_unregister_thread(lmgr_thread_t *item)
    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)
@@ -475,16 +640,16 @@ bool lmgr_detect_deadlock_unlocked()
           *
           */
          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);
    //}
@@ -493,7 +658,7 @@ bool lmgr_detect_deadlock_unlocked()
    if (ret) {
       printf("Found a deadlock !!!!\n");
    }
-   
+
    delete g;
    return ret;
 }
@@ -508,7 +673,7 @@ bool lmgr_detect_deadlock()
    bool ret=false;
    if (!lmgr_is_active()) {
       return ret;
-   } 
+   }
 
    lmgr_p(&lmgr_global_mutex);
    {
@@ -529,7 +694,7 @@ bool lmgr_detect_deadlock()
 }
 
 /*
- * !!! WARNING !!! 
+ * !!! WARNING !!!
  * Use this function is used only after a fatal signal
  * We don't use locking to display the information
  */
@@ -574,8 +739,12 @@ void *check_deadlock(void *)
    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();
@@ -600,6 +769,24 @@ inline lmgr_thread_t *lmgr_get_thread_info()
    }
 }
 
+/*
+ * 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
  */
@@ -610,7 +797,7 @@ void create_lmgr_key()
       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;
@@ -622,7 +809,7 @@ void create_lmgr_key()
          berrno be;
          Pmsg1(000, _("pthread_create failed: ERR=%s\n"),
                be.bstrerror(status));
-         ASSERT(0);
+         ASSERT2(0, "pthread_create failed");
       }
    }
 }
@@ -638,7 +825,7 @@ void lmgr_init_thread()
       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);
@@ -666,12 +853,16 @@ void lmgr_cleanup_thread()
 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);
@@ -683,7 +874,14 @@ void lmgr_cleanup_main()
    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)
@@ -710,25 +908,31 @@ int pthread_mutex_destroy(bthread_mutex_t *m)
    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;
          }
@@ -736,24 +940,25 @@ int bthread_kill(pthread_t thread, int sig,
    }
    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;
 }
 
@@ -771,14 +976,14 @@ int bthread_mutex_unlock_p(bthread_mutex_t *m, const char *file, int line)
 
 /*
  * 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;
 }
 
@@ -803,7 +1008,7 @@ int bthread_cond_wait_p(pthread_cond_t *cond,
 {
    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();
@@ -819,7 +1024,7 @@ int bthread_cond_timedwait_p(pthread_cond_t *cond,
 {
    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();
@@ -834,7 +1039,7 @@ int bthread_cond_wait_p(pthread_cond_t *cond,
 {
    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();
@@ -850,14 +1055,14 @@ int bthread_cond_timedwait_p(pthread_cond_t *cond,
 {
    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
@@ -922,7 +1127,7 @@ typedef struct {
    void *arg;
 } lmgr_thread_arg_t;
 
-extern "C" 
+extern "C"
 void *lmgr_thread_launcher(void *x)
 {
    void *ret=NULL;
@@ -945,7 +1150,7 @@ int lmgr_thread_create(pthread_t *thread,
                        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;
@@ -953,10 +1158,19 @@ int lmgr_thread_create(pthread_t *thread,
    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
  */
@@ -965,13 +1179,46 @@ void dbg_print_lock(FILE *fp)
    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__)
@@ -994,7 +1241,7 @@ void *self_lock(void *temp)
    P(mutex1);
    P(mutex1);
    V(mutex1);
-   
+
    return NULL;
 }
 
@@ -1036,11 +1283,40 @@ void *mix_rwl_mutex(void *temp)
 }
 
 
+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);
@@ -1056,7 +1332,7 @@ void *th1(void *temp)
    P(mutex1);
    sleep(2);
    P(mutex2);
-   
+
    lmgr_dump();
 
    sleep(10);
@@ -1098,10 +1374,34 @@ void *th_prio(void *a) {
    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)
@@ -1136,7 +1436,11 @@ int report()
    return err>0;
 }
 
-/* 
+void terminate(int sig)
+{
+}
+
+/*
  * TODO:
  *  - Must detect multiple lock
  *  - lock/unlock in wrong order
@@ -1146,11 +1450,12 @@ int main(int argc, char **argv)
 {
    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();
@@ -1163,6 +1468,10 @@ int main(int argc, char **argv)
       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);
 
@@ -1181,6 +1490,12 @@ int main(int argc, char **argv)
    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);
@@ -1195,7 +1510,7 @@ int main(int argc, char **argv)
    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);
 
 
@@ -1213,8 +1528,8 @@ int main(int argc, char **argv)
    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);
@@ -1245,9 +1560,9 @@ int main(int argc, char **argv)
    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);
 
@@ -1298,7 +1613,23 @@ int main(int argc, char **argv)
    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);
 //