]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/watchdog.c
Nic's watchdog code + cleanup jcr locking/use_count
[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-2003 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 /* This breaks Kern's #include rules, but I don't want to put it into bacula.h
34  * until it has been discussed with him */
35 #include "bsd_queue.h"
36
37 /* Exported globals */
38 time_t watchdog_time;                 /* this has granularity of SLEEP_TIME */
39
40 #define SLEEP_TIME 1                  /* examine things every second */
41
42 /* Forward referenced functions */
43 static void *watchdog_thread(void *arg);
44
45 /* Static globals */
46 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
47 static pthread_cond_t  timer = PTHREAD_COND_INITIALIZER;
48 static int quit;
49 static bool wd_is_init = false;
50
51 /* Forward referenced callback functions */
52 static void callback_child_timer(watchdog_t *self);
53 static void callback_thread_timer(watchdog_t *self);
54 static pthread_t wd_tid;
55
56 /* Static globals */
57 static TAILQ_HEAD(/* no struct */, s_watchdog_t) wd_queue =
58         TAILQ_HEAD_INITIALIZER(wd_queue);
59 static TAILQ_HEAD(/* no struct */, s_watchdog_t) wd_inactive =
60         TAILQ_HEAD_INITIALIZER(wd_inactive);
61
62 /*   
63  * Start watchdog thread
64  *
65  *  Returns: 0 on success
66  *           errno on failure
67  */
68 int start_watchdog(void)
69 {
70    int stat;
71
72    Dmsg0(200, "Initialising NicB-hacked watchdog thread\n");
73    watchdog_time = time(NULL);
74    quit = FALSE;
75    if ((stat = pthread_create(&wd_tid, NULL, watchdog_thread, NULL)) != 0) {
76       return stat;
77    }
78    wd_is_init = true;
79    return 0;
80 }
81
82 /*
83  * Terminate the watchdog thread
84  *
85  * Returns: 0 on success
86  *          errno on failure
87  */
88 int stop_watchdog(void)
89 {
90    int stat;
91    watchdog_t *p, *n;
92
93    if (!wd_is_init) {
94       Emsg0(M_ABORT, 0, "BUG! stop_watchdog called before start_watchdog\n");
95    }
96
97    Dmsg0(200, "Sending stop signal to NicB-hacked watchdog thread\n");
98    P(mutex);
99    quit = true;
100    stat = pthread_cond_signal(&timer);
101    V(mutex);
102
103    wd_is_init = false;
104
105    stat = pthread_join(wd_tid, NULL);
106
107    TAILQ_FOREACH_SAFE(p, &wd_queue, qe, n) {
108       TAILQ_REMOVE(&wd_queue, p, qe);
109       if (p->destructor != NULL) {
110          p->destructor(p);
111       }
112       free(p);
113    }
114
115    TAILQ_FOREACH_SAFE(p, &wd_inactive, qe, n) {
116       TAILQ_REMOVE(&wd_inactive, p, qe);
117       if (p->destructor != NULL) {
118          p->destructor(p);
119       }
120       free(p);
121    }
122
123    return stat;
124 }
125
126 watchdog_t *watchdog_new(void)
127 {
128    watchdog_t *wd = (watchdog_t *) malloc(sizeof(watchdog_t));
129
130    if (!wd_is_init) {
131       Emsg0(M_ABORT, 0, "BUG! watchdog_new called before start_watchdog\n");
132    }
133
134    if (wd == NULL) {
135       return NULL;
136    }
137    wd->one_shot = true;
138    wd->interval = 0;
139    wd->callback = NULL;
140    wd->destructor = NULL;
141    wd->data = NULL;
142
143    return wd;
144 }
145
146 bool register_watchdog(watchdog_t *wd)
147 {
148    if (!wd_is_init) {
149       Emsg0(M_ABORT, 0, "BUG! register_watchdog called before start_watchdog\n");
150    }
151    if (wd->callback == NULL) {
152       Emsg1(M_ABORT, 0, "BUG! Watchdog %p has NULL callback\n", wd);
153    }
154    if (wd->interval == 0) {
155       Emsg1(M_ABORT, 0, "BUG! Watchdog %p has zero interval\n", wd);
156    }
157
158    P(mutex);
159    wd->next_fire = watchdog_time + wd->interval;
160    TAILQ_INSERT_TAIL(&wd_queue, wd, qe);
161    Dmsg3(200, "Registered watchdog %p, interval %d%s\n",
162          wd, wd->interval, wd->one_shot ? " one shot" : "");
163    V(mutex);
164
165    return false;
166 }
167
168 bool unregister_watchdog_unlocked(watchdog_t *wd)
169 {
170    watchdog_t *p, *n;
171
172    if (!wd_is_init) {
173       Emsg0(M_ABORT, 0, "BUG! unregister_watchdog_unlocked called before start_watchdog\n");
174    }
175
176    TAILQ_FOREACH_SAFE(p, &wd_queue, qe, n) {
177       if (wd == p) {
178          TAILQ_REMOVE(&wd_queue, wd, qe);
179          Dmsg1(200, "Unregistered watchdog %p\n", wd);
180          return true;
181       }
182    }
183
184    TAILQ_FOREACH_SAFE(p, &wd_inactive, qe, n) {
185       if (wd == p) {
186          TAILQ_REMOVE(&wd_inactive, wd, qe);
187          Dmsg1(200, "Unregistered inactive watchdog %p\n", wd);
188          return true;
189       }
190    }
191
192    Dmsg1(200, "Failed to unregister watchdog %p\n", wd);
193
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    P(mutex);
206    ret = unregister_watchdog_unlocked(wd);
207    V(mutex);
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 (true) {
217       watchdog_t *p, *n;
218
219       P(mutex);
220       if (quit) {
221          V(mutex);
222          break;
223       }
224
225       watchdog_time = time(NULL);
226
227       TAILQ_FOREACH_SAFE(p, &wd_queue, qe, n) {
228          if (p->next_fire < watchdog_time) {
229             /* Run the callback */
230             p->callback(p);
231
232             /* Reschedule (or move to inactive list if it's a one-shot timer) */
233             if (p->one_shot) {
234                TAILQ_REMOVE(&wd_queue, p, qe);
235                TAILQ_INSERT_TAIL(&wd_inactive, p, qe);
236             } else {
237                p->next_fire = watchdog_time + p->interval;
238             }
239          }
240       }
241       V(mutex);
242       bmicrosleep(SLEEP_TIME, 0);
243    }
244
245    Dmsg0(200, "NicB-reworked watchdog thread exited\n");
246
247    return NULL;
248 }