File Coverage

blib/lib/Lock/Server.pm
Criterion Covered Total %
statement 275 297 92.5
branch 50 66 75.7
condition 37 64 57.8
subroutine 37 40 92.5
pod 6 6 100.0
total 405 473 85.6


line stmt bran cond sub pod time code
1             package Lock::Server;
2              
3             =head1 NAME
4              
5             Lock::Server - Light-weight RESTful socket based resource locking manager.
6              
7             =head1 DESCRIPTION
8              
9             This creates a socket server that handles lock and
10             unlock requests. The lock requests only return once a lock is
11             obtained or a timeout has occurred. A lock may only be locked
12             for a specific amount of time before the lock is timed out.
13              
14             The protocol used is RESTFUL HTTP though the helper class wraps
15             that. It uses the GET verb with the following paths :
16              
17             * CHECK/key - returns 1 if the key in question is currently locked
18             returns 0 if not
19              
20             * LOCK/key/requester - returns lock expire time or 0
21             if there was an error
22              
23             * UNLOCK/key/requester - returns 1 if the unlock went as expected,
24             0 otherwise
25              
26             * VERIFY/key/requester - returns 1 if the key is locked to the
27             requester and did not time out and 0
28             otherwise.
29             * PING - returns 1 if the server is active
30              
31             * SHUTDOWN - stops this LockServer
32              
33             This does not do deadlock detection, relying on the timeouts to
34             prevent the system from getting in a hopelessly tangled state.
35             Care should be taken, as with any resource locking system, with
36             the use of Lock::Server. Adjust the timeouts for what makes sense
37             with the system you are designing. The lock requests return with the
38             time that the lock will expire.
39              
40             =head1 SYNPOSIS
41              
42             use Lock::Server;
43             use Lock::Server::Client;
44              
45             my $lockServer = new Lock::Server( {
46             lock_timeout => 10, #microsecondsseconds. default is 3000
47             lock_attempt_timeout => 12, #microseconds. default is 4000
48             port => 888, #default is 8004
49             host => 'localhost', #default 127.0.0.1
50             } );
51              
52             if( my $childPid = $lockServer->start ) {
53             print "Lock server started in child thread $childPid\n";
54             }
55              
56             my $optional_args = { reconnect_attempts => 3, time_between_attempts => 1 };
57             my $lockClient_A = $lockServer->client( "CLIENT_A", $optional_args );
58             my $lockClient_B =
59             new Lock::Server::Client( "CLIENT_B", 'localhost', 888, $optional_args );
60              
61             if( $lockClient_A->lock( "KEYA" ) ) {
62             print "Lock Successfull for locker A and KEYA\n";
63             } else {
64             print "Could not obtain lock in 12 seconds.\n";
65             }
66              
67             # KEYA for LockerI times out after 10 seconds.
68             # Lock Client B waits until it can obtain the lock
69             if( $lockClient_B->lock( "KEYA" ) ) {
70             print "Lock Successfull for Client B lock 'KEYA'\n";
71             } else {
72             print "Could not obtain lock in 12 seconds.\n";
73             }
74              
75             # KEYA for LockerII is now freed. The next locker
76             # attempting to lock KEYA will then obtain the lock.
77             if( $lockClientB->unlock( "KEYA" ) ) {
78             print "Unlock Successfull\n";
79             }
80              
81             if( $lockServer->stop ) {
82             print "Lock server shut down.\n";
83             }
84              
85             =head1 METHODS
86              
87             =cut
88              
89 9     9   27945 use strict;
  9         9  
  9         306  
90 9     9   54 use warnings;
  9         9  
  9         315  
91 9     9   54 no warnings 'uninitialized';
  9         45  
  9         432  
92              
93 9     9   7065 use Data::Dumper;
  9         89163  
  9         639  
94              
95 9     9   5688 use IO::Select;
  9         18423  
  9         540  
96 9     9   6111 use IO::Socket;
  9         230976  
  9         45  
97              
98 9     9   5292 use IO::Socket::INET;
  9         18  
  9         54  
99 9     9   16128 use POSIX ":sys_wait_h";
  9         45387  
  9         36  
100 9     9   7875 use Time::HiRes qw(ualarm usleep);
  9         9  
  9         63  
101              
102 9     9   963 use vars qw($VERSION);
  9         9  
  9         15138  
103              
104             $VERSION = '1.75';
105              
106              
107             $Lock::Server::DEBUG = 0;
108              
109             =head2 Lock::Server::new( $args )
110              
111             Creates a new lock server for the given optional arguments.
112            
113             Arguments are :
114             * port - port to serve on. Defaults to 8004
115             * lock_timeout - low long should a lock last in seconds
116             * lock_attempt_timeout - how long should a requester
117             wait for a lock in seconds
118             * allow_shutdown - allows a client to shut the server down
119             * reconnect_attempts - if port is busy when starting the server
120             how many retries to connect before giving up and failing startup
121             * time_between_attempts - interval between reconnection attempts
122              
123             =cut
124             sub new {
125 11     11 1 807 my( $pkg, $args ) = @_;
126 11   33     74 my $class = ref( $pkg ) || $pkg;
127             bless {
128             lock_timeout => $args->{lock_timeout} || 3,
129             lock_attempt_timeout => $args->{lock_attempt_timeout} || 4,
130             host => $args->{host} || '127.0.0.1',
131             port => $args->{port} || 8004,
132             allow_shutdown => $args->{allow_shutdown},
133             max_connections => $args->{max_connections} || 10,
134             _pids => {},
135             _id2pid => {},
136             _locks => {},
137             _locker_counts => {},
138             attempts => $args->{reconnect_attemps} || 10,
139 11   50     278 time_between_attempts => $args->{time_between_attempts} || 5, #seconds
      50        
      50        
      50        
      50        
      50        
      50        
140              
141             }, $class;
142             } #new
143              
144              
145             =head2 client( lockername )
146              
147             Returns a client with the given name that can send lock and unlock requests for keys.
148              
149             =cut
150             sub client {
151 21     21 1 6001426 my( $self, $name, $args ) = @_;
152 21         615 Lock::Server::Client->new( $name, $self->{host}, $self->{port}, $args );
153             }
154              
155             =head2 ping
156              
157             Returns '1' if this lock server is up and running
158              
159             =cut
160             sub ping {
161 8     8 1 30 return shift->client("PING")->ping;
162             }
163              
164             =head2 stop
165              
166             Kills the lock server, breaking off any connections that are waiting for a lock.
167              
168             =cut
169             sub stop {
170 3     3 1 8 my $self = shift;
171              
172 3         34 _log( " with '$self->{listener_socket}' socket" );
173 3 50       9 if( $self->{listener_socket} ) {
174 3         14 $self->{listener_socket}->close;
175             }
176              
177 3 100       63 if( my $pid = $self->{server_pid} ) {
178 2         22 $self->{error} = "Sending INT signal to lock server of pid '$pid'";
179 2         10 _log( " Killing lock server proc $pid" );
180 2         5026 kill 'INT', $pid;
181              
182 2         18 my $res = waitpid( $pid, WNOHANG );
183              
184 2         12 _log( " STOP DONE" );
185             } else {
186 1         16 $self->{error} = "No lock server running";
187 1         4 return 0;
188             }
189              
190 2         12 return 1;
191             }
192              
193             =head2 start
194              
195             Starts the lock server in a child process, opening up a
196             tcpip socket and returning the child pid or 0 if there
197             was an error.
198              
199             =cut
200             sub start {
201 11     11 1 631 my $self = shift;
202 11         35 my $sock = $self->_create_listener_socket;
203 11         24 $self->{listener_socket} = $sock;
204 11 50       33 die "Unable to open lockserver socket $@,$! " unless $sock;
205              
206 11 100       8692 if( my $pid = fork ) {
207             # parent
208 7         92 $self->{server_pid} = $pid;
209 7         233 return $pid;
210             }
211              
212             # child process
213 4         325 $0 = "LockServer";
214 4         143 $self->_run_loop( $sock );
215 1         239 exit;
216             } #start
217              
218             =head2 run
219              
220             Runs the lock server.
221              
222             =cut
223             sub run {
224 0     0 1 0 my $self = shift;
225 0         0 my $sock = $self->_create_listener_socket;
226 0         0 $self->{listener_socket} = $sock;
227 0 0       0 die "Unable to open lockserver socket $@,$! " unless $sock;
228 0         0 $self->_run_loop( $sock );
229 0         0 exit;
230             } #run
231              
232             sub _create_listener_socket {
233 11     11   20 my $self = shift;
234              
235 11         13 my( $listener_socket, $count );
236              
237 11         35 my $st = Time::HiRes::time;
238            
239 11   33     84 until( $listener_socket || $count++ > $self->{attempts} ) {
240             $listener_socket = new IO::Socket::INET(
241             Listen => 1,
242             LocalPort => $self->{port},
243             # LocalAddr => "$self->{host}:$self->{port}",
244             # Proto => 'tcp',
245             # ReuseAddr => 1,
246             # ReusePort => 1,
247 17         260 );
248 17 100       5246 last if $listener_socket;
249 6         660 print STDERR "Unable to open the lock server socket $@, $!. Retry $count of 10\n";
250 6 50 33     60001160 sleep $count*$self->{time_between_attempts} unless $listener_socket || $count > $self->{attempts};
251             }
252 11 50       33 unless( $listener_socket ) {
253              
254 0         0 $self->{error} = "Unable to open socket on port '$self->{port}' : $! $@\n";
255 0         0 _log( "unable to start lock server : $@ $!." );
256 0         0 return 0;
257             }
258              
259             # if this is cancelled, make sure all child procs are killed too
260             $SIG{TERM} = $SIG{INT} = sub {
261 1     1   600 _log( "lock server : got INT signal. Shutting down." );
262 1 50       12 $listener_socket && $listener_socket->close;
263              
264 1         27 kill 'INT', keys %{ $self->{_pids} };
  1         19  
265              
266 1         30 while( (my $kidpid = waitpid( -1, WNOHANG ) ) > 0 ) {
267 2         17 _log( " Killed $kidpid" );
268             }
269 1         2 $self->{_pids} = {};
270 1         5 _log( "lock server : got INT signal. EXITING." );
271 1         166 exit;
272 11         218 };
273 11         100 return $listener_socket;
274             } #_create_listener_socket
275              
276             sub _run_loop {
277 4     4   21 my( $self, $listener_socket ) = @_;
278              
279 4         228 my $sel = IO::Select->new( $listener_socket );
280 4         834 my @ready;
281 4         51 while(@ready = $sel->can_read) {
282 209         14616451 for my $connection (@ready) {
283 211 100       676 if( $connection == $listener_socket ) {
284 106         629 $sel->add($listener_socket->accept );
285             } else {
286 105         1190 my $req = <$connection>;
287 105         781 $req =~ s/\s+$//s;
288 105         395 _log( "lock server : incoming request : '$req'" );
289             # could have headers, but ignore those. Find \n\n
290 105         314 while( my $data = <$connection> ) {
291 105         130 chomp $data;
292 105 50       291 last unless $data =~ /\S/;
293             }
294              
295 105         453 my( $cmd, $key, $locker_id ) = split( '/', substr( $req, 5 ) );
296 105 100       386 if( $cmd eq 'CHECK' ) {
    100          
    100          
    100          
    100          
    50          
297 20         83 $self->_check( $connection, $key );
298             } elsif( $cmd eq 'LOCK' ) {
299 22         89 $self->_lock( $connection, $key, $locker_id );
300             } elsif( $cmd eq 'UNLOCK' ) {
301 17         119 $self->_unlock( $connection, $key, $locker_id );
302             } elsif( $cmd eq 'VERIFY' ) {
303 35         153 $self->_verify( $connection, $key, $locker_id );
304             } elsif( $cmd eq 'PING' ) {
305 9         221 print $connection "1\n";
306             } elsif( $cmd eq 'SHUTDOWN') {
307 2 100       9 if( $self->{allow_shutdown}) {
308 1         36 print $connection "1\n";
309 1         8 $connection->close;
310 1         23 $self->stop;
311             } else {
312 1         4 _log( "lock server : got shutdown request but not configured to allow it" );
313             }
314             } else {
315 0         0 _log( "lock server : did not understand command '$cmd'" );
316             }
317 103         451 $sel->remove($connection);
318 103         3034 $connection->close;
319             }
320             } #ready loop
321             } #can_read loop
322             } #_run_loop
323              
324             sub _check {
325 20     20   33 my( $self, $connection, $key_to_check ) = @_;
326 20         52 _log( "locker server check for key '$key_to_check'" );
327              
328 20   100     109 $self->{_locks}{$key_to_check} ||= [];
329 20         26 my $lockers = $self->{_locks}{$key_to_check};
330              
331            
332             #check for timed out lockers
333 20         87 my $t = Time::HiRes::time;
334 20   100     169 while( @$lockers && $t > $self->{_locker_counts}{$lockers->[0]}{$key_to_check} ) {
335 2         11 _log( "lock server _check : '$key_to_check' timed out for locker '$lockers->[0]'" );
336 2 50       1 if( 1 == keys %{ $self->{_locker_counts}{$lockers->[0]} } ) {
  2         10  
337 2         10 delete $self->{_locker_counts}{$lockers->[0]};
338             } else {
339 0         0 delete $self->{_locker_counts}{$lockers->[0]}{$key_to_check};
340             }
341 2         13 shift @$lockers;
342             }
343              
344              
345 20 100       54 if( @$lockers ) {
346 12         300 print $connection "1\n";
347             } else {
348 8         209 print $connection "0\n";
349             }
350             }
351              
352             sub _log {
353 339     339   362 my $msg = shift;
354 339         702 $msg = "($$) $msg";
355 339 50       679 print STDERR "Lock::Server : $msg\n" if $Lock::Server::DEBUG;
356             }
357              
358             sub _lock {
359 22     22   73 my( $self, $connection, $key_to_lock, $locker_id ) = @_;
360 22         65 _log( "lock request : for '$locker_id' and key '$key_to_lock'" );
361              
362 22   100     108 $self->{_locks}{$key_to_lock} ||= [];
363 22         34 my $lockers = $self->{_locks}{$key_to_lock};
364             #check for timed out lockers
365 22         58 my $t = Time::HiRes::time;
366              
367 22   66     135 while( @$lockers && $t > $self->{_locker_counts}{$lockers->[0]}{$key_to_lock} ) {
368 0         0 _log( "lock '$key_to_lock' timed out for locker '$lockers->[0]'" );
369 0 0       0 if( 1 == keys %{ $self->{_locker_counts}{$lockers->[0]} } ) {
  0         0  
370 0         0 delete $self->{_locker_counts}{$lockers->[0]};
371             } else {
372 0         0 delete $self->{_locker_counts}{$lockers->[0]}{$key_to_lock};
373             }
374 0         0 shift @$lockers;
375             }
376              
377 22 100       54 if( 0 < (grep { $_ eq $locker_id } @$lockers) ) {
  8         32  
378 3         9 _log( "lock request error. '$locker_id' already in the lock queue" );
379 3         63 print $connection "0\n";
380 3         12 return;
381             }
382              
383             # store when this times out
384 19         83 my $timeout_time = Time::HiRes::time + $self->{lock_timeout};
385 19         57 $self->{_locker_counts}{$locker_id}{$key_to_lock} = $timeout_time;
386 19         28 push @$lockers, $locker_id;
387              
388 19         71 _log( "lock request : there are now ".scalar(@$lockers)." lockers" );
389 19 100       56 if( @$lockers > 1 ) {
390 5 100       4395 if( (my $pid=fork)) {
391 3         50 $self->{_id2pid}{$locker_id} = $pid;
392 3         36 $self->{_pids}{$pid} = 1;
393 3         152 _log( "lock request : parent process associating '$locker_id' with pid '$pid' ".scalar(@$lockers)." lockers" );
394             # parent
395             } else {
396             # use Devel::SimpleProfiler;Devel::SimpleProfiler::start;
397 2         123 $0 = "LockServer processing request";
398             $SIG{INT} = sub {
399 0     0   0 _log( "lock request : child got INT, exiting." );
400 0         0 $connection->close;
401 0         0 exit;
402 2         135 };
403             $SIG{HUP} = sub {
404 1     1   22 _log( "lock request : child got HUP, so is now locked." );
405 1         154 $connection->print( "$timeout_time\n" );
406 1         1543 $connection->close;
407 1         424 exit;
408 2         71 };
409 2         42 _log( "lock request : child ready to wait" );
410 2         7109081 usleep 1_000_000 * $self->{lock_attempt_timeout};
411 2         130 _log( "lock request failed : child timed out" );
412 1         131 print $connection "0\n";
413 1         37 $connection->close;
414 1         1588 exit;
415             }
416             } else {
417 14         31 _log( "lock request : no need to invoke more processes. locking" );
418 14         522 print $connection "$timeout_time\n";
419             }
420             } #_lock
421              
422             sub _unlock {
423 17     17   34 my( $self, $connection, $key_to_unlock, $locker_id ) = @_;
424 17         56 _log( "unlock request : key '$key_to_unlock' for locker '$locker_id'" );
425              
426 17   50     63 $self->{_locks}{$key_to_unlock} ||= [];
427 17         24 my $lockers = $self->{_locks}{$key_to_unlock};
428              
429 17 100       39 if( $lockers->[0] eq $locker_id ) {
430 11         23 shift @$lockers;
431 11         38 delete $self->{_locker_counts}{$locker_id}{$key_to_unlock};
432 11 100       14 if( 0 == scalar(keys %{$self->{_locker_counts}{$locker_id}}) ) {
  11         81  
433 8         22 _log( "unlock : remove information about '$locker_id'" );
434 8         16 delete $self->{_id2pid}{$locker_id};
435 8         26 delete $self->{_locker_counts}{$locker_id};
436             }
437 11         41 _log( "unlocking '$locker_id'" );
438 11 100       32 if( @$lockers ) {
439 2         12 my $next_locker_id = $lockers->[0];
440 2         8 my $pid = $self->{_id2pid}{$next_locker_id};
441 2         22 _log( "unlock : next locker in queue is '$next_locker_id'. Sending kill signal to its pid '$pid'" );
442 2         88 kill 'HUP', $pid;
443             } else {
444 9         25 _log( "unlock : now no one waiting on a lock for key '$key_to_unlock'" );
445             }
446 11         17 _log( "unlock : done, informing connection" );
447 11         356 print $connection "1\n";
448             } else {
449 6         21 _log( "unlock error : Wrong locker_id to unlock for unlock for locker '$locker_id' and key '$key_to_unlock'. The locker_id must be the one at the front of the queue" );
450             # "Wrong locker_id to unlock. The locker_id must be the one at the front of the queue";
451 6         249 print $connection "0\n";
452             }
453             } #_unlock
454              
455             sub _verify {
456 35     35   78 my( $self, $connection, $key_to_check, $locker_id ) = @_;
457              
458 35         86 _log( "verify : locker server check for key '$key_to_check' for locker '$locker_id'" );
459              
460 35   50     90 $self->{_locks}{$key_to_check} ||= [];
461 35         81 my $lockers = $self->{_locks}{$key_to_check};
462              
463             #check for timed out lockers
464 35         71 my $t = Time::HiRes::time;
465 35         533 _log( "verify: compare '$t' > '$self->{_locker_counts}{$lockers->[0]}{$key_to_check}' if the first is greater, there is a time out" );
466 35   100     212 while( @$lockers && $t > $self->{_locker_counts}{$lockers->[0]}{$key_to_check} ) {
467 1         9 _log( "verify: '$key_to_check' timed out for locker '$lockers->[0]'" );
468 1 50       3 if( 1 == keys %{ $self->{_locker_counts}{$lockers->[0]} } ) {
  1         8  
469 0         0 delete $self->{_locker_counts}{$lockers->[0]};
470             } else {
471 1         2 delete $self->{_locker_counts}{$lockers->[0]}{$key_to_check};
472             }
473 1         4 shift @$lockers;
474             }
475              
476 35 100       67 if( $lockers->[0] eq $locker_id ) {
477 17         438 print $connection "1\n";
478             } else {
479 18         492 print $connection "0\n";
480             }
481             }
482              
483              
484              
485             =head1 Helper package
486              
487             =head2 NAME
488              
489             Lock::Server::Client - client for locking server.
490              
491             =head2 DESCRIPTION
492              
493             Sends request to a Lock::Server to lock, unlock and check locks.
494              
495             =head2 METHODS
496              
497             =cut
498             package Lock::Server::Client;
499              
500 9     9   45 use strict;
  9         9  
  9         180  
501 9     9   27 use warnings;
  9         9  
  9         189  
502 9     9   27 no warnings 'uninitialized';
  9         0  
  9         288  
503              
504 9     9   99 use IO::Socket::INET;
  9         9  
  9         36  
505              
506             =head3 new( lockername, host, port )
507              
508             Creates a client object with the given name for the host and port.
509              
510             =cut
511             sub new {
512 22     22   167 my( $pkg, $lockerName, $host, $port, $args ) = @_;
513 22 50       131 die "Must supply locker name" unless $lockerName;
514              
515 22   50     69 $host ||= '127.0.0.1';
516 22   50     88 $port ||= '8004';
517              
518 22   33     340 my $class = ref( $pkg ) || $pkg;
519             bless {
520             host => $host,
521             port => $port,
522             name => $lockerName,
523             attempts => $args->{reconnect_attemps} || 3,
524 22   50     1336 time_between_attempts => $args->{time_between_attempts} || 3,
      50        
525             }, $class;
526             } #new
527              
528             sub _get_sock {
529 162     162   195 my $self = shift;
530 162   66     528 my $attempts = shift || $self->{attempts};
531              
532             # try a few times, then give up
533 162         128 my( $sock, $count );
534 162   66     872 until( $sock || $count++ > $attempts ) {
535 162         2061 $sock = new IO::Socket::INET( "$self->{host}:$self->{port}" );
536 162 100 66     12083619 sleep $self->{time_between_attempts}*($count) unless $sock || $count > $attempts;
537             }
538 158 50       282 die "Could not connect : $@" unless $sock;
539 158         820 binmode $sock, ':utf8';
540 158         236 $sock;
541             }
542              
543             =head3 isLocked( key )
544              
545             Returns true if the key is locked by anyone.
546              
547             =cut
548             sub isLocked {
549 28     28   5554494 my( $self, $key ) = @_;
550 28         57 my $sock = $self->_get_sock;
551              
552 28         150 $sock->print( "GET /CHECK/$key\n\n" );
553 28         7389 my $resp = <$sock>;
554 28         145 $sock->close;
555 28         1290 chomp $resp;
556 28         225 $resp;
557             }
558              
559             =head3 lockedByMe( key )
560              
561             Returns true if the key is locked by this client or
562             anyone with the name of this client. The name was given in the constructor.
563              
564             =cut
565             sub lockedByMe {
566 58     58   136 my( $self, $key ) = @_;
567 58         107 my $sock = $self->_get_sock;
568              
569 58         265 $sock->print( "GET /VERIFY/$key/$self->{name}\n\n" );
570 58         15324 my $resp = <$sock>;
571 58         213 $sock->close;
572 58         2375 chomp $resp;
573 58         437 $resp;
574             }
575              
576             =head3 lock( key )
577              
578             Attempt to get the lock for the given key. Returns true if the lock
579             was obtained.
580              
581             =cut
582             sub lock {
583 24     24   97 my( $self, $key ) = @_;
584 24         65 my $sock = $self->_get_sock;
585              
586 24         198 $sock->print( "GET /LOCK/$key/$self->{name}\n\n" );
587 24         7118513 my $resp = <$sock>;
588 24         159 $sock->close;
589 24         1018 chomp $resp;
590 24         193 $resp;
591             }
592              
593             =head3 unlock( key )
594              
595             Attempt to get unlock the given key. Returns true if the
596             key was locked to this client ( or someting with the same name ).
597              
598             =cut
599             sub unlock {
600 27     27   2010416 my( $self, $key ) = @_;
601 27         80 my $sock = $self->_get_sock;
602 27         166 $sock->print( "GET /UNLOCK/$key/$self->{name}\n\n" );
603 27         11115 my $resp = <$sock>;
604 27         140 $sock->close;
605 27         1190 chomp $resp;
606 27         226 $resp;
607             }
608              
609             sub ping {
610 22     22   1000312 my( $self, $timeout ) = @_;
611              
612 22   50     209 $timeout //= 3;
613              
614            
615 22     5   689 local $SIG{ALRM} = sub { die "ALARM\n" };
  5         233  
616 22         149 alarm $timeout;
617 22         62 my $resp = '0';
618 22         55 eval {
619 22         66 my $sock = $self->_get_sock( 1 );
620 18         243 $sock->print( "GET /PING\n\n" );
621 18         3006091 $resp = <$sock>;
622 17         100 alarm 0;
623 17         126 $sock->close;
624             };
625 22         4852 chomp $resp;
626 22         396 $resp;
627             } #ping
628              
629             sub shutdown {
630 3     3   9 my( $self, $timeout ) = @_;
631              
632 3   50     30 $timeout //= 3;
633              
634 3     0   35 local $SIG{ALRM} = sub { die "ALARM\n" };
  0         0  
635 3         8 alarm $timeout;
636 3         5 eval {
637 3         8 my $sock = $self->_get_sock( 1 );
638 3         16 $sock->print( "GET /SHUTDOWN\n\n" );
639 3         104 alarm 0;
640 3         11 $sock->close;
641             };
642 3         100 $@;
643             } #shutdown
644              
645              
646             1;
647              
648              
649             __END__