3 # This file is part of Stem.
4 # Copyright (C) 1999, 2000, 2001 Stem Systems, Inc.
6 # Stem is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # Stem is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with Stem; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 # For a license to use the Stem under conditions other than those
21 # described here, to purchase support for this software, or to purchase a
22 # commercial warranty contract, please contact Stem Systems at:
24 # Stem Systems, Inc. 781-643-7504
25 # 79 Everett St. info@stemsystems.com
29 # this is the base class for all of the other event classes. it
30 # provides common services and also stubs for the internal _methods so
31 # the other classes don't need to declare them if they don't use them.
39 # this will hold the hashes of events for each event type.
50 # table of loop types to the Stem::Event::* class name
62 # use the requested event loop and default to perl on windows and
65 my $loop_class = _get_loop_class() ;
72 $loop_class->_init_loop() ;
74 #Stem::Event::Queue::_init_queue() if defined &Stem::Event::Queue::_init_queue ;
80 $loop_class->_start_loop() ;
85 $loop_class->_stop_loop() ;
90 my( $self, $method ) = @_ ;
92 # never trigger inactive events
94 return unless $self->{active} ;
97 $method ||= $self->{'method'} ;
98 #print "METHOD [$method]\n" ;
100 $self->{'object'}->$method( $self->{'id'} ) ;
102 Stem::Msg::process_queue() if defined &Stem::Msg::process_queue;
108 # all the stuff below is a rough cell call trace thing. it needs work
109 # it would be put inside the trigger method
110 # 'log_type' attribute is set or the event type is used.
111 #_init subs need to set event_log_type in the object
112 #use Stem::Trace 'log' => 'stem_status', 'sub' => 'TraceStatus' ;
113 #use Stem::Trace 'log' => 'stem_error' , 'sub' => 'TraceError' ;
114 # $log_type = $self->{'log_type'} || $self->{'event_type'} ;
115 # TraceStatus "[$log_type] [$object] [$method]\n" ;
116 # $Stem::Event::current_object = $object ;
117 # my ( $cell_name, $target ) = Stem::Route::lookup_cell_name( $object ) ;
118 # if ( $cell_name ) {
120 # # "EVENT $event to $cell_name:$target [$object] [$method]\n" ;
123 # # Debug "EVENT $event to [$object] [$method]\n" ;
128 # get all the event objects for an event type
129 # this is a class sub.
133 my( $event_type ) = @_ ;
135 my $events = $all_events{ $event_type } ;
137 return unless $events ;
139 return values %{$events} if wantarray ;
144 # initialize the subclass object for this event and store generic event
147 sub _build_core_event {
151 my( $self, $event_type ) = @_ ;
154 #print "EVT [$self] [$event_type]\n" ;
156 # call and and check the return of the core event constructor
158 if ( my $core_event = $self->_build() ) {
160 # return the error if it was an error string
162 return $core_event unless ref $core_event ;
164 # save the core event
166 $self->{core_event} = $core_event ;
169 # mark the event type and track it
171 $self->{event_type} = $event_type ;
172 $all_events{ $event_type }{ $self } = $self ;
177 # these are the public versions of the support methods.
178 # subclasses can provide a _method to override the stub ones in this class.
184 $self->{'active'} = 0 ;
185 delete $self->{'object'} ;
187 # delete the core object
189 if ( my $core_event = delete $self->{core_event} ) {
191 # call the core cancel
193 $self->_cancel( $core_event ) ;
196 # delete this event from the tracking hash
198 delete $all_events{ $self->{event_type} }{ $self } ;
206 $self->{'active'} = 1 ;
207 $self->_start( $self->{core_event} ) ;
215 $self->{'active'} = 0 ;
216 $self->_stop( $self->{core_event} ) ;
221 # stubs for the internal methods that subclasses should override if needed.
230 use Stem::Debug qw( dump_socket dump_owner dump_data ) ;
234 print dump_data( \%all_events ) ;
241 my $event_text = <<TEXT ;
243 ACT: $self->{'active'}
246 my $obj_dump = dump_owner $self->{'object'} ;
247 $event_text .= <<TEXT ;
249 METH: $self->{'method'}
252 if ( my $fh = $self->{'fh'} ) {
254 my $fh_text = dump_socket( $self->{'fh'} ) ;
255 $event_text .= <<TEXT ;
260 if ( $self->{event_type} eq 'timer' ) {
262 my $delay = $self->{delay} || 'NONE' ;
263 my $interval = $self->{interval} || 'NONE' ;
264 $event_text .= <<TEXT ;
270 if ( my $io_timer_event = $self->{'io_timer_event'} ) {
272 $event_text = "IO TIMER: >>>>>\n" . $io_timer_event->dump() .
286 # change this to a cleaner loop style which can handle more event loops and
287 # try them in sequence
290 sub _get_loop_class {
292 my $loop_type = $Stem::Vars::Env{ 'event_loop' } ||
293 ($^O =~ /win32/i ? 'perl' : 'event' );
295 $loop_type = 'perl' unless $loop_to_class{ $loop_type } ;
296 my $loop_class = "Stem::Event::$loop_to_class{ $loop_type }" ;
298 unless ( eval "require $loop_class" ) {
299 die "can't load $loop_class: $@" if $@ && $@ !~ /locate/ ;
301 $loop_type = 'perl' ;
302 eval { require Stem::Event::Perl } ;
303 die "can't load event loop Stem::Event::Perl $@" if $@ ;
306 # save the event loop that we loaded.
308 #print "using event loop [$loop_type]\n" ;
309 $Stem::Vars::Env{ 'event_loop' } = $loop_type ;
315 ############################################################################
317 package Stem::Event::Plain ;
319 our @ISA = qw( Stem::Event ) ;
321 =head2 Stem::Event::Plain::new
323 This class creates an event that will trigger a callback after all
324 other pending events have been triggered.
328 $plain_event = Stem::Event::Plain->new( 'object' => $self ) ;
332 my $attr_spec_plain = [
339 This object gets the method callbacks
344 'default' => 'triggered',
346 This method is called on the object when the plain event is triggered
352 The id is passed to the callback method as its only argument. Use it to
353 identify different instances of this object.
361 my( $class ) = shift ;
363 my $self = Stem::Class::parse_args( $attr_spec_plain, @_ ) ;
364 return $self unless ref $self ;
366 my $err = $self->_build_core_event( 'plain' ) ;
367 return $err if $err ;
372 ############################################################################
374 package Stem::Event::Signal ;
376 our @ISA = qw( Stem::Event ) ;
378 =head2 Stem::Event::Signal::new
380 This class creates an event that will trigger a callback whenever
381 its its signal has been received.
385 $signal_event = Stem::Event::Signal->new( 'object' => $self,
386 'signal' => 'INT' ) ;
388 sub sig_int_handler { die "SIGINT\n" }
392 my $attr_spec_signal = [
399 This object gets the method callbacks
405 This method is called on the object when this event is triggered. The
406 default method name for the signal NAME is 'sig_name_handler' (all lower case)
413 This is the name of the signal to handle. It is used as part of the
414 default handler method name.
422 This flag marks the event as being active. It can be toggled with the
429 The id is passed to the callback method as its only argument. Use it to
430 identify different instances of this object.
438 my( $class ) = shift ;
440 my $self = Stem::Class::parse_args( $attr_spec_signal, @_ ) ;
441 return $self unless ref $self ;
443 my $signal = uc $self->{'signal'} ;
445 return "Unknown signal: $signal" unless exists $SIG{ $signal } ;
447 $self->{'method'} ||= "sig_\L${signal}_handler" ;
448 $self->{'signal'} = $signal ;
450 my $err = $self->_build_core_event( 'signal' ) ;
451 return $err if $err ;
453 #print "SELF SIG $self\nPID $$\n" ;
459 ############################################################################
461 package Stem::Event::Timer ;
463 our @ISA = qw( Stem::Event ) ;
465 =head2 Stem::Event::Timer::new
467 This class creates an event that will trigger a callback after a time
468 period has elapsed. The initial timer delay is set from the 'delay',
469 'at' or 'interval' attributes in that order. If the 'interval'
470 attribute is not set, the timer will cancel itself after its first
471 triggering (it is a one-shot). The 'hard' attribute means that the
472 next interval delay starts before the callback to the object is
473 made. If a soft timer is selected (hard is 0), the delay starts after
474 the callback returns. So the hard timer ignores the time taken by the
475 callback and so it is a more accurate timer. The accuracy a soft timer
476 is affected by how much time the callback takes.
480 $timer_event = Stem::Event::Timer->new( 'object' => $self,
484 sub timed_out { print "timer alert\n" } ;
491 my $attr_spec_timer = [
498 This object gets the method callbacks
503 'default' => 'timed_out',
505 This method is called on the object when the timeout is triggered
511 Delay this amount of seconds before triggering the first time. If this
512 is not set then the 'at' or 'interval' attributes will be used.
516 'name' => 'interval',
518 Wait this time (in seconds) before any repeated triggers. If not set
519 then the timer is a one-shot
525 Trigger in the future at this time (in epoch seconds). It will set the intial
526 delay to the different between the current time and the 'at' time.
534 If this is set, the interval time starts when the event is
535 triggered. If it is not set, the interval time starts when the object
536 callback has finished. So 'hard' timers repeat closer to equal
537 intervals while without 'hard' the repeat time is dependant on how
538 long the callback takes.
546 This flag marks the event as being active. It can be toggled with the
553 The id is passed to the callback method as its only argument. Use it to
554 identify different instances of this object.
562 my( $class ) = shift ;
564 my $self = Stem::Class::parse_args( $attr_spec_timer, @_ ) ;
565 return $self unless ref $self ;
567 # the delay is either set, or at a future time or the interval
569 my $delay = exists( $self->{ 'delay' } ) ?
571 exists( $self->{ 'at' } ) ?
572 $self->{ 'at' } - time() :
573 $self->{'interval'} ;
575 #print "INT $self->{'interval'} DELAY $delay\n" ;
577 # squawk if no delay value
579 return "No initial delay was specified for timer"
580 unless defined $delay ;
582 $self->{'delay'} = $delay ;
583 $self->{'time_left'} = $delay ;
585 my $err = $self->_build_core_event( 'timer' ) ;
586 return $err if $err ;
589 # check on this logic
592 $self->_stop unless $self->{'active'} ;
601 my( $self, $reset_delay ) = @_ ;
603 return unless $self->{'active'} ;
605 # if we don't get passed a delay, use the interval or the delay attribute
607 $reset_delay ||= ($self->{'interval'}) ?
608 $self->{'interval'} : $self->{'delay'} ;
610 # track the new delay and reset the real timer (if we are using one)
612 $self->{'time_left'} = $reset_delay ;
614 $self->_reset( $self->{core_event}, $reset_delay ) ;
619 sub timer_triggered {
623 #print time(), " TIMER TRIG\n" ;
624 #use Carp qw( cluck ) ;
627 # check if this is a one-shot timer
629 $self->cancel() unless $self->{'interval'} ;
631 # reset the timer count before the trigger code for hard timers
632 #(trigger on fixed intervals)
634 $self->reset( $self->{'interval'} ) if $self->{'hard'};
638 # reset the timer count before the trigger code for soft timers
639 #(trigger on at least fixed intervals)
641 $self->reset( $self->{'interval'} ) unless $self->{'hard'};
644 ############################################################################
646 ####################################################################
647 # common methods for the Read/Write event classes to handle the optional
649 # these override Stem::Event's methods and then call those via SUPER::
651 package Stem::Event::IO ;
653 our @ISA = qw( Stem::Event ) ;
655 sub init_io_timeout {
659 my $timeout = $self->{'timeout'} ;
660 return unless $timeout ;
662 $self->{'io_timer_event'} = Stem::Event::Timer->new(
664 'interval' => $timeout,
674 #print "IO CANCEL $self\n" ;
676 if ( my $io_timer_event = delete $self->{'io_timer_event'} ) {
677 $io_timer_event->cancel() ;
680 $self->SUPER::cancel() ;
682 delete $self->{'fh'} ;
691 if ( my $io_timer_event = $self->{'io_timer_event'} ) {
692 $io_timer_event->start() ;
695 $self->SUPER::start() ;
704 $self->{'active'} = 0 ;
706 if ( my $io_timer_event = $self->{'io_timer_event'} ) {
707 $io_timer_event->stop() ;
710 $self->SUPER::stop() ;
719 # $self->{log_type} = "$self->{'event_type'}_timeout" ;
720 $self->trigger( $self->{'timeout_method'} ) ;
723 #######################################################
725 package Stem::Event::Read ;
727 our @ISA = qw( Stem::Event::IO ) ;
729 =head2 Stem::Event::Read::new
731 This class creates an event that will trigger a callback whenever
732 its file descriptor has data to be read. It takes an optional timeout
733 value which will trigger a callback to the object if no data has been
734 read during that period.
736 Read events are active when created - a call to the stop method is
737 needed to deactivate them.
743 my $attr_spec_read = [
750 This object gets the method callbacks
758 This file handle is checked if it has data to read
764 How long to wait (in seconds) without being readable before calling
770 'default' => 'readable',
772 This method is called on the object when the file handle has data to read
776 'name' => 'timeout_method',
777 'default' => 'read_timeout',
779 This method is called on the object when the hasn't been readable
780 after the timeout period
788 This flag marks the event as being active. It can be toggled with the
795 The id is passed to the callback method as its only argument. Use it to
796 identify different instances of this object.
804 my( $class ) = shift ;
806 my $self = Stem::Class::parse_args( $attr_spec_read, @_ ) ;
807 return $self unless ref $self ;
809 # return <<ERR unless defined fileno $self->{fh} ;
810 # Stem::Event::Read: $self->{fh} is not an open handle
813 my $err = $self->_build_core_event( 'read' ) ;
814 return $err if $err ;
816 $self->init_io_timeout() ;
822 ############################################################################
824 package Stem::Event::Write ;
826 our @ISA = qw( Stem::Event::IO ) ;
828 =head2 Stem::Event::Write::new
830 This class creates an event that will trigger a callback whenever
831 its file descriptor can be written to. It takes an optional timeout
832 value which will trigger a callback to the object if no data has been
833 written during that period.
835 Write events are stopped when created - a call to the start method is
836 needed to activate them.
840 my $attr_spec_write = [
847 This object gets the method callbacks
855 This file handle is checked if it is writeable
861 How long to wait (in seconds) without being writeable before calling
867 'default' => 'writeable',
869 This method is called on the object when the file handle is writeable
873 'name' => 'timeout_method',
874 'default' => 'write_timeout',
876 This method is called on the object when the hasn't been writeable
877 after the timeout period
885 This flag marks the event as being active. It can be toggled with the
887 NOTE: Write events are not active by default.
893 The id is passed to the callback method as its only argument. Use it to
894 identify different instances of this object.
902 my( $class ) = shift ;
904 my $self = Stem::Class::parse_args( $attr_spec_write, @_ ) ;
905 return $self unless ref $self ;
907 my $err = $self->_build_core_event( 'write' ) ;
908 return $err if $err ;
910 #print $self->dump_events() ;
912 $self->init_io_timeout() ;
914 $self->stop() unless $self->{'active'} ;
916 #print $self->dump() ;