ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-Watchdog/Watchdog/Util.pm
Revision: 1.4
Committed: Tue Sep 1 16:33:34 2009 UTC (15 years, 1 month ago) by root
Branch: MAIN
Changes since 1.3: +5 -3 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 AnyEvent::Watchdog::Util - watchdog control and process management
4
5 =head1 SYNOPSIS
6
7 use AnyEvent::Watchdog::Util;
8
9 =head1 DESCRIPTION
10
11 This module can control the watchdog started by using
12 L<AnyEvent::Watchdog> in your main program, but it has useful
13 functionality even when not running under the watchdog at all, such as
14 program exit hooks.
15
16 =head1 VARIABLES/FUNCTIONS
17
18 The module supports the following variables and functions:
19
20 =over 4
21
22 =cut
23
24 package AnyEvent::Watchdog::Util;
25
26 # load modules we will use later anyways
27 use common::sense;
28 use AnyEvent ();
29 use Carp ();
30
31 our $VERSION = '1.0';
32
33 our $C;
34 BEGIN {
35 *C = \$AnyEvent::Watchdog::C;
36 }
37
38 our $AUTORESTART;
39 our $HEARTBEAT_W;
40
41 =item AnyEvent::Watchdog::Util::enabled
42
43 Return true when the program is running under the regime of
44 AnyEvent::Watchdog, false otherwise.
45
46 AnyEvent::Watchdog::Util::enabled
47 or die "watchdog not enabled...";
48 AnyEvent::Watchdog::Util::restart;
49
50 Note that if it returns defined, but false, then AnyEvent::Watchdog is
51 running, but you are in the watchdog process - you probably did something
52 very wrong in this case.
53
54 =cut
55
56 sub enabled() {
57 $AnyEvent::Watchdog::ENABLED
58 }
59
60 =item AnyEvent::Watchdog::restart_in [$timeout]
61
62 Tells the supervisor to restart the process when it exits (enable
63 autorestart), or forcefully after C<$timeout> seconds (minimum 1, maximum
64 255, default 60).
65
66 This function disables the heartbeat, if it was enabled. Also, after
67 calling this function the watchdog will ignore any further requests until
68 the program has restarted.
69
70 Good to call before you intend to exit, in case your clean-up handling
71 gets stuck.
72
73 =cut
74
75 sub restart_in(;$) {
76 my ($timeout) = @_;
77
78 return unless $C;
79
80 undef $HEARTBEAT_W;
81
82 $timeout = 60 unless defined $timeout;
83 $timeout = 1 if $timeout < 1;
84 $timeout = 255 if $timeout > 255;
85
86 syswrite $C, "\x01\x02" . chr $timeout;
87
88 # now make sure we dont' send it any further requests
89 our $OLD_C = $C; undef $C;
90 }
91
92 =item AnyEvent::Watchdog::restart [$timeout]
93
94 Just like C<restart_in>, but also calls C<exit 0>. This means that this is
95 the ideal method to force a restart.
96
97 =cut
98
99 sub restart(;$) {
100 &restart_in;
101 exit 0;
102 }
103
104 =item AnyEvent::Watchdog::Util::autorestart [$boolean]
105
106 =item use AnyEvent::Watchdog autorestart => $boolean
107
108 Enables or disables autorestart (initially disabled, default for
109 C<$boolean> is to enable): By default, the supervisor will exit if the
110 program exits or dies in any way. When enabling autorestart behaviour,
111 then the supervisor will try to restart the program after it dies.
112
113 Note that the supervisor will never autorestart when the child died with
114 SIGINT or SIGTERM.
115
116 =cut
117
118 sub autorestart(;$) {
119 my $AUTORESTART = !@_ || $_[0];
120
121 return unless $C;
122
123 unless (enabled) {
124 warn "AnyEvent::Watchdog: watchdog not running, cannot enable autorestart, ignoring.\n"
125 if $AUTORESTART;
126
127 $AUTORESTART = 0;
128
129 return;
130 }
131
132 syswrite $C, $AUTORESTART ? "\x01" : "\x00";
133 }
134
135 =item AnyEvent::Watchdog::heartbeat [$interval]
136
137 =item use AnyEvent::Watchdog heartbeat => $interval
138
139 Tells the supervisor to automatically kill the program if it doesn't
140 react for C<$interval> seconds (minium 1, maximum 255, default 60) , then
141 installs an AnyEvent timer the sends a regular heartbeat to the supervisor
142 twice as often.
143
144 Exit behaviour isn't changed, so if you want a restart instead of an exit,
145 you have to call C<autorestart>.
146
147 The heartbeat frequency can be changed as often as you want, an interval
148 of C<0> disables the heartbeat check again.
149
150 =cut
151
152 sub heartbeat(;$) {
153 my ($interval) = @_;
154
155 unless (enabled) {
156 warn "AnyEvent::Watchdog: watchdog not running, cannot enable heartbeat, ignoring.\n";
157 return;
158 }
159
160 $interval = 60 unless defined $interval;
161 $interval = 0 if $interval < 0;
162 $interval = 255 if $interval > 255;
163
164 $interval = int $interval;
165
166 syswrite $C, "\x03" . chr $interval
167 if $C;
168
169 $HEARTBEAT_W = AE::timer 0, $interval * 0.5, sub {
170 syswrite $C, "\x04"
171 if $C;
172 };
173 }
174
175 =item AnyEvent::Watchdog::on_exit { BLOCK; shift->() }
176
177 Installs an exit hook that is executed when the program is about to exit,
178 while event processing is still active to some extent.
179
180 The hook should do whatever it needs to do (close active connections,
181 disable listeners, write state, free resources etc.). When it is done, it
182 should call the code reference that has been passed to it.
183
184 This means you can install event handlers and return from the block, and
185 the program will not exit until the callback is invoked.
186
187 Exiting "the right way" is surprisingly difficult. This is what C<on_exit>
188 does:
189
190 It installs watchers for C<SIGTERM>, C<SIGINT>, C<SIGXCPU> and C<SIGXFSZ>,
191 and well as an C<END> block (the END block is actually registered
192 in L<AnyEvent::Watchdog>, if possible, so it executes as late as
193 possible). The signal handlers remember the signal and then call C<exit>,
194 invoking the C<END> callback.
195
196 The END block then checks for an exit code of C<255>, in which case
197 nothing happens (C<255> is the exit code that results from a program
198 error), otherwise it runs all C<on_exit> hooks and waits for their
199 completion using the event loop.
200
201 After all C<on_exit> hooks have finished, the program will either be
202 C<exit>ed with the relevant status code (if C<exit> was the cause for the
203 program exit), or it will reset the signal handler, unblock the signal and
204 kill itself with the signal, to ensure that the exit status is correct.
205
206 If the program is running under the watchdog, and autorestart is enabled,
207 then the heartbeat is disabled and the watchdog is told that the program
208 wishes to exit within C<60> seconds, after which it will be forcefully
209 killed.
210
211 All of this should ensure that C<on_exit> hooks are only executed when the
212 program is in a sane state and data structures are still intact. This only
213 works when the program does not install it's own TERM (etc.) watchers, of
214 course, as there is no control over them.
215
216 There is currently no way to unregister C<on_exit> hooks.
217
218 =cut
219
220 our @ON_EXIT;
221 our %SIG_W;
222 our $EXIT_STATUS; # >= 0 exit status; arrayref => signal, undef if exit was just called
223
224 # in case AnyEvent::Watchdog is not loaded, use our own END block
225 END { $AnyEvent::Watchdog::end && &$AnyEvent::Watchdog::end }
226
227 sub _exit {
228 $EXIT_STATUS = $? unless defined $EXIT_STATUS;
229
230 # we might have two END blocks trying to call us.
231 undef $AnyEvent::Watchdog::end;
232
233 if (enabled) {
234 undef $HEARTBEAT_W;
235 restart_in 60;
236 }
237
238 my $cv = AE::cv;
239 my $cb = sub { $cv->end };
240
241 $cv->begin;
242 while (@ON_EXIT) {
243 $cv->begin;
244 (pop @ON_EXIT)->($cb);
245 }
246 $cv->end;
247 $cv->recv;
248
249 if (ref $EXIT_STATUS) {
250 # signal
251 # reset to default, hopefully this overrides any C-level handlers
252 $SIG{$EXIT_STATUS->[0]} = 'DEFAULT';
253
254 eval {
255 # try to unblock
256 require POSIX;
257
258 my $set = POSIX::SigSet->new;
259 $set->addset ($EXIT_STATUS->[1]);
260 POSIX::sigprocmask (POSIX::SIG_UNBLOCK (), $set);
261 };
262
263 # now raise the signal
264 kill $EXIT_STATUS->[1], $$;
265
266 # well, if we can't force it even now, try exit 255
267 $? = 255;
268 } else {
269 # exit status
270 $? = $EXIT_STATUS;
271 }
272
273 }
274
275 sub on_exit(&) {
276 unless ($AnyEvent::Watchdog::end) {
277 $AnyEvent::Watchdog::end = \&_exit;
278
279 push @ON_EXIT, $_[0];
280
281 for my $signal (qw(TERM INT XFSZ XCPU)) {
282 my $signum = AnyEvent::Base::sig2num $signal
283 or next;
284 $SIG_W{$signum} = AE::signal $signal => sub {
285 $EXIT_STATUS = [$signal => $signum];
286 exit 124;
287 };
288 }
289 }
290 }
291
292 =back
293
294 =head1 SEE ALSO
295
296 L<AnyEvent>.
297
298 =head1 AUTHOR
299
300 Marc Lehmann <schmorp@schmorp.de>
301 http://home.schmorp.de/
302
303 =cut
304
305 1
306