updated manifest
[urisagit/Stem.git] / lib / Stem / Event.pm
CommitLineData
4536f655 1# File: Stem/Event.pm
2
3# This file is part of Stem.
4# Copyright (C) 1999, 2000, 2001 Stem Systems, Inc.
5
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.
10
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.
15
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
19
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:
23
24# Stem Systems, Inc. 781-643-7504
25# 79 Everett St. info@stemsystems.com
26# Arlington, MA 02474
27# USA
28
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.
32
33package Stem::Event ;
34
35use Stem::Class ;
36
37use strict ;
38
39# this will hold the hashes of events for each event type.
40
41my %all_events = (
42
43 plain => {},
44 signal => {},
45 timer => {},
46 read => {},
47 write => {},
48) ;
49
50# table of loop types to the Stem::Event::* class name
51
52my %loop_to_class = (
53
54 event => 'EventPM',
55 perl => 'Perl',
56 tk => 'Tk',
57 wx => 'Wx',
58# gtk => 'Gtk',
59# qt => 'Qt',
60) ;
61
62# use the requested event loop and default to perl on windows and
63# event.pm elsewhere.
64
65my $loop_class = _get_loop_class() ;
66
67init_loop() ;
68
69
70sub init_loop {
71
72 $loop_class->_init_loop() ;
73
74Stem::Event::Queue::_init_queue() if defined &Stem::Event::Queue::_init_queue ;
75
76}
77
78sub start_loop {
79
80 $loop_class->_start_loop() ;
81}
82
83sub stop_loop {
84
85 $loop_class->_stop_loop() ;
86}
87
88sub trigger {
89
90 my( $self, $method ) = @_ ;
91
92# never trigger inactive events
93
94 return unless $self->{active} ;
95
96
97 $method ||= $self->{'method'} ;
98#print "METHOD [$method]\n" ;
99
100 $self->{'object'}->$method( $self->{'id'} ) ;
101
102 Stem::Msg::process_queue() if defined &Stem::Msg::process_queue;
103
104 return ;
105}
106
107#################
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 ) {
119# # Debug
120# # "EVENT $event to $cell_name:$target [$object] [$method]\n" ;
121# }
122# else {
123# # Debug "EVENT $event to [$object] [$method]\n" ;
124# }
125#################
126
127
128# get all the event objects for an event type
129# this is a class sub.
130
131sub _get_events {
132
133 my( $event_type ) = @_ ;
134
135 my $events = $all_events{ $event_type } ;
136
137 return unless $events ;
138
139 return values %{$events} if wantarray ;
140
141 return $events ;
142}
143
144# initialize the subclass object for this event and store generic event
145# info.
146
147sub _build_core_event {
148
149#print "BAZ\n" ;
150
151 my( $self, $event_type ) = @_ ;
152
153
154#print "EVT [$self] [$event_type]\n" ;
155
156# call and and check the return of the core event constructor
157
158 if ( my $core_event = $self->_build() ) {
159
160# return the error if it was an error string
161
162 return $core_event unless ref $core_event ;
163
164# save the core event
165
166 $self->{core_event} = $core_event ;
167 }
168
169# mark the event type and track it
170
171 $self->{event_type} = $event_type ;
172 $all_events{ $event_type }{ $self } = $self ;
173
174 return ;
175}
176
177# these are the public versions of the support methods.
178# subclasses can provide a _method to override the stub ones in this class.
179
180sub cancel {
181
182 my( $self ) = @_ ;
183
184 $self->{'active'} = 0 ;
185 delete $self->{'object'} ;
186
187# delete the core object
188
189 if ( my $core_event = delete $self->{core_event} ) {
190
191 # call the core cancel
192
193 $self->_cancel( $core_event ) ;
194 }
195
196# delete this event from the tracking hash
197
198 delete $all_events{ $self->{event_type} }{ $self } ;
199
200 return ;
201}
202
203sub start {
204 my( $self ) = @_ ;
205
206 $self->{'active'} = 1 ;
207 $self->_start( $self->{core_event} ) ;
208
209 return ;
210}
211
212sub stop {
213 my( $self ) = @_ ;
214
215 $self->{'active'} = 0 ;
216 $self->_stop( $self->{core_event} ) ;
217
218 return ;
219}
220
221# stubs for the internal methods that subclasses should override if needed.
222
223sub _init_loop {}
224sub _build {}
225sub _start {}
226sub _stop {}
227sub _reset {}
228sub _cancel {}
229
230use Stem::Debug qw( dump_socket dump_owner dump_data ) ;
231
232sub dump_events {
233
234 print dump_data( \%all_events ) ;
235}
236
237sub dump {
238
239 my( $self ) = @_ ;
240
241 my $event_text = <<TEXT ;
242EV: $self
243ACT: $self->{'active'}
244TEXT
245
246 my $obj_dump = dump_owner $self->{'object'} ;
247 $event_text .= <<TEXT ;
248OBJ: $obj_dump
249METH: $self->{'method'}
250TEXT
251
252 if ( my $fh = $self->{'fh'} ) {
253
254 my $fh_text = dump_socket( $self->{'fh'} ) ;
255 $event_text .= <<TEXT ;
256FH: $fh_text
257TEXT
258 }
259
260 if ( $self->{event_type} eq 'timer' ) {
261
262 my $delay = $self->{delay} || 'NONE' ;
263 my $interval = $self->{interval} || 'NONE' ;
264 $event_text .= <<TEXT ;
265DELAY: $delay
266INT: $interval
267TEXT
268 }
269
270 if ( my $io_timer_event = $self->{'io_timer_event'} ) {
271
272 $event_text = "IO TIMER: >>>>>\n" . $io_timer_event->dump() .
273 "END\n";
274 }
275
276 return <<DUMP ;
277
278>>>
279$event_text<<<
280
281DUMP
282
283}
284
285#############
286# change this to a cleaner loop style which can handle more event loops and
287# try them in sequence
288#############
289
290sub _get_loop_class {
291
292 my $loop_type = $Stem::Vars::Env{ 'event_loop' } ||
293 ($^O =~ /win32/i ? 'perl' : 'event' );
294
295 $loop_type = 'perl' unless $loop_to_class{ $loop_type } ;
296 my $loop_class = "Stem::Event::$loop_to_class{ $loop_type }" ;
297
298 unless ( eval "require $loop_class" ) {
299 die "can't load $loop_class: $@" if $@ && $@ !~ /locate/ ;
300
301 $loop_type = 'perl' ;
302 eval { require Stem::Event::Perl } ;
303 die "can't load event loop Stem::Event::Perl $@" if $@ ;
304 }
305
306 # save the event loop that we loaded.
307
308 #print "using event loop [$loop_type]\n" ;
309 $Stem::Vars::Env{ 'event_loop' } = $loop_type ;
310
311 return $loop_class ;
312}
313
314
315############################################################################
316
317package Stem::Event::Plain ;
318
319BEGIN {
320 @Stem::Event::Plain::ISA = qw( Stem::Event ) ;
321}
322
323=head2 Stem::Event::Plain::new
324
325This class creates an event that will trigger a callback after all
326other pending events have been triggered.
327
328=head2 Example
329
330 $plain_event = Stem::Event::Plain->new( 'object' => $self ) ;
331
332=cut
333
334my $attr_spec_plain = [
335
336 {
337 'name' => 'object',
338 'required' => 1,
339 'type' => 'object',
340 'help' => <<HELP,
341This object gets the method callbacks
342HELP
343 },
344 {
345 'name' => 'method',
346 'default' => 'triggered',
347 'help' => <<HELP,
348This method is called on the object when the plain event is triggered
349HELP
350 },
351 {
352 'name' => 'id',
353 'help' => <<HELP,
354The id is passed to the callback method as its only argument. Use it to
355identify different instances of this object.
356HELP
357
358 },
359] ;
360
361sub new {
362
363 my( $class ) = shift ;
364
365 my $self = Stem::Class::parse_args( $attr_spec_plain, @_ ) ;
366 return $self unless ref $self ;
367
368 my $err = $self->_core_event_build( 'plain' ) ;
369 return $err if $err ;
370
371 return $self ;
372}
373
374############################################################################
375
376package Stem::Event::Signal ;
377
378BEGIN { our @ISA = qw( Stem::Event ) } ;
379
380=head2 Stem::Event::Signal::new
381
382This class creates an event that will trigger a callback whenever
383its its signal has been received.
384
385=head2 Example
386
387 $signal_event = Stem::Event::Signal->new( 'object' => $self,
388 'signal' => 'INT' ) ;
389
390 sub sig_int_handler { die "SIGINT\n" }
391
392=cut
393
394my $attr_spec_signal = [
395
396 {
397 'name' => 'object',
398 'required' => 1,
399 'type' => 'object',
400 'help' => <<HELP,
401This object gets the method callbacks
402HELP
403 },
404 {
405 'name' => 'method',
406 'help' => <<HELP,
407This method is called on the object when this event is triggered. The
408default method name for the signal NAME is 'sig_name_handler' (all lower case)
409HELP
410 },
411 {
412 'name' => 'signal',
413 'required' => 1,
414 'help' => <<HELP,
415This is the name of the signal to handle. It is used as part of the
416default handler method name.
417HELP
418 },
419 {
420 'name' => 'active',
421 'default' => 1,
422 'type' => 'boolean',
423 'help' => <<HELP,
424This flag marks the event as being active. It can be toggled with the
425start/stop methods.
426HELP
427 },
428 {
429 'name' => 'id',
430 'help' => <<HELP,
431The id is passed to the callback method as its only argument. Use it to
432identify different instances of this object.
433HELP
434
435 },
436] ;
437
438sub new {
439
440 my( $class ) = shift ;
441
442 my $self = Stem::Class::parse_args( $attr_spec_signal, @_ ) ;
443 return $self unless ref $self ;
444
445 my $signal = uc $self->{'signal'} ;
446
447 return "Unknown signal: $signal" unless exists $SIG{ $signal } ;
448
449 $self->{'method'} ||= "sig_\L${signal}_handler" ;
450 $self->{'signal'} = $signal ;
451
452 my $err = $self->_build_core_event( 'signal' ) ;
453 return $err if $err ;
454
455#print "SELF SIG $self\nPID $$\n" ;
456
457 return $self ;
458}
459
460
461############################################################################
462
463package Stem::Event::Timer ;
464
465BEGIN { our @ISA = qw( Stem::Event ) } ;
466
467=head2 Stem::Event::Timer::new
468
469This class creates an event that will trigger a callback after a time
470period has elapsed. The initial timer delay is set from the 'delay',
471'at' or 'interval' attributes in that order. If the 'interval'
472attribute is not set, the timer will cancel itself after its first
473triggering (it is a one-shot). The 'hard' attribute means that the
474next interval delay starts before the callback to the object is
475made. If a soft timer is selected (hard is 0), the delay starts after
476the callback returns. So the hard timer ignores the time taken by the
477callback and so it is a more accurate timer. The accuracy a soft timer
478is affected by how much time the callback takes.
479
480=head2 Example
481
482 $timer_event = Stem::Event::Timer->new( 'object' => $self,
483 'delay' => 5,
484 'interval' => 10 ) ;
485
486 sub timed_out { print "timer alert\n" } ;
487
488
489=cut
490
491BEGIN {
492
493my $attr_spec_timer = [
494
495 {
496 'name' => 'object',
497 'required' => 1,
498 'type' => 'object',
499 'help' => <<HELP,
500This object gets the method callbacks
501HELP
502 },
503 {
504 'name' => 'method',
505 'default' => 'timed_out',
506 'help' => <<HELP,
507This method is called on the object when the timeout is triggered
508HELP
509 },
510 {
511 'name' => 'delay',
512 'help' => <<HELP,
513Delay this amount of seconds before triggering the first time. If this
514is not set then the 'at' or 'interval' attributes will be used.
515HELP
516 },
517 {
518 'name' => 'interval',
519 'help' => <<HELP,
520Wait this time (in seconds) before any repeated triggers. If not set
521then the timer is a one-shot
522HELP
523 },
524 {
525 'name' => 'at',
526 'help' => <<HELP,
527Trigger in the future at this time (in epoch seconds). It will set the intial
528delay to the different between the current time and the 'at' time.
529HELP
530 },
531 {
532 'name' => 'hard',
533 'type' => 'boolean',
534 'default' => 0,
535 'help' => <<HELP,
536If this is set, the interval time starts when the event is
537triggered. If it is not set, the interval time starts when the object
538callback has finished. So 'hard' timers repeat closer to equal
539intervals while without 'hard' the repeat time is dependant on how
540long the callback takes.
541HELP
542 },
543 {
544 'name' => 'active',
545 'default' => 1,
546 'type' => 'boolean',
547 'help' => <<HELP,
548This flag marks the event as being active. It can be toggled with the
549start/stop methods.
550HELP
551 },
552 {
553 'name' => 'id',
554 'help' => <<HELP,
555The id is passed to the callback method as its only argument. Use it to
556identify different instances of this object.
557HELP
558
559 },
560] ;
561
562sub new {
563
564 my( $class ) = shift ;
565
566 my $self = Stem::Class::parse_args( $attr_spec_timer, @_ ) ;
567 return $self unless ref $self ;
568
569# the delay is either set, or at a future time or the interval
570
571 my $delay = exists( $self->{ 'delay' } ) ?
572 $self->{ 'delay' } :
573 exists( $self->{ 'at' } ) ?
574 $self->{ 'at' } - time() :
575 $self->{'interval'} ;
576
577#print "INT $self->{'interval'} DELAY $delay\n" ;
578
579# squawk if no delay value
580
581 return "No initial delay was specified for timer"
582 unless defined $delay ;
583
584 $self->{'delay'} = $delay ;
585 $self->{'time_left'} = $delay ;
586
587 my $err = $self->_build_core_event( 'timer' ) ;
588 return $err if $err ;
589
590##########
591# check on this logic
592#########
593
594 $self->_stop unless $self->{'active'} ;
595
596 return $self ;
597}
598
599}
600
601sub reset {
602
603 my( $self, $reset_delay ) = @_ ;
604
605 return unless $self->{'active'} ;
606
607# if we don't get passed a delay, use the interval or the delay attribute
608
609 $reset_delay ||= ($self->{'interval'}) ?
610 $self->{'interval'} : $self->{'delay'} ;
611
612# track the new delay and reset the real timer (if we are using one)
613
614 $self->{'time_left'} = $reset_delay ;
615
616 $self->_reset( $self->{core_event}, $reset_delay ) ;
617
618 return ;
619}
620
621sub timer_triggered {
622
623 my( $self ) = @_ ;
624
625#print time(), " TIMER TRIG\n" ;
626#use Carp qw( cluck ) ;
627#cluck ;
628
629# check if this is a one-shot timer
630
631 $self->cancel() unless $self->{'interval'} ;
632
633# reset the timer count before the trigger code for hard timers
634#(trigger on fixed intervals)
635
636 $self->reset( $self->{'interval'} ) if $self->{'hard'};
637
638 $self->trigger() ;
639
640# reset the timer count before the trigger code for soft timers
641#(trigger on at least fixed intervals)
642
643 $self->reset( $self->{'interval'} ) unless $self->{'hard'};
644}
645
646############################################################################
647
648####################################################################
649# common methods for the Read/Write event classes to handle the optional
650# I/O timeouts.
651# these override Stem::Event's methods and then call those via SUPER::
652
653package Stem::Event::IO ;
654
655BEGIN { our @ISA = qw( Stem::Event ) } ;
656
657sub init_io_timeout {
658
659 my( $self ) = @_ ;
660
661 my $timeout = $self->{'timeout'} ;
662 return unless $timeout ;
663
664 $self->{'io_timer_event'} = Stem::Event::Timer->new(
665 'object' => $self,
666 'interval' => $timeout,
667 ) ;
668
669 return ;
670}
671
672sub cancel {
673
674 my( $self ) = @_ ;
675
676#print "IO CANCEL $self\n" ;
677
678 if ( my $io_timer_event = delete $self->{'io_timer_event'} ) {
679 $io_timer_event->cancel() ;
680 }
681
682 $self->SUPER::cancel() ;
683
684 delete $self->{'fh'} ;
685
686 return ;
687}
688
689sub start {
690
691 my( $self ) = @_ ;
692
693 if ( my $io_timer_event = $self->{'io_timer_event'} ) {
694 $io_timer_event->start() ;
695 }
696
697 $self->SUPER::start() ;
698
699 return ;
700}
701
702sub stop {
703
704 my( $self ) = @_ ;
705
706 $self->{'active'} = 0 ;
707
708 if ( my $io_timer_event = $self->{'io_timer_event'} ) {
709 $io_timer_event->stop() ;
710 }
711
712 $self->SUPER::stop() ;
713
714 return ;
715}
716
717sub timed_out {
718
719 my( $self ) = @_ ;
720
721# $self->{log_type} = "$self->{'event_type'}_timeout" ;
722 $self->trigger( $self->{'timeout_method'} ) ;
723}
724
725#######################################################
726
727package Stem::Event::Read ;
728
729BEGIN { our @ISA = qw( Stem::Event::IO ) }
730
731=head2 Stem::Event::Read::new
732
733This class creates an event that will trigger a callback whenever
734its file descriptor has data to be read. It takes an optional timeout
735value which will trigger a callback to the object if no data has been
736read during that period.
737
738Read events are active when created - a call to the stop method is
739needed to deactivate them.
740
741=cut
742
743BEGIN {
744
745my $attr_spec_read = [
746
747 {
748 'name' => 'object',
749 'required' => 1,
750 'type' => 'object',
751 'help' => <<HELP,
752This object gets the method callbacks
753HELP
754 },
755 {
756 'name' => 'fh',
757 'required' => 1,
758 'type' => 'handle',
759 'help' => <<HELP,
760This file handle is checked if it has data to read
761HELP
762 },
763 {
764 'name' => 'timeout',
765 'help' => <<HELP,
766How long to wait (in seconds) without being readable before calling
767the timeout method
768HELP
769 },
770 {
771 'name' => 'method',
772 'default' => 'readable',
773 'help' => <<HELP,
774This method is called on the object when the file handle has data to read
775HELP
776 },
777 {
778 'name' => 'timeout_method',
779 'default' => 'read_timeout',
780 'help' => <<HELP,
781This method is called on the object when the hasn't been readable
782after the timeout period
783HELP
784 },
785 {
786 'name' => 'active',
787 'default' => 1,
788 'type' => 'boolean',
789 'help' => <<HELP,
790This flag marks the event as being active. It can be toggled with the
791start/stop methods.
792HELP
793 },
794 {
795 'name' => 'id',
796 'help' => <<HELP,
797The id is passed to the callback method as its only argument. Use it to
798identify different instances of this object.
799HELP
800
801 },
802] ;
803
804sub new {
805
806 my( $class ) = shift ;
807
808 my $self = Stem::Class::parse_args( $attr_spec_read, @_ ) ;
809 return $self unless ref $self ;
810
811# return <<ERR unless defined fileno $self->{fh} ;
812# Stem::Event::Read: $self->{fh} is not an open handle
813# ERR
814
815 my $err = $self->_build_core_event( 'read' ) ;
816 return $err if $err ;
817
818 $self->init_io_timeout() ;
819
820 return $self ;
821}
822
823}
824############################################################################
825
826package Stem::Event::Write ;
827
828BEGIN { our @ISA = qw( Stem::Event::IO ) } ;
829
830=head2 Stem::Event::Write::new
831
832This class creates an event that will trigger a callback whenever
833its file descriptor can be written to. It takes an optional timeout
834value which will trigger a callback to the object if no data has been
835written during that period.
836
837Write events are stopped when created - a call to the start method is
838needed to activate them.
839
840=cut
841
842my $attr_spec_write = [
843
844 {
845 'name' => 'object',
846 'required' => 1,
847 'type' => 'object',
848 'help' => <<HELP,
849This object gets the method callbacks
850HELP
851 },
852 {
853 'name' => 'fh',
854 'required' => 1,
855 'type' => 'handle',
856 'help' => <<HELP,
857This file handle is checked if it is writeable
858HELP
859 },
860 {
861 'name' => 'timeout',
862 'help' => <<HELP,
863How long to wait (in seconds) without being writeable before calling
864the timeout method
865HELP
866 },
867 {
868 'name' => 'method',
869 'default' => 'writeable',
870 'help' => <<HELP,
871This method is called on the object when the file handle is writeable
872HELP
873 },
874 {
875 'name' => 'timeout_method',
876 'default' => 'write_timeout',
877 'help' => <<HELP,
878This method is called on the object when the hasn't been writeable
879after the timeout period
880HELP
881 },
882 {
883 'name' => 'active',
884 'default' => 0,
885 'type' => 'boolean',
886 'help' => <<HELP,
887This flag marks the event as being active. It can be toggled with the
888start/stop methods.
889NOTE: Write events are not active by default.
890HELP
891 },
892 {
893 'name' => 'id',
894 'help' => <<HELP,
895The id is passed to the callback method as its only argument. Use it to
896identify different instances of this object.
897HELP
898
899 },
900] ;
901
902sub new {
903
904 my( $class ) = shift ;
905
906 my $self = Stem::Class::parse_args( $attr_spec_write, @_ ) ;
907 return $self unless ref $self ;
908
909 my $err = $self->_build_core_event( 'write' ) ;
910 return $err if $err ;
911
912#print $self->dump_events() ;
913
914 $self->init_io_timeout() ;
915
916 $self->stop() unless $self->{'active'} ;
917
918#print $self->dump() ;
919
920 return $self ;
921}
922
9231 ;