]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/watchdog.c
Restore JobIds in right order + Tru64 porting
[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 time_t watchdog_sleep_time = 10;      /* examine things every 10 seconds */
36
37
38 /* Forward referenced functions */
39 extern "C" void *watchdog_thread(void *arg);
40
41 static void wd_lock();
42 static void wd_unlock();
43
44 /* Static globals */
45 static bool quit = false;;
46 static bool wd_is_init = false;
47 static brwlock_t lock;                /* watchdog lock */
48
49 static pthread_t wd_tid;
50 static dlist *wd_queue;
51 static dlist *wd_inactive;
52
53 /*   
54  * Start watchdog thread
55  *
56  *  Returns: 0 on success
57  *           errno on failure
58  */
59 int start_watchdog(void)
60 {
61    int stat;
62    watchdog_t *dummy = NULL;
63    int errstat;
64
65    if (wd_is_init) {
66       return 0;
67    }
68    Dmsg0(400, "Initialising NicB-hacked watchdog thread\n");
69    watchdog_time = time(NULL);
70
71    if ((errstat=rwl_init(&lock)) != 0) {
72       Emsg1(M_ABORT, 0, _("Unable to initialize watchdog lock. ERR=%s\n"), 
73             strerror(errstat));
74    }
75    wd_queue = new dlist(wd_queue, &dummy->link);
76    wd_inactive = new dlist(wd_inactive, &dummy->link);
77
78    if ((stat = pthread_create(&wd_tid, NULL, watchdog_thread, NULL)) != 0) {
79       return stat;
80    }
81    wd_is_init = true;
82    return 0;
83 }
84
85 /*
86  * Terminate the watchdog thread
87  *
88  * Returns: 0 on success
89  *          errno on failure
90  */
91 int stop_watchdog(void)
92 {
93    int stat;
94    watchdog_t *p;    
95
96    if (!wd_is_init) {
97       return 0;
98    }
99
100    quit = true;                       /* notify watchdog thread to stop */
101    wd_is_init = false;
102
103    stat = pthread_join(wd_tid, NULL);
104
105    while (!wd_queue->empty()) {
106       void *item = wd_queue->first();
107       wd_queue->remove(item);
108       p = (watchdog_t *)item;
109       if (p->destructor != NULL) {
110          p->destructor(p);
111       }
112       free(p);
113    }
114    delete wd_queue;
115    wd_queue = NULL;
116
117    while (!wd_inactive->empty()) {
118       void *item = wd_inactive->first();
119       wd_inactive->remove(item);
120       p = (watchdog_t *)item;
121       if (p->destructor != NULL) {
122          p->destructor(p);
123       }
124       free(p);
125    }
126    delete wd_inactive;
127    wd_inactive = NULL;
128    rwl_destroy(&lock);
129
130    return stat;
131 }
132
133 watchdog_t *new_watchdog(void)
134 {
135    watchdog_t *wd = (watchdog_t *)malloc(sizeof(watchdog_t));
136
137    if (!wd_is_init) {
138       start_watchdog();
139    }
140
141    if (wd == NULL) {
142       return NULL;
143    }
144    wd->one_shot = true;
145    wd->interval = 0;
146    wd->callback = NULL;
147    wd->destructor = NULL;
148    wd->data = NULL;
149
150    return wd;
151 }
152
153 bool register_watchdog(watchdog_t *wd)
154 {
155    if (!wd_is_init) {
156       Emsg0(M_ABORT, 0, "BUG! register_watchdog called before start_watchdog\n");
157    }
158    if (wd->callback == NULL) {
159       Emsg1(M_ABORT, 0, "BUG! Watchdog %p has NULL callback\n", wd);
160    }
161    if (wd->interval == 0) {
162       Emsg1(M_ABORT, 0, "BUG! Watchdog %p has zero interval\n", wd);
163    }
164
165    wd_lock();
166    wd->next_fire = watchdog_time + wd->interval;
167    wd_queue->append(wd);
168    Dmsg3(400, "Registered watchdog %p, interval %d%s\n",
169          wd, wd->interval, wd->one_shot ? " one shot" : "");
170    wd_unlock();
171
172    return false;
173 }
174
175 bool unregister_watchdog_unlocked(watchdog_t *wd)
176 {
177    watchdog_t *p;
178
179    if (!wd_is_init) {
180       Emsg0(M_ABORT, 0, "BUG! unregister_watchdog_unlocked called before start_watchdog\n");
181    }
182
183    foreach_dlist(p, wd_queue) {
184       if (wd == p) {
185          wd_queue->remove(wd);
186          Dmsg1(400, "Unregistered watchdog %p\n", wd);
187          return true;
188       }
189    }
190
191    foreach_dlist(p, wd_inactive) {
192       if (wd == p) {
193          wd_inactive->remove(wd);
194          Dmsg1(400, "Unregistered inactive watchdog %p\n", wd);
195          return true;
196       }
197    }
198
199    Dmsg1(400, "Failed to unregister watchdog %p\n", wd);
200    return false;
201 }
202
203 bool unregister_watchdog(watchdog_t *wd)
204 {
205    bool ret;
206
207    if (!wd_is_init) {
208       Emsg0(M_ABORT, 0, "BUG! unregister_watchdog called before start_watchdog\n");
209    }
210
211    wd_lock();
212    ret = unregister_watchdog_unlocked(wd);
213    wd_unlock();
214
215    return ret;
216 }
217
218 extern "C" void *watchdog_thread(void *arg)
219 {
220    Dmsg0(400, "NicB-reworked watchdog thread entered\n");
221
222    while (!quit) {
223       watchdog_t *p, *q;
224
225       /* 
226        * We lock the jcr chain here because a good number of the
227        *   callback routines lock the jcr chain. We need to lock
228        *   it here *before* the watchdog lock because the SD message
229        *   thread first locks the jcr chain, then when closing the
230        *   job locks the watchdog chain. If the two threads do not
231        *   lock in the same order, we get a deadlock -- each holds
232        *   the other's needed lock.
233        */
234       lock_jcr_chain();
235       wd_lock();
236       watchdog_time = time(NULL);
237
238       foreach_dlist(p, wd_queue) {
239          if (p->next_fire < watchdog_time) {
240             /* Run the callback */
241             p->callback(p);
242
243             /* Reschedule (or move to inactive list if it's a one-shot timer) */
244             if (p->one_shot) {
245                /* 
246                 * Note, when removing an item while walking the list
247                 *  we must get the previous pointer (q) and set the
248                 *  current pointer (p) to this previous pointer after
249                 *  removing the current pointer, otherwise, we won't
250                 *  walk the rest of the list.
251                 */
252                q = (watchdog_t *)wd_queue->prev(p);
253                wd_queue->remove(p);
254                wd_inactive->append(p);
255                p = q;
256             } else {
257                p->next_fire = watchdog_time + p->interval;
258             }
259          }
260       }
261       wd_unlock();
262       unlock_jcr_chain();
263       bmicrosleep(watchdog_sleep_time, 0);
264    }
265
266    Dmsg0(400, "NicB-reworked watchdog thread exited\n");
267    return NULL;
268 }
269
270 /*
271  * Watchdog lock, this can be called multiple times by the same
272  *   thread without blocking, but must be unlocked the number of
273  *   times it was locked.
274  */
275 static void wd_lock()
276 {
277    int errstat;
278    if ((errstat=rwl_writelock(&lock)) != 0) {
279       Emsg1(M_ABORT, 0, "rwl_writelock failure. ERR=%s\n",
280            strerror(errstat));
281    }
282 }    
283
284 /*
285  * Unlock the watchdog. This can be called multiple times by the
286  *   same thread up to the number of times that thread called
287  *   wd_ lock()/
288  */
289 static void wd_unlock()
290 {
291    int errstat;
292    if ((errstat=rwl_writeunlock(&lock)) != 0) {
293       Emsg1(M_ABORT, 0, "rwl_writeunlock failure. ERR=%s\n",
294            strerror(errstat));
295    }
296 }