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   17802 use strict;
  9         9  
  9         207  
90 9     9   27 use warnings;
  9         9  
  9         171  
91 9     9   27 no warnings 'uninitialized';
  9         27  
  9         234  
92              
93 9     9   4491 use Data::Dumper;
  9         56691  
  9         387  
94              
95 9     9   3348 use IO::Select;
  9         9666  
  9         324  
96 9     9   4041 use IO::Socket;
  9         141687  
  9         36  
97              
98 9     9   3123 use IO::Socket::INET;
  9         9  
  9         36  
99 9     9   9396 use POSIX ":sys_wait_h";
  9         37683  
  9         63  
100 9     9   7515 use Time::HiRes qw(ualarm usleep);
  9         9  
  9         54  
101              
102 9     9   972 use vars qw($VERSION);
  9         9  
  9         15732  
103              
104             $VERSION = '1.73';
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 148 my( $pkg, $args ) = @_;
126 11   33     70 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     329 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 6001222 my( $self, $name, $args ) = @_;
152 21         468 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 27 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 10 my $self = shift;
171              
172 3         21 _log( " with '$self->{listener_socket}' socket" );
173 3 50       11 if( $self->{listener_socket} ) {
174 3         11 $self->{listener_socket}->close;
175             }
176              
177 3 100       45 if( my $pid = $self->{server_pid} ) {
178 2         18 $self->{error} = "Sending INT signal to lock server of pid '$pid'";
179 2         6 _log( " Killing lock server proc $pid" );
180 2         48 kill 'INT', $pid;
181              
182 2         12 my $res = waitpid( $pid, WNOHANG );
183              
184 2         4 _log( " STOP DONE" );
185             } else {
186 1         12 $self->{error} = "No lock server running";
187 1         4 return 0;
188             }
189              
190 2         4 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 353 my $self = shift;
202 11         48 my $sock = $self->_create_listener_socket;
203 11         31 $self->{listener_socket} = $sock;
204 11 50       31 die "Unable to open lockserver socket $@,$! " unless $sock;
205              
206 11 100       7045 if( my $pid = fork ) {
207             # parent
208 7         118 $self->{server_pid} = $pid;
209 7         319 return $pid;
210             }
211              
212             # child process
213 4         331 $0 = "LockServer";
214 4         160 $self->_run_loop( $sock );
215 1         158 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     99 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         275 );
248 17 100       5215 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     60001112 sleep $count*$self->{time_between_attempts} unless $listener_socket || $count > $self->{attempts};
251             }
252 11 50       31 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   734 _log( "lock server : got INT signal. Shutting down." );
262 1 50       5 $listener_socket && $listener_socket->close;
263              
264 1         25 kill 'INT', keys %{ $self->{_pids} };
  1         17  
265              
266 1         26 while( (my $kidpid = waitpid( -1, WNOHANG ) ) > 0 ) {
267 2         21 _log( " Killed $kidpid" );
268             }
269 1         3 $self->{_pids} = {};
270 1         4 _log( "lock server : got INT signal. EXITING." );
271 1         98 exit;
272 11         211 };
273 11         78 return $listener_socket;
274             } #_create_listener_socket
275              
276             sub _run_loop {
277 4     4   34 my( $self, $listener_socket ) = @_;
278              
279 4         301 my $sel = IO::Select->new( $listener_socket );
280 4         726 my @ready;
281 4         88 while(@ready = $sel->can_read) {
282 209         14543427 for my $connection (@ready) {
283 211 100       685 if( $connection == $listener_socket ) {
284 106         559 $sel->add($listener_socket->accept );
285             } else {
286 105         908 my $req = <$connection>;
287 105         665 $req =~ s/\s+$//s;
288 105         312 _log( "lock server : incoming request : '$req'" );
289             # could have headers, but ignore those. Find \n\n
290 105         256 while( my $data = <$connection> ) {
291 105         126 chomp $data;
292 105 50       222 last unless $data =~ /\S/;
293             }
294              
295 105         360 my( $cmd, $key, $locker_id ) = split( '/', substr( $req, 5 ) );
296 105 100       329 if( $cmd eq 'CHECK' ) {
    100          
    100          
    100          
    100          
    50          
297 20         46 $self->_check( $connection, $key );
298             } elsif( $cmd eq 'LOCK' ) {
299 22         53 $self->_lock( $connection, $key, $locker_id );
300             } elsif( $cmd eq 'UNLOCK' ) {
301 17         54 $self->_unlock( $connection, $key, $locker_id );
302             } elsif( $cmd eq 'VERIFY' ) {
303 35         114 $self->_verify( $connection, $key, $locker_id );
304             } elsif( $cmd eq 'PING' ) {
305 9         191 print $connection "1\n";
306             } elsif( $cmd eq 'SHUTDOWN') {
307 2 100       12 if( $self->{allow_shutdown}) {
308 1         34 print $connection "1\n";
309 1         6 $connection->close;
310 1         22 $self->stop;
311             } else {
312 1         5 _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         337 $sel->remove($connection);
318 103         2861 $connection->close;
319             }
320             } #ready loop
321             } #can_read loop
322             } #_run_loop
323              
324             sub _check {
325 20     20   27 my( $self, $connection, $key_to_check ) = @_;
326 20         46 _log( "locker server check for key '$key_to_check'" );
327              
328 20   100     87 $self->{_locks}{$key_to_check} ||= [];
329 20         23 my $lockers = $self->{_locks}{$key_to_check};
330              
331            
332             #check for timed out lockers
333 20         57 my $t = Time::HiRes::time;
334 20   100     107 while( @$lockers && $t > $self->{_locker_counts}{$lockers->[0]}{$key_to_check} ) {
335 2         10 _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         9  
337 2         7 delete $self->{_locker_counts}{$lockers->[0]};
338             } else {
339 0         0 delete $self->{_locker_counts}{$lockers->[0]}{$key_to_check};
340             }
341 2         10 shift @$lockers;
342             }
343              
344              
345 20 100       46 if( @$lockers ) {
346 12         249 print $connection "1\n";
347             } else {
348 8         189 print $connection "0\n";
349             }
350             }
351              
352             sub _log {
353 339     339   337 my $msg = shift;
354 339         599 $msg = "($$) $msg";
355 339 50       540 print STDERR "Lock::Server : $msg\n" if $Lock::Server::DEBUG;
356             }
357              
358             sub _lock {
359 22     22   29 my( $self, $connection, $key_to_lock, $locker_id ) = @_;
360 22         58 _log( "lock request : for '$locker_id' and key '$key_to_lock'" );
361              
362 22   100     103 $self->{_locks}{$key_to_lock} ||= [];
363 22         25 my $lockers = $self->{_locks}{$key_to_lock};
364             #check for timed out lockers
365 22         50 my $t = Time::HiRes::time;
366              
367 22   66     100 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       45 if( 0 < (grep { $_ eq $locker_id } @$lockers) ) {
  8         27  
378 3         12 _log( "lock request error. '$locker_id' already in the lock queue" );
379 3         57 print $connection "0\n";
380 3         9 return;
381             }
382              
383             # store when this times out
384 19         63 my $timeout_time = Time::HiRes::time + $self->{lock_timeout};
385 19         70 $self->{_locker_counts}{$locker_id}{$key_to_lock} = $timeout_time;
386 19         31 push @$lockers, $locker_id;
387              
388 19         47 _log( "lock request : there are now ".scalar(@$lockers)." lockers" );
389 19 100       33 if( @$lockers > 1 ) {
390 5 100       2391 if( (my $pid=fork)) {
391 3         56 $self->{_id2pid}{$locker_id} = $pid;
392 3         45 $self->{_pids}{$pid} = 1;
393 3         102 _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         99 $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         124 };
403             $SIG{HUP} = sub {
404 1     1   10 _log( "lock request : child got HUP, so is now locked." );
405 1         108 $connection->print( "$timeout_time\n" );
406 1         3546 $connection->close;
407 1         350 exit;
408 2         36 };
409 2         45 _log( "lock request : child ready to wait" );
410 2         7107762 usleep 1_000_000 * $self->{lock_attempt_timeout};
411 2         78 _log( "lock request failed : child timed out" );
412 1         84 print $connection "0\n";
413 1         26 $connection->close;
414 1         1099 exit;
415             }
416             } else {
417 14         20 _log( "lock request : no need to invoke more processes. locking" );
418 14         414 print $connection "$timeout_time\n";
419             }
420             } #_lock
421              
422             sub _unlock {
423 17     17   55 my( $self, $connection, $key_to_unlock, $locker_id ) = @_;
424 17         48 _log( "unlock request : key '$key_to_unlock' for locker '$locker_id'" );
425              
426 17   50     46 $self->{_locks}{$key_to_unlock} ||= [];
427 17         25 my $lockers = $self->{_locks}{$key_to_unlock};
428              
429 17 100       46 if( $lockers->[0] eq $locker_id ) {
430 11         21 shift @$lockers;
431 11         31 delete $self->{_locker_counts}{$locker_id}{$key_to_unlock};
432 11 100       20 if( 0 == scalar(keys %{$self->{_locker_counts}{$locker_id}}) ) {
  11         62  
433 8         24 _log( "unlock : remove information about '$locker_id'" );
434 8         20 delete $self->{_id2pid}{$locker_id};
435 8         35 delete $self->{_locker_counts}{$locker_id};
436             }
437 11         35 _log( "unlocking '$locker_id'" );
438 11 100       25 if( @$lockers ) {
439 2         10 my $next_locker_id = $lockers->[0];
440 2         10 my $pid = $self->{_id2pid}{$next_locker_id};
441 2         20 _log( "unlock : next locker in queue is '$next_locker_id'. Sending kill signal to its pid '$pid'" );
442 2         118 kill 'HUP', $pid;
443             } else {
444 9         18 _log( "unlock : now no one waiting on a lock for key '$key_to_unlock'" );
445             }
446 11         19 _log( "unlock : done, informing connection" );
447 11         257 print $connection "1\n";
448             } else {
449 6         15 _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         114 print $connection "0\n";
452             }
453             } #_unlock
454              
455             sub _verify {
456 35     35   56 my( $self, $connection, $key_to_check, $locker_id ) = @_;
457              
458 35         71 _log( "verify : locker server check for key '$key_to_check' for locker '$locker_id'" );
459              
460 35   50     85 $self->{_locks}{$key_to_check} ||= [];
461 35         30 my $lockers = $self->{_locks}{$key_to_check};
462              
463             #check for timed out lockers
464 35         79 my $t = Time::HiRes::time;
465 35         395 _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     169 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       1 if( 1 == keys %{ $self->{_locker_counts}{$lockers->[0]} } ) {
  1         14  
469 0         0 delete $self->{_locker_counts}{$lockers->[0]};
470             } else {
471 1         5 delete $self->{_locker_counts}{$lockers->[0]}{$key_to_check};
472             }
473 1         6 shift @$lockers;
474             }
475              
476 35 100       54 if( $lockers->[0] eq $locker_id ) {
477 17         394 print $connection "1\n";
478             } else {
479 18         390 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         171  
501 9     9   27 use warnings;
  9         9  
  9         171  
502 9     9   27 no warnings 'uninitialized';
  9         9  
  9         279  
503              
504 9     9   99 use IO::Socket::INET;
  9         9  
  9         27  
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   126 my( $pkg, $lockerName, $host, $port, $args ) = @_;
513 22 50       82 die "Must supply locker name" unless $lockerName;
514              
515 22   50     73 $host ||= '127.0.0.1';
516 22   50     56 $port ||= '8004';
517              
518 22   33     199 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     1019 time_between_attempts => $args->{time_between_attempts} || 3,
      50        
525             }, $class;
526             } #new
527              
528             sub _get_sock {
529 162     162   165 my $self = shift;
530 162   66     430 my $attempts = shift || $self->{attempts};
531              
532             # try a few times, then give up
533 162         108 my( $sock, $count );
534 162   66     728 until( $sock || $count++ > $attempts ) {
535 162         1547 $sock = new IO::Socket::INET( "$self->{host}:$self->{port}" );
536 162 100 66     12068832 sleep $self->{time_between_attempts}*($count) unless $sock || $count > $attempts;
537             }
538 158 50       231 die "Could not connect : $@" unless $sock;
539 158         659 binmode $sock, ':utf8';
540 158         176 $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   5554418 my( $self, $key ) = @_;
550 28         48 my $sock = $self->_get_sock;
551              
552 28         125 $sock->print( "GET /CHECK/$key\n\n" );
553 28         6289 my $resp = <$sock>;
554 28         129 $sock->close;
555 28         958 chomp $resp;
556 28         181 $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   113 my( $self, $key ) = @_;
567 58         76 my $sock = $self->_get_sock;
568              
569 58         244 $sock->print( "GET /VERIFY/$key/$self->{name}\n\n" );
570 58         12752 my $resp = <$sock>;
571 58         159 $sock->close;
572 58         1841 chomp $resp;
573 58         345 $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   91 my( $self, $key ) = @_;
584 24         55 my $sock = $self->_get_sock;
585              
586 24         122 $sock->print( "GET /LOCK/$key/$self->{name}\n\n" );
587 24         7115298 my $resp = <$sock>;
588 24         140 $sock->close;
589 24         849 chomp $resp;
590 24         229 $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   2010353 my( $self, $key ) = @_;
601 27         61 my $sock = $self->_get_sock;
602 27         157 $sock->print( "GET /UNLOCK/$key/$self->{name}\n\n" );
603 27         6219 my $resp = <$sock>;
604 27         96 $sock->close;
605 27         863 chomp $resp;
606 27         150 $resp;
607             }
608              
609             sub ping {
610 22     22   1000285 my( $self, $timeout ) = @_;
611              
612 22   50     153 $timeout //= 3;
613              
614            
615 22     5   436 local $SIG{ALRM} = sub { die "ALARM\n" };
  5         224  
616 22         113 alarm $timeout;
617 22         65 my $resp = '0';
618 22         82 eval {
619 22         72 my $sock = $self->_get_sock( 1 );
620 18         214 $sock->print( "GET /PING\n\n" );
621 18         3004674 $resp = <$sock>;
622 17         72 alarm 0;
623 17         88 $sock->close;
624             };
625 22         4509 chomp $resp;
626 22         374 $resp;
627             } #ping
628              
629             sub shutdown {
630 3     3   7 my( $self, $timeout ) = @_;
631              
632 3   50     24 $timeout //= 3;
633              
634 3     0   36 local $SIG{ALRM} = sub { die "ALARM\n" };
  0         0  
635 3         7 alarm $timeout;
636 3         5 eval {
637 3         10 my $sock = $self->_get_sock( 1 );
638 3         14 $sock->print( "GET /SHUTDOWN\n\n" );
639 3         95 alarm 0;
640 3         10 $sock->close;
641             };
642 3         96 $@;
643             } #shutdown
644              
645              
646             1;
647              
648              
649             __END__