--- Timers.pm.saved 2011-04-07 13:50:08.000000000 -0700 +++ Timers.pm 2011-09-04 11:02:24.000000000 -0700 @@ -1,6 +1,6 @@ package Slim::Utils::Timers; -# $Id$ +# $Id: Timers.pm 32244 2011-04-07 20:50:08Z adrian $ # Squeezebox Server Copyright 2001-2009 Logitech. # This program is free software; you can redistribute it and/or @@ -16,6 +16,12 @@ # Run someCode( $client, @args ) in 20 seconds Slim::Utils::Timers::setTimer( $client, Time::HiRes::time() + 20, \&someCode, @args ); + # Run someCode( $client, @args ) in 20 seconds + Slim::Utils::Timers::setRelTimer( $client, 20, \&someCode, @args ); + + # Run someCode( $client, @args ) in at absolute time seconds + Slim::Utils::Timers::setAbsTimer( $client, $alarmtime, \&someCode, @args ); + # On second thought, don't run it Slim::Utils::Timers::killTimers( $client, \&someCode ); @@ -94,6 +100,70 @@ *setTimer = \&_makeTimer; +=head2 setAbsTimer( $obj, $when, $coderef, @args ) + +Schedule a nnormal priority absolute timer. Returns a reference to the internal timer +object. This can be passed to killSpecific to remove this specific timer. + +=over 4 + +=item obj + +$obj can be any value you would like passed to $coderef as the first argument. + +=item when + +A hi-res epoch time value for when the timer should fire. Typically, this is set +with Time::HiRes::time() + $seconds. + +=item coderef + +A code reference that will be run when the timer fires. It is passed $obj as the first +argument, followed by any other arguments specified. + +=item args + +An array of any other arguments to be passed to $coderef. + +=back + +=cut + +*setAbsTimer = \&_makeAbsTimer; + +=head2 setRelTimer( $obj, $delta, $coderef, @args ) + +Schedule a nnormal priority relative timer. Returns a reference to the internal timer +object. This can be passed to killSpecific to remove this specific timer. + +=over 4 + +=item obj + +$obj can be any value you would like passed to $coderef as the first argument. + +=item delta + +A hi-res time value for when the timer should fire relative to the current epoch. +Typically, this is set with some value of $seconds. + +=item coderef + +A code reference that will be run when the timer fires. It is passed $obj as the first +argument, followed by any other arguments specified. + +=item args + +An array of any other arguments to be passed to $coderef. + +=back + +=cut + +*setRelTimer = \&_makeRelTimer; + + + =head2 killTimers ( $obj, $coderef ) Remove all normal timers that match the $obj and $coderef. Returns the number @@ -281,6 +351,115 @@ return $w; } + +sub _makeRelTimer { + my ($objRef, $delta, $subptr, @args) = @_; + + if ( !defined $objRef ) { + $objRef = ''; + } + + my $now; #used by PERFMON below + EV::now_update; + + # We could use AnyEvent->timer here but paying the overhead + # cost of proxying the method is silly + my $w; + $w = EV::timer( $delta, 0, sub { + if ( $_[0] && $_[0] == EV_KILL ) { + # Nasty hack to destroy the EV::Timer object properly + defined $w && $w->stop; + undef $w; + return; + } + + main::PERFMON && ($now = AnyEvent->time); + + eval { $subptr->( $objRef, @args ) }; + + main::PERFMON && Slim::Utils::PerfMon->check('timers', AnyEvent->time - $now, undef, $subptr); + + if ( $@ ) { + my $name = Slim::Utils::PerlRunTime::realNameForCodeRef($subptr); + + logError("Timer $name failed: $@"); + + if ( main::SLIM_SERVICE ) { + $@ =~ s/"/'/g; + SDI::Util::Syslog::error("service=SS-Timer method=${name} error=\"$@\""); + } + } + + # Destroy the timer after it's been run + undef $w; + } ); + + _storeTimer( $subptr, $objRef, $w ); + + if ( !$CLEANUP ) { + # start periodic cleanup timer + $CLEANUP = 1; + + setTimer( undef, $now + CLEANUP_INTERVAL, \&cleanupTimers ); + } + + return $w; +} + + +sub _makeAbsTimer { + my ($objRef, $when, $subptr, @args) = @_; + + if ( !defined $objRef ) { + $objRef = ''; + } + + my $now; + # We could use AnyEvent->timer here but paying the overhead + # cost of proxying the method is silly + my $w; + $w = EV::periodic( $when, 0, 0, sub { + if ( $_[0] && $_[0] == EV_KILL ) { + # Nasty hack to destroy the EV::Timer object properly + defined $w && $w->stop; + undef $w; + return; + } + + main::PERFMON && ($now = AnyEvent->time); + + eval { $subptr->( $objRef, @args ) }; + + main::PERFMON && Slim::Utils::PerfMon->check('timers', AnyEvent->time - $now, undef, $subptr); + + if ( $@ ) { + my $name = Slim::Utils::PerlRunTime::realNameForCodeRef($subptr); + + logError("Timer $name failed: $@"); + + if ( main::SLIM_SERVICE ) { + $@ =~ s/"/'/g; + SDI::Util::Syslog::error("service=SS-Timer method=${name} error=\"$@\""); + } + } + + # Destroy the timer after it's been run + undef $w; + } ); + + _storeTimer( $subptr, $objRef, $w ); + + if ( !$CLEANUP ) { + # start periodic cleanup timer + $CLEANUP = 1; + + setTimer( undef, $now + CLEANUP_INTERVAL, \&cleanupTimers ); + } + + return $w; +} + + # Periodically go through and clean out empty timers sub cleanupTimers { my @subs = keys %TIMERS;