ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-Watchdog/Watchdog/Util.pm
Revision: 1.2
Committed: Tue Sep 1 14:53:19 2009 UTC (15 years, 1 month ago) by root
Branch: MAIN
Changes since 1.1: +0 -5 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.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 [$timeout]
61    
62     Tells the supervisor to restart the process when it exits, or forcefully
63     after C<$timeout> seconds (minimum 1, maximum 255, default 60).
64    
65     Calls C<exit 0> to exit the process cleanly.
66    
67     =cut
68    
69     sub restart(;$) {
70     my ($timeout) = @_;
71    
72     $timeout = 60 unless defined $timeout;
73     $timeout = 1 if $timeout < 1;
74     $timeout = 255 if $timeout > 255;
75    
76     syswrite $C, "\x01\x02" . chr $timeout;
77     exit 0;
78     }
79    
80     =item AnyEvent::Watchdog::Util::autorestart [$boolean]
81    
82     =item use AnyEvent::Watchdog autorestart => $boolean
83    
84     Enables or disables autorestart (initially disabled, default for
85     C<$boolean> is to enable): By default, the supervisor will exit if the
86     program exits or dies in any way. When enabling autorestart behaviour,
87     then the supervisor will try to restart the program after it dies.
88    
89     Note that the supervisor will never autorestart when the child died with
90     SIGINT or SIGTERM.
91    
92     =cut
93    
94     sub autorestart(;$) {
95     my $AUTORESTART = !@_ || $_[0];
96    
97     unless (enabled) {
98     warn "AnyEvent::Watchdog: watchdog not running, cannot enable autorestart, ignoring.\n"
99     if $AUTORESTART;
100    
101     $AUTORESTART = 0;
102    
103     return;
104     }
105    
106     syswrite $C, $AUTORESTART ? "\x01" : "\x00";
107     }
108    
109     =item AnyEvent::Watchdog::heartbeat [$interval]
110    
111     =item use AnyEvent::Watchdog heartbeat => $interval
112    
113     Tells the supervisor to automatically kill the program if it doesn't
114     react for C<$interval> seconds (minium 1, maximum 255, default 60) , then
115     installs an AnyEvent timer the sends a regular heartbeat to the supervisor
116     twice as often.
117    
118     Exit behaviour isn't changed, so if you want a restart instead of an exit,
119     you have to call C<autorestart>.
120    
121     The heartbeat frequency can be changed as often as you want, an interval
122     of C<0> disables the heartbeat check again.
123    
124     =cut
125    
126     sub heartbeat(;$) {
127     my ($interval) = @_;
128    
129     unless (enabled) {
130     warn "AnyEvent::Watchdog: watchdog not running, cannot enable heartbeat, ignoring.\n";
131     return;
132     }
133    
134     $interval = 60 unless defined $interval;
135     $interval = 1 if $interval < 1;
136     $interval = 255 if $interval > 255;
137    
138     syswrite $C, "\x03" . chr $interval;
139    
140     $HEARTBEAT_W = AE::timer (0, $interval * 0.5, sub {
141     syswrite $C, "\x04";
142     });
143     }
144    
145     =item AnyEvent::Watchdog::on_exit { BLOCK; shift->() }
146    
147     Installs an exit hook that is executed when the program is about to exit,
148     while event processing is still active to some extent.
149    
150     The hook should do whatever it needs to do (close active connections,
151     disable listeners, write state, free resources etc.). When it is done, it
152     should call the code reference that has been passed to it.
153    
154     This means you can install event handlers and return from the block, and
155     the program will not exit until the callback is invoked.
156    
157     Exiting "the right way" is surprisingly difficult. This is what C<on_exit>
158     does:
159    
160     It installs watchers for C<SIGTERM>, C<SIGINT>, C<SIGXCPU> and C<SIGXFSZ>,
161     and well as an C<END> block (the END block is actually registered
162     in L<AnyEvent::Watchdog>, if possible, so it executes as late as
163     possible). The signal handlers remember the signal and then call C<exit>,
164     invoking the C<END> callback.
165    
166     The END block then checks for an exit code of C<255>, in which case
167     nothing happens (C<255> is the exit code that results from a program
168     error), otherwise it runs all C<on_exit> hooks and waits for their
169     completion using the event loop.
170    
171     After all C<on_exit> hooks have finished, the program will either be
172     C<exit>ed with the relevant status code (if C<exit> was the cause for the
173     program exit), or it will reset the signal handler, unblock the signal and
174     kill itself with the signal, to ensure that the exit status is correct.
175    
176     If the program is running under the watchdog, and autorestart is enabled,
177     then the heartbeat is disabled and the watchdog is told that the program
178     wishes to exit within C<60> seconds, after which it will be forcefully
179     killed.
180    
181     All of this should ensure that C<on_exit> hooks are only executed when the
182     program is in a sane state and data structures are still intact. This only
183     works when the program does not install it's own TERM (etc.) watchers, of
184     course, as there is no control over them.
185    
186     There is currently no way to unregister C<on_exit> hooks.
187    
188     =cut
189    
190     our @ON_EXIT;
191     our %SIG_W;
192     our $EXIT_STATUS; # >= 0 exit status; arrayref => signal, undef if exit was just called
193    
194     # in case AnyEvent::Watchdog is not loaded, use our own END block
195     END { $AnyEvent::Watchdog::end && &$AnyEvent::Watchdog::end }
196    
197     sub _exit {
198     $EXIT_STATUS = $? unless defined $EXIT_STATUS;
199    
200     undef $AnyEvent::Watchdog::end;
201    
202     my $cv = AE::cv;
203     my $cb = sub { $cv->end };
204    
205     $cv->begin;
206     while (@ON_EXIT) {
207     $cv->begin;
208     (pop @ON_EXIT)->($cb);
209     }
210     $cv->end;
211     $cv->recv;
212    
213     if (ref $EXIT_STATUS) {
214     # signal
215     # reset to default, hopefully this overrides any C-level handlers
216     $SIG{$EXIT_STATUS->[0]} = 'DEFAULT';
217     eval {
218     # try to unblock
219     require POSIX;
220    
221     my $set = POSIX::SigSet->new;
222     $set->addset ($EXIT_STATUS->[1]);
223     POSIX::sigprocmask (POSIX::SIG_UNBLOCK (), $set);
224     };
225     # now raise the signal
226     kill $EXIT_STATUS->[1], $$;
227    
228     # well, if we can't force it even now, try exit 255
229     $? = 255;
230     } else {
231     # exit status
232     $? = $EXIT_STATUS;
233     }
234    
235     }
236    
237     sub on_exit(&) {
238     unless ($AnyEvent::Watchdog::end) {
239     $AnyEvent::Watchdog::end = \&_exit;
240    
241     push @ON_EXIT, $_[0];
242    
243     for my $signal (qw(TERM INT XFSZ XCPU)) {
244     my $signum = AnyEvent::Base::sig2num $signal
245     or next;
246     $SIG_W{$signum} = AE::signal $signal => sub {
247     $EXIT_STATUS = [$signal => $signum];
248     exit 124;
249     };
250     }
251     }
252     }
253    
254     =back
255    
256     =head1 SEE ALSO
257    
258     L<AnyEvent>.
259    
260     =head1 AUTHOR
261    
262     Marc Lehmann <schmorp@schmorp.de>
263     http://home.schmorp.de/
264    
265     =cut
266    
267     1
268