]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/watchdog.c
Add second COALESCE to sql_find -- thanks to JML
[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(200, "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    foreach_dlist(p, wd_queue) {
105       if (p->destructor != NULL) {
106          p->destructor(p);
107       }
108       free(p);
109    }
110    delete wd_queue;
111    wd_queue = NULL;
112
113    foreach_dlist(p, wd_inactive) {
114       if (p->destructor != NULL) {
115          p->destructor(p);
116       }
117       free(p);
118    }
119
120    delete wd_inactive;
121    wd_inactive = NULL;
122    rwl_destroy(&lock);
123
124    return stat;
125 }
126
127 watchdog_t *new_watchdog(void)
128 {
129    watchdog_t *wd = (watchdog_t *)malloc(sizeof(watchdog_t));
130
131    if (!wd_is_init) {
132       start_watchdog();
133    }
134
135    if (wd == NULL) {
136       return NULL;
137    }
138    wd->one_shot = true;
139    wd->interval = 0;
140    wd->callback = NULL;
141    wd->destructor = NULL;
142    wd->data = NULL;
143
144    return wd;
145 }
146
147 bool register_watchdog(watchdog_t *wd)
148 {
149    if (!wd_is_init) {
150       Emsg0(M_ABORT, 0, "BUG! register_watchdog called before start_watchdog\n");
151    }
152    if (wd->callback == NULL) {
153       Emsg1(M_ABORT, 0, "BUG! Watchdog %p has NULL callback\n", wd);
154    }
155    if (wd->interval == 0) {
156       Emsg1(M_ABORT, 0, "BUG! Watchdog %p has zero interval\n", wd);
157    }
158
159    wd_lock();
160    wd->next_fire = watchdog_time + wd->interval;
161    wd_queue->append(wd);
162    Dmsg3(200, "Registered watchdog %p, interval %d%s\n",
163          wd, wd->interval, wd->one_shot ? " one shot" : "");
164    wd_unlock();
165
166    return false;
167 }
168
169 bool unregister_watchdog_unlocked(watchdog_t *wd)
170 {
171    watchdog_t *p;
172
173    if (!wd_is_init) {
174       Emsg0(M_ABORT, 0, "BUG! unregister_watchdog_unlocked called before start_watchdog\n");
175    }
176
177    foreach_dlist(p, wd_queue) {
178       if (wd == p) {
179          wd_queue->remove(wd);
180          Dmsg1(200, "Unregistered watchdog %p\n", wd);
181          return true;
182       }
183    }
184
185    foreach_dlist(p, wd_inactive) {
186       if (wd == p) {
187          wd_inactive->remove(wd);
188          Dmsg1(200, "Unregistered inactive watchdog %p\n", wd);
189          return true;
190       }
191    }
192
193    Dmsg1(200, "Failed to unregister watchdog %p\n", wd);
194    return false;
195 }
196
197 bool unregister_watchdog(watchdog_t *wd)
198 {
199    bool ret;
200
201    if (!wd_is_init) {
202       Emsg0(M_ABORT, 0, "BUG! unregister_watchdog called before start_watchdog\n");
203    }
204
205    wd_lock();
206    ret = unregister_watchdog_unlocked(wd);
207    wd_unlock();
208
209    return ret;
210 }
211
212 static void *watchdog_thread(void *arg)
213 {
214    Dmsg0(200, "NicB-reworked watchdog thread entered\n");
215
216    while (!quit) {
217       watchdog_t *p;
218
219       /* 
220        * We lock the jcr chain here because a good number of the
221        *   callback routines lock the jcr chain. We need to lock
222        *   it here *before* the watchdog lock because the SD message
223        *   thread first locks the jcr chain, then when closing the
224        *   job locks the watchdog chain. If the two thread do not
225        *   lock in the same order, we get a deadlock -- each holds
226        *   the other's needed lock.
227        */
228       lock_jcr_chain();
229       wd_lock();
230       watchdog_time = time(NULL);
231
232       foreach_dlist(p, wd_queue) {
233          if (p->next_fire < watchdog_time) {
234             /* Run the callback */
235             p->callback(p);
236
237             /* Reschedule (or move to inactive list if it's a one-shot timer) */
238             if (p->one_shot) {
239                wd_queue->remove(p);
240                wd_inactive->append(p);
241             } else {
242                p->next_fire = watchdog_time + p->interval;
243             }
244          }
245       }
246       wd_unlock();
247       unlock_jcr_chain();
248       bmicrosleep(SLEEP_TIME, 0);
249    }
250
251    Dmsg0(200, "NicB-reworked watchdog thread exited\n");
252    return NULL;
253 }
254
255 /*
256  * Watchdog lock, this can be called multiple times by the same
257  *   thread without blocking, but must be unlocked the number of
258  *   times it was locked.
259  */
260 static void wd_lock()
261 {
262    int errstat;
263    if ((errstat=rwl_writelock(&lock)) != 0) {
264       Emsg1(M_ABORT, 0, "rwl_writelock failure. ERR=%s\n",
265            strerror(errstat));
266    }
267 }    
268
269 /*
270  * Unlock the watchdog. This can be called multiple times by the
271  *   same thread up to the number of times that thread called
272  *   wd_ lock()/
273  */
274 static void wd_unlock()
275 {
276    int errstat;
277    if ((errstat=rwl_writeunlock(&lock)) != 0) {
278       Emsg1(M_ABORT, 0, "rwl_writeunlock failure. ERR=%s\n",
279            strerror(errstat));
280    }
281 }