]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/watchdog.c
Fix typo.
[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 three of the GNU Affero 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 Affero 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  * Returns: 0 if the current thread is NOT the watchdog
65  *          1 if the current thread is the watchdog
66  */
67 bool is_watchdog()
68 {
69    if (wd_is_init && pthread_equal(pthread_self(), wd_tid)) {
70       return true;
71    } else {
72       return false;
73    }
74 }
75
76 /*
77  * Start watchdog thread
78  *
79  *  Returns: 0 on success
80  *           errno on failure
81  */
82 int start_watchdog(void)
83 {
84    int stat;
85    watchdog_t *dummy = NULL;
86    int errstat;
87
88    if (wd_is_init) {
89       return 0;
90    }
91    Dmsg0(800, "Initialising NicB-hacked watchdog thread\n");
92    watchdog_time = time(NULL);
93
94    if ((errstat=rwl_init(&lock)) != 0) {
95       berrno be;
96       Jmsg1(NULL, M_ABORT, 0, _("Unable to initialize watchdog lock. ERR=%s\n"),
97             be.bstrerror(errstat));
98    }
99    wd_queue = New(dlist(dummy, &dummy->link));
100    wd_inactive = New(dlist(dummy, &dummy->link));
101    wd_is_init = true;
102
103    if ((stat = pthread_create(&wd_tid, NULL, watchdog_thread, NULL)) != 0) {
104       return stat;
105    }
106    return 0;
107 }
108
109 /*
110  * Wake watchdog timer thread so that it walks the
111  *  queue and adjusts its wait time (or exits).
112  */
113 static void ping_watchdog()
114 {
115    P(timer_mutex);
116    pthread_cond_signal(&timer);
117    V(timer_mutex);
118    bmicrosleep(0, 100);
119 }
120
121 /*
122  * Terminate the watchdog thread
123  *
124  * Returns: 0 on success
125  *          errno on failure
126  */
127 int stop_watchdog(void)
128 {
129    int stat;
130    watchdog_t *p;
131
132    if (!wd_is_init) {
133       return 0;
134    }
135
136    quit = true;                       /* notify watchdog thread to stop */
137    ping_watchdog();
138
139    stat = pthread_join(wd_tid, NULL);
140
141    while (!wd_queue->empty()) {
142       void *item = wd_queue->first();
143       wd_queue->remove(item);
144       p = (watchdog_t *)item;
145       if (p->destructor != NULL) {
146          p->destructor(p);
147       }
148       free(p);
149    }
150    delete wd_queue;
151    wd_queue = NULL;
152
153    while (!wd_inactive->empty()) {
154       void *item = wd_inactive->first();
155       wd_inactive->remove(item);
156       p = (watchdog_t *)item;
157       if (p->destructor != NULL) {
158          p->destructor(p);
159       }
160       free(p);
161    }
162    delete wd_inactive;
163    wd_inactive = NULL;
164    rwl_destroy(&lock);
165    wd_is_init = false;
166
167    return stat;
168 }
169
170 watchdog_t *new_watchdog(void)
171 {
172    watchdog_t *wd = (watchdog_t *)malloc(sizeof(watchdog_t));
173
174    if (!wd_is_init) {
175       start_watchdog();
176    }
177
178    if (wd == NULL) {
179       return NULL;
180    }
181    wd->one_shot = true;
182    wd->interval = 0;
183    wd->callback = NULL;
184    wd->destructor = NULL;
185    wd->data = NULL;
186
187    return wd;
188 }
189
190 bool register_watchdog(watchdog_t *wd)
191 {
192    if (!wd_is_init) {
193       Jmsg0(NULL, M_ABORT, 0, _("BUG! register_watchdog called before start_watchdog\n"));
194    }
195    if (wd->callback == NULL) {
196       Jmsg1(NULL, M_ABORT, 0, _("BUG! Watchdog %p has NULL callback\n"), wd);
197    }
198    if (wd->interval == 0) {
199       Jmsg1(NULL, M_ABORT, 0, _("BUG! Watchdog %p has zero interval\n"), wd);
200    }
201
202    wd_lock();
203    wd->next_fire = watchdog_time + wd->interval;
204    wd_queue->append(wd);
205    Dmsg3(800, "Registered watchdog %p, interval %d%s\n",
206          wd, wd->interval, wd->one_shot ? " one shot" : "");
207    wd_unlock();
208    ping_watchdog();
209
210    return false;
211 }
212
213 bool unregister_watchdog(watchdog_t *wd)
214 {
215    watchdog_t *p;
216    bool ok = false;
217
218    if (!wd_is_init) {
219       Jmsg0(NULL, M_ABORT, 0, _("BUG! unregister_watchdog_unlocked called before start_watchdog\n"));
220    }
221
222    wd_lock();
223    foreach_dlist(p, wd_queue) {
224       if (wd == p) {
225          wd_queue->remove(wd);
226          Dmsg1(800, "Unregistered watchdog %p\n", wd);
227          ok = true;
228          goto get_out;
229       }
230    }
231
232    foreach_dlist(p, wd_inactive) {
233       if (wd == p) {
234          wd_inactive->remove(wd);
235          Dmsg1(800, "Unregistered inactive watchdog %p\n", wd);
236          ok = true;
237          goto get_out;
238       }
239    }
240
241    Dmsg1(800, "Failed to unregister watchdog %p\n", wd);
242
243 get_out:
244    wd_unlock();
245    ping_watchdog();
246    return ok;
247 }
248
249 /*
250  * This is the thread that walks the watchdog queue
251  *  and when a queue item fires, the callback is
252  *  invoked.  If it is a one shot, the queue item
253  *  is moved to the inactive queue.
254  */
255 extern "C" void *watchdog_thread(void *arg)
256 {
257    struct timespec timeout;
258    struct timeval tv;
259    struct timezone tz;
260    utime_t next_time;
261
262    set_jcr_in_tsd(INVALID_JCR);
263    Dmsg0(800, "NicB-reworked watchdog thread entered\n");
264
265    while (!quit) {
266       watchdog_t *p;
267
268       /*
269        *
270        *  NOTE. lock_jcr_chain removed, but the message below
271        *   was left until we are sure there are no deadlocks.
272        *  
273        * We lock the jcr chain here because a good number of the
274        *   callback routines lock the jcr chain. We need to lock
275        *   it here *before* the watchdog lock because the SD message
276        *   thread first locks the jcr chain, then when closing the
277        *   job locks the watchdog chain. If the two threads do not
278        *   lock in the same order, we get a deadlock -- each holds
279        *   the other's needed lock.
280        */
281       wd_lock();
282
283 walk_list:
284       watchdog_time = time(NULL);
285       next_time = watchdog_time + watchdog_sleep_time;
286       foreach_dlist(p, wd_queue) {
287          if (p->next_fire <= watchdog_time) {
288             /* Run the callback */
289             Dmsg2(3400, "Watchdog callback p=0x%p fire=%d\n", p, p->next_fire);
290             p->callback(p);
291
292             /* Reschedule (or move to inactive list if it's a one-shot timer) */
293             if (p->one_shot) {
294                wd_queue->remove(p);
295                wd_inactive->append(p);
296                goto walk_list;
297             } else {
298                p->next_fire = watchdog_time + p->interval;
299             }
300          }
301          if (p->next_fire <= next_time) {
302             next_time = p->next_fire;
303          }
304       }
305       wd_unlock();
306
307       /*
308        * Wait sleep time or until someone wakes us
309        */
310       gettimeofday(&tv, &tz);
311       timeout.tv_nsec = tv.tv_usec * 1000;
312       timeout.tv_sec = tv.tv_sec + next_time - time(NULL);
313       while (timeout.tv_nsec >= 1000000000) {
314          timeout.tv_nsec -= 1000000000;
315          timeout.tv_sec++;
316       }
317
318       Dmsg1(1900, "pthread_cond_timedwait %d\n", timeout.tv_sec - tv.tv_sec);
319       /* Note, this unlocks mutex during the sleep */
320       P(timer_mutex);
321       pthread_cond_timedwait(&timer, &timer_mutex, &timeout);
322       V(timer_mutex);
323    }
324
325    Dmsg0(800, "NicB-reworked watchdog thread exited\n");
326    return NULL;
327 }
328
329 /*
330  * Watchdog lock, this can be called multiple times by the same
331  *   thread without blocking, but must be unlocked the number of
332  *   times it was locked.
333  */
334 static void wd_lock()
335 {
336    int errstat;
337    if ((errstat=rwl_writelock(&lock)) != 0) {
338       berrno be;
339       Jmsg1(NULL, M_ABORT, 0, _("rwl_writelock failure. ERR=%s\n"),
340            be.bstrerror(errstat));
341    }
342 }
343
344 /*
345  * Unlock the watchdog. This can be called multiple times by the
346  *   same thread up to the number of times that thread called
347  *   wd_ lock()/
348  */
349 static void wd_unlock()
350 {
351    int errstat;
352    if ((errstat=rwl_writeunlock(&lock)) != 0) {
353       berrno be;
354       Jmsg1(NULL, M_ABORT, 0, _("rwl_writeunlock failure. ERR=%s\n"),
355            be.bstrerror(errstat));
356    }
357 }