1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 
20 /* Written by Eric Bollengier December 2008 */
21 
22 #ifndef LOCKMGR_H
23 #define LOCKMGR_H 1
24 
25 #include "mutex_list.h"     /* Manage mutex with priority in a central place */
26 
27 /*
28  * P and V op that don't use the lock manager (for memory allocation or on
29  * win32)
30  */
31 void lmgr_p(pthread_mutex_t *m);
32 void lmgr_v(pthread_mutex_t *m);
33 
34 /*
35  * Get integer thread id
36  */
37 intptr_t bthread_get_thread_id();
38 
39 /*
40  * Set the Thread Id of the current thread to limit I/O operations
41  */
42 int bthread_change_uid(uid_t uid, gid_t gid);
43 
44 #ifdef USE_LOCKMGR
45 
46 typedef struct bthread_mutex_t
47 {
48    pthread_mutex_t mutex;
49    int priority;
50 } bthread_mutex_t;
51 
52 /*
53  * We decide that a thread won't lock more than LMGR_MAX_LOCK at the same time
54  */
55 #define LMGR_MAX_LOCK 32
56 
57 int bthread_cond_wait_p(pthread_cond_t *cond,
58                         bthread_mutex_t *mutex,
59                         const char *file="*unknown*", int line=0);
60 
61 int bthread_cond_timedwait_p(pthread_cond_t *cond,
62                              bthread_mutex_t *mutex,
63                              const struct timespec * abstime,
64                              const char *file="*unknown*", int line=0);
65 
66 /* Same with real pthread_mutex_t */
67 int bthread_cond_wait_p(pthread_cond_t *cond,
68                         pthread_mutex_t *mutex,
69                         const char *file="*unknown*", int line=0);
70 
71 int bthread_cond_timedwait_p(pthread_cond_t *cond,
72                              pthread_mutex_t *mutex,
73                              const struct timespec * abstime,
74                              const char *file="*unknown*", int line=0);
75 
76 /* Replacement of pthread_mutex_lock()  but with real pthread_mutex_t */
77 int bthread_mutex_lock_p(pthread_mutex_t *m,
78                          const char *file="*unknown*", int line=0);
79 
80 /* Replacement for pthread_mutex_unlock() but with real pthread_mutex_t */
81 int bthread_mutex_unlock_p(pthread_mutex_t *m,
82                            const char *file="*unknown*", int line=0);
83 
84 /* Replacement of pthread_mutex_lock() */
85 int bthread_mutex_lock_p(bthread_mutex_t *m,
86                          const char *file="*unknown*", int line=0);
87 
88 /* Replacement of pthread_mutex_unlock() */
89 int bthread_mutex_unlock_p(bthread_mutex_t *m,
90                            const char *file="*unknown*", int line=0);
91 
92 /*  Test if this mutex is locked by the current thread
93  *     0 - not locked by the current thread
94  *     1 - locked by the current thread
95  */
96 int lmgr_mutex_is_locked(void *m);
97 
98 /*
99  * Use them when you want use your lock yourself (ie rwlock)
100  */
101 
102 /* Call before requesting the lock */
103 void lmgr_pre_lock(void *m, int prio=0,
104                    const char *file="*unknown*", int line=0);
105 
106 /* Call after getting it */
107 void lmgr_post_lock();
108 
109 /* Same as pre+post lock */
110 void lmgr_do_lock(void *m, int prio=0,
111                   const char *file="*unknown*", int line=0);
112 
113 /* Call just before releasing the lock */
114 void lmgr_do_unlock(void *m);
115 
116 /* We use C++ mangling to make integration eaysier */
117 int pthread_mutex_init(bthread_mutex_t *m, const pthread_mutexattr_t *attr);
118 int pthread_mutex_destroy(bthread_mutex_t *m);
119 
120 void bthread_mutex_set_priority(bthread_mutex_t *m, int prio);
121 
122 /*
123  * Each thread have to call this function to put a lmgr_thread_t object
124  * in the stack and be able to call mutex_lock/unlock
125  */
126 void lmgr_init_thread();
127 
128 /*
129  * Know if the current thread is registred (used when we
130  * do not control thread creation)
131  */
132 bool lmgr_thread_is_initialized();
133 
134 /*
135  * Call this function at the end of the thread
136  */
137 void lmgr_cleanup_thread();
138 
139 /*
140  * Call this at the end of the program, it will release the
141  * global lock manager
142  */
143 void lmgr_cleanup_main();
144 
145 /*
146  * Dump each lmgr_thread_t object to stdout
147  */
148 void lmgr_dump();
149 
150 /*
151  * Search a deadlock
152  */
153 bool lmgr_detect_deadlock();
154 
155 /* Bit flags */
156 #define LMGR_EVENT_NONE    0
157 #define LMGR_EVENT_DUP     1       /* use strdup() to copy the comment (will set FREE) */
158 #define LMGR_EVENT_FREE    2       /* use free() when overwriting/deleting the comment */
159 #define LMGR_EVENT_INVALID 4       /* Used to mark the record invalid */
160 
161 /*
162  * Add event to the thread event list
163  */
164 void lmgr_add_event_p(const char *comment, intptr_t user_data, int32_t flags, const char *file, int32_t line);
165 #define lmgr_add_event(c, u) lmgr_add_event_p(c, u, 0, __FILE__, __LINE__)
166 #define lmgr_add_event_flag(c, u, f) lmgr_add_event_p(c, u, (f), __FILE__, __LINE__)
167 
168 /*
169  * Search a deadlock after a fatal signal
170  * no lock are granted, so the program must be
171  * stopped.
172  */
173 bool lmgr_detect_deadlock_unlocked();
174 
175 /*
176  * This function will run your thread with lmgr_init_thread() and
177  * lmgr_cleanup_thread().
178  */
179 int lmgr_thread_create(pthread_t *thread,
180                        const pthread_attr_t *attr,
181                        void *(*start_routine)(void*), void *arg);
182 
183 /*
184  * Can use SAFEKILL to check if the argument is a valid threadid
185  */
186 int bthread_kill(pthread_t thread, int sig,
187                  const char *file="*unknown*", int line=0);
188 
189 #define BTHREAD_MUTEX_NO_PRIORITY      {PTHREAD_MUTEX_INITIALIZER, 0}
190 #define BTHREAD_MUTEX_INITIALIZER      BTHREAD_MUTEX_NO_PRIORITY
191 
192 /* Define USE_LOCKMGR_PRIORITY to detect mutex wrong order */
193 #ifdef USE_LOCKMGR_PRIORITY
194 # define BTHREAD_MUTEX_PRIORITY(p)       {PTHREAD_MUTEX_INITIALIZER, p}
195 #else
196 # define BTHREAD_MUTEX_PRIORITY(p)       BTHREAD_MUTEX_NO_PRIORITY
197 #endif
198 
199 #define bthread_mutex_lock(x)      bthread_mutex_lock_p(x, __FILE__, __LINE__)
200 #define bthread_mutex_unlock(x)    bthread_mutex_unlock_p(x, __FILE__, __LINE__)
201 #define bthread_cond_wait(x,y)     bthread_cond_wait_p(x,y, __FILE__, __LINE__)
202 #define bthread_cond_timedwait(x,y,z) bthread_cond_timedwait_p(x,y,z, __FILE__, __LINE__)
203 
204 /*
205  * Define LOCKMGR_COMPLIANT to use real pthread functions
206  */
207 #define real_P(x) lmgr_p(&(x))
208 #define real_V(x) lmgr_v(&(x))
209 
210 #ifdef LOCKMGR_COMPLIANT
211 # define P(x)  lmgr_p(&(x))
212 # define pP(x) lmgr_p(x)
213 # define V(x)  lmgr_v(&(x))
214 # define pV(x) lmgr_v(x)
215 # define PFL(x, file, line)  lmgr_p(&(x))
216 # define VFL(x, file, line)  lmgr_v(&(x))
217 #else
218 # define P(x)  bthread_mutex_lock_p(&(x), __FILE__, __LINE__)
219 # define pP(x) bthread_mutex_lock_p((x), __FILE__, __LINE__)
220 # define V(x)  bthread_mutex_unlock_p(&(x), __FILE__, __LINE__)
221 # define pV(x) bthread_mutex_unlock_p((x), __FILE__, __LINE__)
222 # define PFL(x, file, line)  bthread_mutex_lock_p(&(x), file, line)
223 # define VFL(x, file, line)  bthread_mutex_unlock_p(&(x), file, line)
224 # define pthread_create(a, b, c, d)      lmgr_thread_create(a,b,c,d)
225 # define pthread_mutex_lock(x)           bthread_mutex_lock(x)
226 # define pthread_mutex_unlock(x)         bthread_mutex_unlock(x)
227 # define pthread_cond_wait(x,y)          bthread_cond_wait(x,y)
228 # define pthread_cond_timedwait(x,y,z)   bthread_cond_timedwait(x,y,z)
229 
230 # ifdef USE_LOCKMGR_SAFEKILL
231 #  define pthread_kill(a,b)      bthread_kill((a),(b), __FILE__, __LINE__)
232 # endif
233 #endif
234 
235 #else   /* !USE_LOCKMGR */
236 
237 # define lmgr_detect_deadlock()
238 # define lmgr_add_event_p(c, u, f, l)
239 # define lmgr_add_event(c, u)
240 # define lmgr_dump()
241 # define lmgr_thread_is_initialized() (1)
242 # define lmgr_init_thread()
243 # define lmgr_cleanup_thread()
244 # define lmgr_pre_lock(m, prio, f, l)
245 # define lmgr_post_lock()
246 # define lmgr_do_lock(m, prio, f, l)
247 # define lmgr_do_unlock(m)
248 # define lmgr_cleanup_main()
249 # define bthread_mutex_set_priority(a,b)
250 # define bthread_mutex_lock(a)           pthread_mutex_lock(a)
251 # define bthread_mutex_lock_p(a, f, l)   pthread_mutex_lock(a)
252 # define bthread_mutex_unlock(a)         pthread_mutex_unlock(a)
253 # define bthread_mutex_unlock_p(a, f, l) pthread_mutex_unlock(a)
254 # define lmgr_cond_wait(a,b)             pthread_cond_wait(a,b)
255 # define lmgr_cond_timedwait(a,b,c)      pthread_cond_timedwait(a,b,c)
256 # define bthread_mutex_t                 pthread_mutex_t
257 # define P(x)  lmgr_p(&(x))
258 # define pP(x) lmgr_p((x))
259 # define V(x)  lmgr_v(&(x))
260 # define pV(x) lmgr_v((x))
261 # define PFL(x, file, line)  lmgr_p(&(x))
262 # define VFL(x, file, line)  lmgr_v(&(x))
263 # define BTHREAD_MUTEX_PRIORITY(p)      PTHREAD_MUTEX_INITIALIZER
264 # define BTHREAD_MUTEX_NO_PRIORITY      PTHREAD_MUTEX_INITIALIZER
265 # define BTHREAD_MUTEX_INITIALIZER      PTHREAD_MUTEX_INITIALIZER
266 # define lmgr_mutex_is_locked(m)        (1)
267 # define bthread_cond_wait_p(w, x, y, z) pthread_cond_wait(w,x)
268 #endif  /* USE_LOCKMGR */
269 
270 /* a very basic lock_guard implementation :
271  * Lock_guard is mostly usefull to garanty mutex unlocking. Also, it's exception safe.
272  * usage example:
273  * void foobar()
274  * {
275  *    lock_guard protector(m_mutex); // m_mutex is locked
276  *    // the following section is protected until the function exits and/or returns
277  *
278  *    if (case == TRUE)
279  *    {
280  *       return; // when returning, m_mutex is unlocked
281  *    }
282  *    .
283  *    .
284  *    .
285  *
286  *    // when the method exits, m_mutex is unlocked.
287  * }
288  */
289 class lock_guard
290 {
291 public:
292 
293    pthread_mutex_t &m_mutex; /* the class keeps a reference on the mutex*/
294    const char *file;
295    int line;
296 
lock_guard(pthread_mutex_t & mutex)297    explicit lock_guard(pthread_mutex_t &mutex) : m_mutex(mutex),
298          file(NULL), line(0)
299    {
300       P(m_mutex); /* constructor locks the mutex*/
301    }
302 
lock_guard(pthread_mutex_t & mutex,const char * file,int line)303    explicit lock_guard(pthread_mutex_t &mutex, const char *file, int line) :
304          m_mutex(mutex), file(file), line(line)
305    {
306       PFL(m_mutex, file, line); /* constructor locks the mutex*/
307    }
308 
~lock_guard()309    ~lock_guard()
310    {
311       if (file != NULL) {
312          VFL(m_mutex, file, line); /* file & line of where the mutex was locked */
313       } else {
314          V(m_mutex);
315       }
316    }
317 };
318 
319 // Do magic! Creates a unique name using the line number, this need two level
320 #define _DO_JOIN( symbol1, symbol2 ) _DO_JOIN2( symbol1, symbol2 )
321 #define _DO_JOIN2( symbol1, symbol2 ) symbol1##symbol2
322 
323 /* You can check it by creating a variable with the expected name and see if the
324  * compiler complain, for example
325  * > LOCK_GUARD(mutex);  // this is line 123
326  * > lock_guard lock_guard_123(mutex);
327  */
328 
329 #define LOCK_GUARD(mutex) lock_guard _DO_JOIN(lock_guard_, __LINE__) (mutex, __FILE__, __LINE__)
330 
331 /* If you have a variable collision anyway, use the "postfix" version
332  * with a posfix name you have chosen
333  */
334 #define LOCK_GUARD_POSTFIX(mutex, postfix) lock_guard _DO_JOIN2(lock_guard_, postfix) (mutex, __FILE__, __LINE__)
335 
336 #endif  /* LOCKMGR_H */
337