]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/watchdog.c
Lots of cleanups + Christopher Hull's patches
[bacula/bacula] / bacula / src / lib / watchdog.c
1 /*
2  * Bacula thread watchdog routine. General routine that monitors
3  *  the daemon and signals a thread if it is blocked on a BSOCK
4  *  too long. This prevents catastropic long waits -- generally
5  *  due to Windows "hanging" the app.
6  *
7  *  Kern Sibbald, January MMII
8  *
9  */
10 /*
11    Copyright (C) 2000-2004 Kern Sibbald and John Walker
12
13    This program is free software; you can redistribute it and/or
14    modify it under the terms of the GNU General Public License as
15    published by the Free Software Foundation; either version 2 of
16    the License, or (at your option) any later version.
17
18    This program is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21    General Public License for more details.
22
23    You should have received a copy of the GNU General Public
24    License along with this program; if not, write to the Free
25    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
26    MA 02111-1307, USA.
27
28  */
29
30 #include "bacula.h"
31 #include "jcr.h"
32
33 /* Exported globals */
34 time_t watchdog_time = 0;             /* this has granularity of SLEEP_TIME */
35
36 #define SLEEP_TIME 1                  /* examine things every second */
37
38 /* Forward referenced functions */
39 static void *watchdog_thread(void *arg);
40 static void wd_lock();
41 static void wd_unlock();
42
43 /* Static globals */
44 static bool quit = false;;
45 static bool wd_is_init = false;
46 static brwlock_t lock;                /* watchdog lock */
47
48 static pthread_t wd_tid;
49 static dlist *wd_queue;
50 static dlist *wd_inactive;
51
52 /*   
53  * Start watchdog thread
54  *
55  *  Returns: 0 on success
56  *           errno on failure
57  */
58 int start_watchdog(void)
59 {
60    int stat;
61    watchdog_t *dummy = NULL;
62    int errstat;
63
64    if (wd_is_init) {
65       return 0;
66    }
67    Dmsg0(400, "Initialising NicB-hacked watchdog thread\n");
68    watchdog_time = time(NULL);
69
70    if ((errstat=rwl_init(&lock)) != 0) {
71       Emsg1(M_ABORT, 0, _("Unable to initialize watchdog lock. ERR=%s\n"), 
72             strerror(errstat));
73    }
74    wd_queue = new dlist(wd_queue, &dummy->link);
75    wd_inactive = new dlist(wd_inactive, &dummy->link);
76
77    if ((stat = pthread_create(&wd_tid, NULL, watchdog_thread, NULL)) != 0) {
78       return stat;
79    }
80    wd_is_init = true;
81    return 0;
82 }
83
84 /*
85  * Terminate the watchdog thread
86  *
87  * Returns: 0 on success
88  *          errno on failure
89  */
90 int stop_watchdog(void)
91 {
92    int stat;
93    watchdog_t *p;    
94
95    if (!wd_is_init) {
96       return 0;
97    }
98
99    quit = true;                       /* notify watchdog thread to stop */
100    wd_is_init = false;
101
102    stat = pthread_join(wd_tid, NULL);
103
104    while (!wd_queue->empty()) {
105       void *item = wd_queue->first();
106       wd_queue->remove(item);
107       p = (watchdog_t *)item;
108       if (p->destructor != NULL) {
109          p->destructor(p);
110       }
111       free(p);
112    }
113    delete wd_queue;
114    wd_queue = NULL;
115
116    while (!wd_inactive->empty()) {
117       void *item = wd_inactive->first();
118       wd_inactive->remove(item);
119       p = (watchdog_t *)item;
120       if (p->destructor != NULL) {
121          p->destructor(p);
122       }
123       free(p);
124    }
125    delete wd_inactive;
126    wd_inactive = NULL;
127    rwl_destroy(&lock);
128
129    return stat;
130 }
131
132 watchdog_t *new_watchdog(void)
133 {
134    watchdog_t *wd = (watchdog_t *)malloc(sizeof(watchdog_t));
135
136    if (!wd_is_init) {
137       start_watchdog();
138    }
139
140    if (wd == NULL) {
141       return NULL;
142    }
143    wd->one_shot = true;
144    wd->interval = 0;
145    wd->callback = NULL;
146    wd->destructor = NULL;
147    wd->data = NULL;
148
149    return wd;
150 }
151
152 bool register_watchdog(watchdog_t *wd)
153 {
154    if (!wd_is_init) {
155       Emsg0(M_ABORT, 0, "BUG! register_watchdog called before start_watchdog\n");
156    }
157    if (wd->callback == NULL) {
158       Emsg1(M_ABORT, 0, "BUG! Watchdog %p has NULL callback\n", wd);
159    }
160    if (wd->interval == 0) {
161       Emsg1(M_ABORT, 0, "BUG! Watchdog %p has zero interval\n", wd);
162    }
163
164    wd_lock();
165    wd->next_fire = watchdog_time + wd->interval;
166    wd_queue->append(wd);
167    Dmsg3(400, "Registered watchdog %p, interval %d%s\n",
168          wd, wd->interval, wd->one_shot ? " one shot" : "");
169    wd_unlock();
170
171    return false;
172 }
173
174 bool unregister_watchdog_unlocked(watchdog_t *wd)
175 {
176    watchdog_t *p;
177
178    if (!wd_is_init) {
179       Emsg0(M_ABORT, 0, "BUG! unregister_watchdog_unlocked called before start_watchdog\n");
180    }
181
182    foreach_dlist(p, wd_queue) {
183       if (wd == p) {
184          wd_queue->remove(wd);
185          Dmsg1(400, "Unregistered watchdog %p\n", wd);
186          return true;
187       }
188    }
189
190    foreach_dlist(p, wd_inactive) {
191       if (wd == p) {
192          wd_inactive->remove(wd);
193          Dmsg1(400, "Unregistered inactive watchdog %p\n", wd);
194          return true;
195       }
196    }
197
198    Dmsg1(400, "Failed to unregister watchdog %p\n", wd);
199    return false;
200 }
201
202 bool unregister_watchdog(watchdog_t *wd)
203 {
204    bool ret;
205
206    if (!wd_is_init) {
207       Emsg0(M_ABORT, 0, "BUG! unregister_watchdog called before start_watchdog\n");
208    }
209
210    wd_lock();
211    ret = unregister_watchdog_unlocked(wd);
212    wd_unlock();
213
214    return ret;
215 }
216
217 static void *watchdog_thread(void *arg)
218 {
219    Dmsg0(400, "NicB-reworked watchdog thread entered\n");
220
221    while (!quit) {
222       watchdog_t *p, *q;
223
224       /* 
225        * We lock the jcr chain here because a good number of the
226        *   callback routines lock the jcr chain. We need to lock
227        *   it here *before* the watchdog lock because the SD message
228        *   thread first locks the jcr chain, then when closing the
229        *   job locks the watchdog chain. If the two threads do not
230        *   lock in the same order, we get a deadlock -- each holds
231        *   the other's needed lock.
232        */
233       lock_jcr_chain();
234       wd_lock();
235       watchdog_time = time(NULL);
236
237       foreach_dlist(p, wd_queue) {
238          if (p->next_fire < watchdog_time) {
239             /* Run the callback */
240             p->callback(p);
241
242             /* Reschedule (or move to inactive list if it's a one-shot timer) */
243             if (p->one_shot) {
244                /* 
245                 * Note, when removing an item while walking the list
246                 *  we must get the previous pointer (q) and set the
247                 *  current pointer (p) to this previous pointer after
248                 *  removing the current pointer, otherwise, we won't
249                 *  walk the rest of the list.
250                 */
251                q = (watchdog_t *)wd_queue->prev(p);
252                wd_queue->remove(p);
253                wd_inactive->append(p);
254                p = q;
255             } else {
256                p->next_fire = watchdog_time + p->interval;
257             }
258          }
259       }
260       wd_unlock();
261       unlock_jcr_chain();
262       bmicrosleep(SLEEP_TIME, 0);
263    }
264
265    Dmsg0(400, "NicB-reworked watchdog thread exited\n");
266    return NULL;
267 }
268
269 /*
270  * Watchdog lock, this can be called multiple times by the same
271  *   thread without blocking, but must be unlocked the number of
272  *   times it was locked.
273  */
274 static void wd_lock()
275 {
276    int errstat;
277    if ((errstat=rwl_writelock(&lock)) != 0) {
278       Emsg1(M_ABORT, 0, "rwl_writelock failure. ERR=%s\n",
279            strerror(errstat));
280    }
281 }    
282
283 /*
284  * Unlock the watchdog. This can be called multiple times by the
285  *   same thread up to the number of times that thread called
286  *   wd_ lock()/
287  */
288 static void wd_unlock()
289 {
290    int errstat;
291    if ((errstat=rwl_writeunlock(&lock)) != 0) {
292       Emsg1(M_ABORT, 0, "rwl_writeunlock failure. ERR=%s\n",
293            strerror(errstat));
294    }
295 }