1# You may distribute under the terms of either the GNU General Public License 2# or the Artistic License (the same terms as Perl itself) 3# 4# (C) Paul Evans, 2009-2012 -- leonerd@leonerd.org.uk 5 6package IO::Async::Timer; 7 8use strict; 9use warnings; 10use base qw( IO::Async::Notifier ); 11 12our $VERSION = '0.800'; 13 14use Carp; 15 16=head1 NAME 17 18C<IO::Async::Timer> - base class for Notifiers that use timed delays 19 20=head1 DESCRIPTION 21 22This module provides a subclass of L<IO::Async::Notifier> for implementing 23notifiers that use timed delays. For specific implementations, see one of the 24subclasses: 25 26=over 8 27 28=item * 29 30L<IO::Async::Timer::Absolute> - event callback at a fixed future time 31 32=item * 33 34L<IO::Async::Timer::Countdown> - event callback after a fixed delay 35 36=item * 37 38L<IO::Async::Timer::Periodic> - event callback at regular intervals 39 40=back 41 42=cut 43 44=head1 CONSTRUCTOR 45 46=cut 47 48=head2 new 49 50 $timer = IO::Async::Timer->new( %args ) 51 52Constructs a particular subclass of C<IO::Async::Timer> object, and returns 53it. This constructor is provided for backward compatibility to older code 54which doesn't use the subclasses. New code should directly construct a 55subclass instead. 56 57=over 8 58 59=item mode => STRING 60 61The type of timer to create. Currently the only allowed mode is C<countdown> 62but more types may be added in the future. 63 64=back 65 66Once constructed, the C<Timer> will need to be added to the C<Loop> before it 67will work. It will also need to be started by the C<start> method. 68 69=cut 70 71sub new 72{ 73 my $class = shift; 74 my %args = @_; 75 76 if( my $mode = delete $args{mode} ) { 77 # Might define some other modes later 78 $mode eq "countdown" or croak "Expected 'mode' to be 'countdown'"; 79 80 require IO::Async::Timer::Countdown; 81 return IO::Async::Timer::Countdown->new( %args ); 82 } 83 84 return $class->SUPER::new( %args ); 85} 86 87sub _add_to_loop 88{ 89 my $self = shift; 90 $self->start if delete $self->{pending}; 91} 92 93sub _remove_from_loop 94{ 95 my $self = shift; 96 $self->stop; 97} 98 99=head1 METHODS 100 101=cut 102 103=head2 is_running 104 105 $running = $timer->is_running 106 107Returns true if the Timer has been started, and has not yet expired, or been 108stopped. 109 110=cut 111 112sub is_running 113{ 114 my $self = shift; 115 116 defined $self->{id}; 117} 118 119=head2 start 120 121 $timer->start 122 123Starts the Timer. Throws an error if it was already running. 124 125If the Timer is not yet in a Loop, the actual start will be deferred until it 126is added. Once added, it will be running, and will expire at the given 127duration after the time it was added. 128 129As a convenience, C<$timer> is returned. This may be useful for starting 130timers at construction time: 131 132 $loop->add( IO::Async::Timer->new( ... )->start ); 133 134=cut 135 136sub start 137{ 138 my $self = shift; 139 140 my $loop = $self->loop; 141 if( !defined $loop ) { 142 $self->{pending} = 1; 143 return $self; 144 } 145 146 defined $self->{id} and croak "Cannot start a Timer that is already running"; 147 148 if( !$self->{cb} ) { 149 $self->{cb} = $self->_make_cb; 150 } 151 152 $self->{id} = $loop->watch_time( 153 $self->_make_enqueueargs, 154 code => $self->{cb}, 155 ); 156 157 return $self; 158} 159 160=head2 stop 161 162 $timer->stop 163 164Stops the Timer if it is running. If it has not yet been added to the C<Loop> 165but there is a start pending, this will cancel it. 166 167=cut 168 169sub stop 170{ 171 my $self = shift; 172 173 if( $self->{pending} ) { 174 delete $self->{pending}; 175 return; 176 } 177 178 return if !$self->is_running; 179 180 my $loop = $self->loop or croak "Cannot stop a Timer that is not in a Loop"; 181 182 defined $self->{id} or return; # nothing to do but no error 183 184 $loop->unwatch_time( $self->{id} ); 185 186 undef $self->{id}; 187} 188 189=head1 AUTHOR 190 191Paul Evans <leonerd@leonerd.org.uk> 192 193=cut 194 1950x55AA; 196