mirror of
https://github.com/pragma-/pbot.git
synced 2024-12-27 13:12:42 +01:00
196 lines
4.5 KiB
Perl
196 lines
4.5 KiB
Perl
# File: Timer.pm
|
|
# Author: pragma_
|
|
#
|
|
# Purpose: Provides functionality to register and execute one or more subroutines every X seconds.
|
|
#
|
|
# Caveats: Uses ALARM signal and all its issues.
|
|
|
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
package PBot::Timer;
|
|
|
|
use warnings;
|
|
use strict;
|
|
|
|
use feature 'unicode_strings';
|
|
|
|
use Carp ();
|
|
|
|
our $min_timeout = 1;
|
|
our $max_seconds = 1000000;
|
|
our $seconds = 0;
|
|
our @timer_funcs;
|
|
|
|
$SIG{ALRM} = sub {
|
|
$seconds += $min_timeout;
|
|
alarm $min_timeout;
|
|
|
|
# print "ALARM! $seconds $min_timeout\n";
|
|
|
|
# call timer func subroutines
|
|
foreach my $func (@timer_funcs) { &$func; }
|
|
|
|
# prevent $seconds over-flow
|
|
$seconds -= $max_seconds if $seconds > $max_seconds;
|
|
};
|
|
|
|
sub new {
|
|
if (ref($_[1]) eq 'HASH') {
|
|
Carp::croak("Options to Timer should be key/value pairs, not hash reference");
|
|
}
|
|
|
|
my ($class, %conf) = @_;
|
|
|
|
my $timeout = delete $conf{timeout};
|
|
$timeout = 10 unless defined $timeout;
|
|
|
|
my $name = delete $conf{name};
|
|
$name = "Unnamed $timeout Second Timer" unless defined $name;
|
|
|
|
my $self = {
|
|
handlers => [],
|
|
|
|
name => $name,
|
|
timeout => $timeout,
|
|
enabled => 0,
|
|
};
|
|
|
|
bless $self, $class;
|
|
|
|
$min_timeout = $timeout if $timeout < $min_timeout;
|
|
|
|
# alarm signal handler (poor-man's timer)
|
|
$self->{timer_func} = sub { on_tick_handler($self) };
|
|
|
|
return $self;
|
|
}
|
|
|
|
sub start {
|
|
my $self = shift;
|
|
# print "Starting Timer $self->{name} $self->{timeout} $self->{enabled}\n";
|
|
$self->{enabled} = 1;
|
|
push @timer_funcs, $self->{timer_func};
|
|
alarm $min_timeout;
|
|
}
|
|
|
|
sub stop {
|
|
my $self = shift;
|
|
# print "Stopping timer $self->{name}\n";
|
|
$self->{enabled} = 0;
|
|
@timer_funcs = grep { $_ != $self->{timer_func} } @timer_funcs;
|
|
}
|
|
|
|
sub on_tick_handler {
|
|
my $self = shift;
|
|
my $elapsed = 0;
|
|
|
|
# print "-----\n";
|
|
# print "on tick handler for $self->{name}\n";
|
|
|
|
if ($self->{enabled}) {
|
|
if ($#{ $self->{handlers} } > -1) {
|
|
# call handlers supplied via register() if timeout for each has elapsed
|
|
foreach my $func (@{ $self->{handlers} }) {
|
|
if (defined $func->{last}) {
|
|
$func->{last} -= $max_seconds if $seconds < $func->{last}; # handle wrap-around of $seconds
|
|
|
|
if ($seconds - $func->{last} >= $func->{timeout}) {
|
|
$func->{last} = $seconds;
|
|
$elapsed = 1;
|
|
}
|
|
} else {
|
|
$func->{last} = $seconds;
|
|
$elapsed = 1;
|
|
}
|
|
|
|
if ($elapsed) {
|
|
&{ $func->{subref} }($self);
|
|
$elapsed = 0;
|
|
}
|
|
}
|
|
} else {
|
|
# call default overridable handler if timeout has elapsed
|
|
if (defined $self->{last}) {
|
|
# print "$self->{name} last = $self->{last}, seconds: $seconds, timeout: $self->{timeout} " . ($seconds - $self->{last}) . "\n";
|
|
|
|
$self->{last} -= $max_seconds if $seconds < $self->{last}; # handle wrap-around
|
|
|
|
if ($seconds - $self->{last} >= $self->{timeout}) {
|
|
$elapsed = 1;
|
|
$self->{last} = $seconds;
|
|
}
|
|
} else {
|
|
# print "New addition for $self->{name}\n";
|
|
$elapsed = 1;
|
|
$self->{last} = $seconds;
|
|
}
|
|
|
|
if ($elapsed) {
|
|
$self->on_tick();
|
|
$elapsed = 0;
|
|
}
|
|
}
|
|
}
|
|
# print "-----\n";
|
|
}
|
|
|
|
# overridable method, executed whenever timeout is triggered
|
|
sub on_tick {
|
|
my $self = shift;
|
|
|
|
print "Tick! $self->{name} $self->{timeout} $self->{last} $seconds\n";
|
|
}
|
|
|
|
sub register {
|
|
my $self = shift;
|
|
my ($ref, $timeout, $id) = @_;
|
|
|
|
Carp::croak("Must pass subroutine reference to register()") if not defined $ref;
|
|
|
|
# TODO: Check if subref already exists in handlers?
|
|
|
|
$timeout = 300 if not defined $timeout; # set default value of 5 minutes if not defined
|
|
$id = 'timer' if not defined $id;
|
|
|
|
my $h = { subref => $ref, timeout => $timeout, id => $id };
|
|
push @{ $self->{handlers} }, $h;
|
|
|
|
# print "-- Registering timer $ref [$id] at $timeout seconds\n";
|
|
|
|
if ($timeout < $min_timeout) {
|
|
$min_timeout = $timeout;
|
|
}
|
|
|
|
if ($self->{enabled}) {
|
|
alarm $min_timeout;
|
|
}
|
|
}
|
|
|
|
sub unregister {
|
|
my $self = shift;
|
|
my $id;
|
|
|
|
if (@_) {
|
|
$id = shift;
|
|
} else {
|
|
Carp::croak("Must pass timer id to unregister()");
|
|
}
|
|
|
|
@{ $self->{handlers} } = grep { $_->{id} ne $id } @{ $self->{handlers} };
|
|
}
|
|
|
|
sub update_interval {
|
|
my ($self, $id, $interval) = @_;
|
|
|
|
foreach my $h (@{ $self->{handlers} }) {
|
|
if ($h->{id} eq $id) {
|
|
$h->{timeout} = $interval;
|
|
last;
|
|
}
|
|
}
|
|
}
|
|
|
|
1;
|