http://www.perlmonks.org?node_id=919608

jdrago999 has asked for the wisdom of the Perl Monks concerning the following question:

Monks,

I am using Plack to run a simple, single-threaded server. I want it to execute a routine when it receives a `$SIG{INT}` or `$SIG{TERM}` but I don't want to clobber any existing INT/TERM signal handlers.

How can I do this?

My PSGI code below:

#!/usr/bin/perl -w use strict; use warnings 'all'; use forks; use forks::shared; use Plack::Request; use JSON::XS; use ASP4::API; my $api; BEGIN { $api = ASP4::API->new } use BP::db::job; use BP::db::job_list; my @finished_jobs : shared = ( ); my $RUNNING : shared = 1; # XXX: This subref right here is what I want to run - safely though - # without clobbering any existing INT/TERM signal handlers (and # without getting mine) clobbered as well! $SIG{INT} = $SIG{TERM} = sub { SCOPE: { lock($RUNNING); $RUNNING = 0; }; # Wait until any remaining completed jobs are marked as updated: map { $_->join } threads->list( threads::joinable ); exit(0); }; # Mark finished records as completed in batches - # makes things more efficient: threads->create(sub { while( $RUNNING ) { if( @finished_jobs ) { my @to_update; SCOPE: { lock(@finished_jobs); @to_update = splice(@finished_jobs, 0, scalar(@finished_jobs)) +; }; warn "About to update @{[ scalar(@to_update) ]} finished jobs... +\n"; # Do the update in one fell swoop: my $sth = BP::db::job_list->db_Main->prepare(<<"SQL"); update jobs set is_completed = 1, completed_on = now() where job_id in ( @{[ join ", ", map { '?' } @to_update ]} ) SQL $sth->execute( @to_update ); $sth->finish(); }# end if() sleep(4); }# end while() }); # The main app itself: my $app = sub { my $env = shift; my $req = Plack::Request->new( $env ); my %modes = map { $_ => $_ } qw( get_jobs mark_as_completed ); my $mode = $modes{ $req->param('mode') } or return [ 404, [ ], [ ] ]; my $s = bless { }, __PACKAGE__; my $result = $s->$mode( $req ) or return [ 500, [ ], [ ] ]; return $result; }; sub get_jobs { my ($s, $req) = @_; my @jobs = ( ); my $dbh = BP::db::job->db_Main; my $job_sth = $dbh->prepare(<<"SQL"); select * from jobs where job_list_id = ? and ( ( is_started = 0 ) or ( is_started = 1 and is_completed = 0 and started_on < date_add(now(), interval -30 minute) ) ) limit 0, 40 SQL $job_sth->execute( $req->param('job_list_id') ); @jobs = map { $_->as_hashref } BP::db::job->sth_to_objects( $job_sth ); # Mark as 'is_started' any jobs we've found: if( @jobs ) { $dbh->do(<<"SQL"); update jobs set is_started = 1, started_on = now() where job_id IN ( @{[ join ',', map { $_->{job_id} } @jobs ]} ) SQL }# end if() # Finally: return [ 200, ['content-type' => 'text/plain'], [ encode_json( \@jobs ) ] ]; }# end get_jobs() sub mark_as_completed { my ($s, $req) = @_; my $job_ids = eval { decode_json( $req->param('job_ids') ) } || [ ]; SCOPE: { lock(@finished_jobs); push @finished_jobs, @$job_ids; }; return [ 200, ['content-type' => 'text/plain'], ['OK'] ]; }# end mark_as_completed()

Replies are listed 'Best First'.
Re: Plack: Shutdown handler?
by sundialsvc4 (Abbot) on Aug 10, 2011 at 16:32 UTC

    Perhaps a flexible strategy would be to create a thread-safe queue of subroutine-references.   When a signal is received, the signal-handler simply runs through this list, calling all of the routines on it in sequence.   Any number of routines can be executed, and the list can be reliably maintained through the use of off-the-shelf CPAN.

      Perhaps a flexible strategy would be to create a thread-safe queue of subroutine-references.

      I like the idea, but I'm curious if there is something built-in to Plack itself. Something along the lines of a mod_perl2 `PerlShutdownHandler` thing. I don't see anything in the Plack docs but maybe I'm just looking wrong.

        I would not expect Plack to have such a thing.   The signal (whatever it is...) comes in once, and you are just carrying-out one or more actions in response to it.   (Hardware interrupt service routines in operating-systems routinely adopt a similar strategy, made necessary in part by the “sharing” of a limited number of IRQs, and in part by there simply being a variable number of “interested listeners.”)

        Packages like POE do have some logic along these lines ... whether to be literally-used or to be studied as a possible example.   These are the basis for “reactive,” “event-driven” systems which would treat “shutdown request” as just another type of event that can happen.   Food for thought, maybe.

Re: Plack: Shutdown handler?
by armstd (Friar) on Aug 11, 2011 at 18:29 UTC

    I'm probably missing something here, but when you are setting up your signal handler, you'll certainly have the opportunity to save aside whatever existing handler is in place already?

    If there's a pre-existing handler, why not just have your handler call it once your handler is done? My concern would be whether the stuff you're using that set their own handler would be as accomodating to your handler, and whether you would ever end up in a loop situation.

    --Dave

      I'm probably missing something here, but when you are setting up your signal handler, you'll certainly have the opportunity to save aside whatever existing handler is in place already?

      Yes - and if there is no other way around it, I'll do that.

      My concern would be whether the stuff you're using that set their own handler would be as accomodating to your handler, and whether you would ever end up in a loop situation.

      That's exactly the reason I was originally reluctant to simply check for existing handlers - "What if something else clobbers mine?" I will just have to try it and find out...