]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/watchdog.c
More changes to ensure that during thread switches the jcr
[bacula/bacula] / bacula / src / lib / watchdog.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Bacula thread watchdog routine. General routine that 
30  *  allows setting a watchdog timer with a callback that is
31  *  called when the timer goes off.
32  *
33  *  Kern Sibbald, January MMII
34  *
35  */
36
37 #include "bacula.h"
38 #include "jcr.h"
39
40 /* Exported globals */
41 utime_t watchdog_time = 0;            /* this has granularity of SLEEP_TIME */
42 utime_t watchdog_sleep_time = 60;     /* examine things every 60 seconds */
43
44 /* Locals */
45 static pthread_mutex_t timer_mutex = PTHREAD_MUTEX_INITIALIZER;
46 static pthread_cond_t timer = PTHREAD_COND_INITIALIZER;
47
48 /* Forward referenced functions */
49 extern "C" void *watchdog_thread(void *arg);
50
51 static void wd_lock();
52 static void wd_unlock();
53
54 /* Static globals */
55 static bool quit = false;;
56 static bool wd_is_init = false;
57 static brwlock_t lock;                /* watchdog lock */
58
59 static pthread_t wd_tid;
60 static dlist *wd_queue;
61 static dlist *wd_inactive;
62
63 /*
64  * Start watchdog thread
65  *
66  *  Returns: 0 on success
67  *           errno on failure
68  */
69 int start_watchdog(void)
70 {
71    int stat;
72    watchdog_t *dummy = NULL;
73    int errstat;
74
75    if (wd_is_init) {
76       return 0;
77    }
78    Dmsg0(800, "Initialising NicB-hacked watchdog thread\n");
79    watchdog_time = time(NULL);
80
81    if ((errstat=rwl_init(&lock)) != 0) {
82       berrno be;
83       Jmsg1(NULL, M_ABORT, 0, _("Unable to initialize watchdog lock. ERR=%s\n"),
84             be.bstrerror(errstat));
85    }
86    wd_queue = New(dlist(dummy, &dummy->link));
87    wd_inactive = New(dlist(dummy, &dummy->link));
88    wd_is_init = true;
89
90    if ((stat = pthread_create(&wd_tid, NULL, watchdog_thread, NULL)) != 0) {
91       return stat;
92    }
93    return 0;
94 }
95
96 /*
97  * Wake watchdog timer thread so that it walks the
98  *  queue and adjusts its wait time (or exits).
99  */
100 static void ping_watchdog()
101 {
102    P(timer_mutex);
103    pthread_cond_signal(&timer);
104    V(timer_mutex);
105    bmicrosleep(0, 100);
106 }
107
108 /*
109  * Terminate the watchdog thread
110  *
111  * Returns: 0 on success
112  *          errno on failure
113  */
114 int stop_watchdog(void)
115 {
116    int stat;
117    watchdog_t *p;
118
119    if (!wd_is_init) {
120       return 0;
121    }
122
123    quit = true;                       /* notify watchdog thread to stop */
124    ping_watchdog();
125
126    stat = pthread_join(wd_tid, NULL);
127
128    while (!wd_queue->empty()) {
129       void *item = wd_queue->first();
130       wd_queue->remove(item);
131       p = (watchdog_t *)item;
132       if (p->destructor != NULL) {
133          p->destructor(p);
134       }
135       free(p);
136    }
137    delete wd_queue;
138    wd_queue = NULL;
139
140    while (!wd_inactive->empty()) {
141       void *item = wd_inactive->first();
142       wd_inactive->remove(item);
143       p = (watchdog_t *)item;
144       if (p->destructor != NULL) {
145          p->destructor(p);
146       }
147       free(p);
148    }
149    delete wd_inactive;
150    wd_inactive = NULL;
151    rwl_destroy(&lock);
152    wd_is_init = false;
153
154    return stat;
155 }
156
157 watchdog_t *new_watchdog(void)
158 {
159    watchdog_t *wd = (watchdog_t *)malloc(sizeof(watchdog_t));
160
161    if (!wd_is_init) {
162       start_watchdog();
163    }
164
165    if (wd == NULL) {
166       return NULL;
167    }
168    wd->one_shot = true;
169    wd->interval = 0;
170    wd->callback = NULL;
171    wd->destructor = NULL;
172    wd->data = NULL;
173
174    return wd;
175 }
176
177 bool register_watchdog(watchdog_t *wd)
178 {
179    if (!wd_is_init) {
180       Jmsg0(NULL, M_ABORT, 0, _("BUG! register_watchdog called before start_watchdog\n"));
181    }
182    if (wd->callback == NULL) {
183       Jmsg1(NULL, M_ABORT, 0, _("BUG! Watchdog %p has NULL callback\n"), wd);
184    }
185    if (wd->interval == 0) {
186       Jmsg1(NULL, M_ABORT, 0, _("BUG! Watchdog %p has zero interval\n"), wd);
187    }
188
189    wd_lock();
190    wd->next_fire = watchdog_time + wd->interval;
191    wd_queue->append(wd);
192    Dmsg3(800, "Registered watchdog %p, interval %d%s\n",
193          wd, wd->interval, wd->one_shot ? " one shot" : "");
194    wd_unlock();
195    ping_watchdog();
196
197    return false;
198 }
199
200 bool unregister_watchdog(watchdog_t *wd)
201 {
202    watchdog_t *p;
203    bool ok = false;
204
205    if (!wd_is_init) {
206       Jmsg0(NULL, M_ABORT, 0, _("BUG! unregister_watchdog_unlocked called before start_watchdog\n"));
207    }
208
209    wd_lock();
210    foreach_dlist(p, wd_queue) {
211       if (wd == p) {
212          wd_queue->remove(wd);
213          Dmsg1(800, "Unregistered watchdog %p\n", wd);
214          ok = true;
215          goto get_out;
216       }
217    }
218
219    foreach_dlist(p, wd_inactive) {
220       if (wd == p) {
221          wd_inactive->remove(wd);
222          Dmsg1(800, "Unregistered inactive watchdog %p\n", wd);
223          ok = true;
224          goto get_out;
225       }
226    }
227
228    Dmsg1(800, "Failed to unregister watchdog %p\n", wd);
229
230 get_out:
231    wd_unlock();
232    ping_watchdog();
233    return ok;
234 }
235
236 /*
237  * This is the thread that walks the watchdog queue
238  *  and when a queue item fires, the callback is
239  *  invoked.  If it is a one shot, the queue item
240  *  is moved to the inactive queue.
241  */
242 extern "C" void *watchdog_thread(void *arg)
243 {
244    struct timespec timeout;
245    struct timeval tv;
246    struct timezone tz;
247    utime_t next_time;
248
249    set_jcr_in_tsd(INVALID_JCR);
250    Dmsg0(800, "NicB-reworked watchdog thread entered\n");
251
252    while (!quit) {
253       watchdog_t *p;
254
255       /*
256        *
257        *  NOTE. lock_jcr_chain removed, but the message below
258        *   was left until we are sure there are no deadlocks.
259        *  
260        * We lock the jcr chain here because a good number of the
261        *   callback routines lock the jcr chain. We need to lock
262        *   it here *before* the watchdog lock because the SD message
263        *   thread first locks the jcr chain, then when closing the
264        *   job locks the watchdog chain. If the two threads do not
265        *   lock in the same order, we get a deadlock -- each holds
266        *   the other's needed lock.
267        */
268       wd_lock();
269
270 walk_list:
271       watchdog_time = time(NULL);
272       next_time = watchdog_time + watchdog_sleep_time;
273       foreach_dlist(p, wd_queue) {
274          if (p->next_fire <= watchdog_time) {
275             /* Run the callback */
276             Dmsg2(3400, "Watchdog callback p=0x%p fire=%d\n", p, p->next_fire);
277             p->callback(p);
278
279             /* Reschedule (or move to inactive list if it's a one-shot timer) */
280             if (p->one_shot) {
281                wd_queue->remove(p);
282                wd_inactive->append(p);
283                goto walk_list;
284             } else {
285                p->next_fire = watchdog_time + p->interval;
286             }
287          }
288          if (p->next_fire <= next_time) {
289             next_time = p->next_fire;
290          }
291       }
292       wd_unlock();
293
294       /*
295        * Wait sleep time or until someone wakes us
296        */
297       gettimeofday(&tv, &tz);
298       timeout.tv_nsec = tv.tv_usec * 1000;
299       timeout.tv_sec = tv.tv_sec + next_time - time(NULL);
300       while (timeout.tv_nsec >= 1000000000) {
301          timeout.tv_nsec -= 1000000000;
302          timeout.tv_sec++;
303       }
304
305       Dmsg1(1900, "pthread_cond_timedwait %d\n", timeout.tv_sec - tv.tv_sec);
306       /* Note, this unlocks mutex during the sleep */
307       P(timer_mutex);
308       pthread_cond_timedwait(&timer, &timer_mutex, &timeout);
309       V(timer_mutex);
310    }
311
312    Dmsg0(800, "NicB-reworked watchdog thread exited\n");
313    return NULL;
314 }
315
316 /*
317  * Watchdog lock, this can be called multiple times by the same
318  *   thread without blocking, but must be unlocked the number of
319  *   times it was locked.
320  */
321 static void wd_lock()
322 {
323    int errstat;
324    if ((errstat=rwl_writelock(&lock)) != 0) {
325       berrno be;
326       Jmsg1(NULL, M_ABORT, 0, _("rwl_writelock failure. ERR=%s\n"),
327            be.bstrerror(errstat));
328    }
329 }
330
331 /*
332  * Unlock the watchdog. This can be called multiple times by the
333  *   same thread up to the number of times that thread called
334  *   wd_ lock()/
335  */
336 static void wd_unlock()
337 {
338    int errstat;
339    if ((errstat=rwl_writeunlock(&lock)) != 0) {
340       berrno be;
341       Jmsg1(NULL, M_ABORT, 0, _("rwl_writeunlock failure. ERR=%s\n"),
342            be.bstrerror(errstat));
343    }
344 }