| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Net::Peep::Scheduler; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | require 5.00503; | 
| 4 | 3 |  |  | 3 |  | 1131 | use strict; | 
|  | 3 |  |  |  |  | 7 |  | 
|  | 3 |  |  |  |  | 103 |  | 
| 5 |  |  |  |  |  |  | # use warnings; # commented out for 5.005 compatibility | 
| 6 | 3 |  |  | 3 |  | 16 | use Carp; | 
|  | 3 |  |  |  |  | 4 |  | 
|  | 3 |  |  |  |  | 178 |  | 
| 7 | 3 |  |  | 3 |  | 932 | use Data::Dumper; | 
|  | 3 |  |  |  |  | 10630 |  | 
|  | 3 |  |  |  |  | 173 |  | 
| 8 | 3 |  |  | 3 |  | 1071 | use Time::HiRes qw{ tv_interval gettimeofday alarm }; | 
|  | 3 |  |  |  |  | 1727 |  | 
|  | 3 |  |  |  |  | 23 |  | 
| 9 | 3 |  |  | 3 |  | 1144 | use Net::Peep::Log; | 
|  | 3 |  |  |  |  | 5 |  | 
|  | 3 |  |  |  |  | 144 |  | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | require Exporter; | 
| 12 |  |  |  |  |  |  |  | 
| 13 | 3 |  |  | 3 |  | 15 | use vars qw{ @ISA %EXPORT_TAGS @EXPORT_OK @EXPORT $VERSION }; | 
|  | 3 |  |  |  |  | 16 |  | 
|  | 3 |  |  |  |  | 495 |  | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | @ISA = qw(Exporter); | 
| 16 |  |  |  |  |  |  | %EXPORT_TAGS = ( 'all' => [ qw( ) ] ); | 
| 17 |  |  |  |  |  |  | @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); | 
| 18 |  |  |  |  |  |  | @EXPORT = qw( ); | 
| 19 |  |  |  |  |  |  | $VERSION = do { my @r = (q$Revision: 1.2 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | # structure of an event | 
| 22 |  |  |  |  |  |  | # $entry = { | 
| 23 |  |  |  |  |  |  | #  'application' => The application name | 
| 24 |  |  |  |  |  |  | #  'schedule_time' => the time for wakeup | 
| 25 |  |  |  |  |  |  | #  'type' => the type of event | 
| 26 |  |  |  |  |  |  | #  'data' => the data to pass to the handler | 
| 27 |  |  |  |  |  |  | #  'handler' => the handler to invoke | 
| 28 |  |  |  |  |  |  | # } | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | # The scheduled event queue | 
| 31 | 3 |  |  | 3 |  | 14 | use vars qw( @scheduler_queue ); | 
|  | 3 |  |  |  |  | 5 |  | 
|  | 3 |  |  |  |  | 3107 |  | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | sub new { | 
| 34 |  |  |  |  |  |  |  | 
| 35 | 5 |  |  | 5 | 0 | 448 | my $self = shift; | 
| 36 | 5 |  | 33 |  |  | 43 | my $class = ref($self) || $self; | 
| 37 | 5 |  |  |  |  | 15 | my $this = { }; | 
| 38 | 5 |  |  |  |  | 16 | bless $this, $class; | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | # Init the scheduler | 
| 41 | 5 |  |  |  |  | 21 | $this->logger()->debug(8, "Registering scheduler and scheduling alarm ..."); | 
| 42 | 5 |  |  | 1 |  | 139 | $SIG{'ALRM'} = sub { $this->schedulerWakeUp }; | 
|  | 1 |  |  |  |  | 2999904 |  | 
| 43 |  |  |  |  |  |  |  | 
| 44 | 5 |  |  |  |  | 24 | return $this; | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | } #end sub new | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | # returns a logging object | 
| 49 |  |  |  |  |  |  | sub logger { | 
| 50 |  |  |  |  |  |  |  | 
| 51 | 16 |  |  | 16 | 0 | 38 | my $self = shift; | 
| 52 | 16 | 100 |  |  |  | 99 | unless ( exists $self->{'__LOGGER'} ) { $self->{'__LOGGER'} = new Net::Peep::Log } | 
|  | 5 |  |  |  |  | 41 |  | 
| 53 | 16 |  |  |  |  | 167 | return $self->{'__LOGGER'}; | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | } #end sub logger | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | sub schedulerAddEvent { | 
| 58 |  |  |  |  |  |  |  | 
| 59 | 3 |  |  | 3 | 0 | 356 | my ($self, $app, $sleepsec, $sleepusec, $type, $handler, $data, $repeated) = @_; | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | # Do some sanity checking | 
| 62 | 3 | 50 |  |  |  | 8 | confess "Error: No application name given to scheduler when adding event." unless $app; | 
| 63 | 3 | 50 | 33 |  |  | 11 | confess "Error: Wakeup given to scheduler is in the past." unless $sleepsec > 0.0 || $sleepusec > 0.0; | 
| 64 | 3 | 50 |  |  |  | 9 | confess "Error: No scheduled event type given to scheduler when adding event." unless $type; | 
| 65 | 3 | 50 |  |  |  | 11 | confess "Error: No handler given to scheduler when adding event." unless $handler; | 
| 66 |  |  |  |  |  |  |  | 
| 67 | 3 |  |  |  |  | 18 | my ($s, $usec) = gettimeofday(); | 
| 68 |  |  |  |  |  |  |  | 
| 69 | 3 |  |  |  |  | 29 | my $entry = { | 
| 70 |  |  |  |  |  |  | 'application' => $app, | 
| 71 |  |  |  |  |  |  | 'sleepsec' => $sleepsec, | 
| 72 |  |  |  |  |  |  | 'sleepusec' => $sleepusec, | 
| 73 |  |  |  |  |  |  | 'schedule_time' => [ $s + $sleepsec, $usec + $sleepusec ], | 
| 74 |  |  |  |  |  |  | 'type' => $type, | 
| 75 |  |  |  |  |  |  | 'data' => $data, | 
| 76 |  |  |  |  |  |  | 'handler' => $handler, | 
| 77 |  |  |  |  |  |  | 'repeated' => $repeated, | 
| 78 |  |  |  |  |  |  | }; | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | # Add the entry into the scheduler queue and sort by time | 
| 81 | 3 |  |  |  |  | 7 | push @scheduler_queue, $entry; | 
| 82 | 4 |  |  |  |  | 9 | @scheduler_queue = sort { | 
| 83 | 3 |  |  |  |  | 12 | my ($asec, $ausec) = @{ $a->{'schedule_time'} }; | 
|  | 4 |  |  |  |  | 5 |  | 
| 84 | 4 |  |  |  |  | 7 | my ($bsec, $busec) = @{ $b->{'schedule_time'} }; | 
|  | 4 |  |  |  |  | 48 |  | 
| 85 | 4 |  |  |  |  | 13 | $asec + 0.000001 * $ausec  <=> $bsec + 0.000001 * $busec; | 
| 86 |  |  |  |  |  |  | } @scheduler_queue; | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | # Now sleep for the new time | 
| 89 | 3 |  |  |  |  | 9 | $self->schedulerSleep; | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | } #end sub schedulerAddEvent | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | sub schedulerRemoveEventsForApp { | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | # Removes all entries in the scheduler queue for an application | 
| 96 |  |  |  |  |  |  |  | 
| 97 | 0 |  |  | 0 | 0 | 0 | my $self = shift; | 
| 98 | 0 |  | 0 |  |  | 0 | my $app = shift || die "Application name not found!"; | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 0 |  |  |  |  | 0 | @scheduler_queue = grep ! $_->{'app'} eq $app, @scheduler_queue; | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | } # end sub schedulerRemoveEventsForApp | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | sub schedulerGetEvent { | 
| 106 |  |  |  |  |  |  |  | 
| 107 | 3 |  |  | 3 | 0 | 10 | my $self = shift; | 
| 108 | 3 |  |  |  |  | 13 | return (shift @scheduler_queue); | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | } #end sub schedulerGetEvent | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | sub schedulerCalcSleepTime { | 
| 113 |  |  |  |  |  |  |  | 
| 114 | 6 |  |  | 6 | 0 | 11 | my $self = shift; | 
| 115 | 6 |  |  |  |  | 14 | my $nextent = $scheduler_queue[0]; | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | # Check if we have an empty queue | 
| 118 | 6 | 100 |  |  |  | 20 | unless ( $nextent ) { return undef; } | 
|  | 1 |  |  |  |  | 10 |  | 
| 119 |  |  |  |  |  |  |  | 
| 120 | 5 |  |  |  |  | 71 | my $sleeptime = tv_interval ( [ gettimeofday() ], $nextent->{'schedule_time'} ); | 
| 121 | 5 |  |  |  |  | 79 | return $sleeptime; | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | } #end sub schedulerCalcSleepTime | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | sub schedulerSleep { | 
| 126 |  |  |  |  |  |  |  | 
| 127 | 6 |  |  | 6 | 0 | 13 | my ($self, $time) = @_; | 
| 128 | 6 |  | 66 |  |  | 42 | my $sleeptime = $time || $self->schedulerCalcSleepTime; | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | # Check if there's no such sleep time at this moment | 
| 131 | 6 | 100 |  |  |  | 22 | unless ( $sleeptime ) { return undef; } | 
|  | 1 |  |  |  |  | 20 |  | 
| 132 |  |  |  |  |  |  |  | 
| 133 | 5 |  |  |  |  | 16 | $self->logger()->debug(8, "Scheduler will wake up in $sleeptime seconds."); | 
| 134 | 5 |  |  |  |  | 49 | alarm ( $sleeptime ); | 
| 135 | 5 |  |  |  |  | 51 | return $sleeptime; | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | } #end sub schedulerSleep | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | sub schedulerExplicitWakeUp { | 
| 140 |  |  |  |  |  |  |  | 
| 141 | 0 |  |  | 0 | 0 | 0 | my $self = shift; | 
| 142 | 0 |  |  |  |  | 0 | $self->logger()->debug(8, "Scheduler received explicit wake up..."); | 
| 143 | 0 |  |  |  |  | 0 | $self->schedulerWakeUp; | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | } #end sub schedulerExplicitWakeUp | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | sub schedulerWakeUp { | 
| 148 |  |  |  |  |  |  |  | 
| 149 | 3 |  |  | 3 | 0 | 16 | my $self = shift; | 
| 150 | 3 |  |  |  |  | 188 | $self->logger()->debug(8, "Scheduler woke up."); | 
| 151 | 3 |  |  |  |  | 15 | my $entry = $self->schedulerGetEvent; | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | # Doesn't apply because a schedulerExplicitWakeUp call would violate this and | 
| 154 |  |  |  |  |  |  | # still be valid | 
| 155 |  |  |  |  |  |  | # | 
| 156 |  |  |  |  |  |  | #	# Check that the time has past | 
| 157 |  |  |  |  |  |  | #	unless ( &Time::HiRes::tv_interval ( [ &Time::HiRes::gettimeofday() ], $entry->{'schedule_time'}) < 0.0 ) { | 
| 158 |  |  |  |  |  |  | #		$self->logger()->debug(8, "Scheduled event was premature - returned error."); | 
| 159 |  |  |  |  |  |  | #		return "Error: Scheduler woke up prematurely."; | 
| 160 |  |  |  |  |  |  | #	} | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | # Check if this is an internal housekeeping entry | 
| 163 |  |  |  |  |  |  | # Otherwise, pass control and data to the handler | 
| 164 | 3 | 50 |  |  |  | 20 | if ($entry->{'application'} eq '__SCHEDULER') { | 
| 165 |  |  |  |  |  |  | # internal processing - reserved for future use | 
| 166 | 0 |  |  |  |  | 0 | $self->logger()->debug(8, "Processing internal event..."); | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  | else { | 
| 169 |  |  |  |  |  |  | # Otherwise, call the handler with arguments of the type | 
| 170 |  |  |  |  |  |  | # of scheduled event and the data associated | 
| 171 | 3 |  |  |  |  | 51 | $self->logger()->debug(8, "Invoking event handler for ". $entry->{'application'}. " of type ". $entry->{'type'}. " ..."); | 
| 172 | 3 |  |  |  |  | 11 | &{ $entry->{'handler'} } ( $entry->{'type'}, $entry->{'data'} ); | 
|  | 3 |  |  |  |  | 26 |  | 
| 173 | 3 | 50 |  |  |  | 591 | if ($entry->{'repeated'}) { | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | # if it's a repeated event, it should | 
| 176 |  |  |  |  |  |  | # reschedule itself | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | # note that repeated events don't happen | 
| 179 |  |  |  |  |  |  | # precisely every sleepsec + 0.000001 * | 
| 180 |  |  |  |  |  |  | # sleepusec because of a delay every cycle | 
| 181 |  |  |  |  |  |  | # imposed by the execution time of the handler | 
| 182 |  |  |  |  |  |  |  | 
| 183 | 0 |  |  |  |  | 0 | $self->schedulerAddEvent( | 
| 184 |  |  |  |  |  |  | $entry->{'application'}, | 
| 185 |  |  |  |  |  |  | $entry->{'sleepsec'}, | 
| 186 |  |  |  |  |  |  | $entry->{'sleepusec'}, | 
| 187 |  |  |  |  |  |  | $entry->{'type'}, | 
| 188 |  |  |  |  |  |  | $entry->{'handler'}, | 
| 189 |  |  |  |  |  |  | $entry->{'data'}, | 
| 190 |  |  |  |  |  |  | $entry->{'repeated'} | 
| 191 |  |  |  |  |  |  | ); | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  | } | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | # Reassign ourselves before we exit | 
| 196 | 3 |  |  | 2 |  | 72 | $SIG{'ALRM'} = sub { $self->schedulerWakeUp }; | 
|  | 2 |  |  |  |  | 2958095 |  | 
| 197 | 3 |  |  |  |  | 16 | $self->schedulerSleep; | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | } #end sub schedulerWakeUp | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | 1; | 
| 202 |  |  |  |  |  |  | __END__ |