fixed perl event loop
[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
4932dd97 74#Stem::Event::Queue::_init_queue() if defined &Stem::Event::Queue::_init_queue ;
4536f655 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
4932dd97 319our @ISA = qw( Stem::Event ) ;
4536f655 320
321=head2 Stem::Event::Plain::new
322
323This class creates an event that will trigger a callback after all
324other pending events have been triggered.
325
326=head2 Example
327
328 $plain_event = Stem::Event::Plain->new( 'object' => $self ) ;
329
330=cut
331
332my $attr_spec_plain = [
333
334 {
335 'name' => 'object',
336 'required' => 1,
337 'type' => 'object',
338 'help' => <<HELP,
339This object gets the method callbacks
340HELP
341 },
342 {
343 'name' => 'method',
344 'default' => 'triggered',
345 'help' => <<HELP,
346This method is called on the object when the plain event is triggered
347HELP
348 },
349 {
350 'name' => 'id',
351 'help' => <<HELP,
352The id is passed to the callback method as its only argument. Use it to
353identify different instances of this object.
354HELP
355
356 },
357] ;
358
359sub new {
360
361 my( $class ) = shift ;
362
363 my $self = Stem::Class::parse_args( $attr_spec_plain, @_ ) ;
364 return $self unless ref $self ;
365
4932dd97 366 my $err = $self->_build_core_event( 'plain' ) ;
4536f655 367 return $err if $err ;
368
369 return $self ;
370}
371
372############################################################################
373
374package Stem::Event::Signal ;
375
4932dd97 376our @ISA = qw( Stem::Event ) ;
4536f655 377
378=head2 Stem::Event::Signal::new
379
380This class creates an event that will trigger a callback whenever
381its its signal has been received.
382
383=head2 Example
384
385 $signal_event = Stem::Event::Signal->new( 'object' => $self,
386 'signal' => 'INT' ) ;
387
388 sub sig_int_handler { die "SIGINT\n" }
389
390=cut
391
392my $attr_spec_signal = [
393
394 {
395 'name' => 'object',
396 'required' => 1,
397 'type' => 'object',
398 'help' => <<HELP,
399This object gets the method callbacks
400HELP
401 },
402 {
403 'name' => 'method',
404 'help' => <<HELP,
405This method is called on the object when this event is triggered. The
406default method name for the signal NAME is 'sig_name_handler' (all lower case)
407HELP
408 },
409 {
410 'name' => 'signal',
411 'required' => 1,
412 'help' => <<HELP,
413This is the name of the signal to handle. It is used as part of the
414default handler method name.
415HELP
416 },
417 {
418 'name' => 'active',
419 'default' => 1,
420 'type' => 'boolean',
421 'help' => <<HELP,
422This flag marks the event as being active. It can be toggled with the
423start/stop methods.
424HELP
425 },
426 {
427 'name' => 'id',
428 'help' => <<HELP,
429The id is passed to the callback method as its only argument. Use it to
430identify different instances of this object.
431HELP
432
433 },
434] ;
435
436sub new {
437
438 my( $class ) = shift ;
439
440 my $self = Stem::Class::parse_args( $attr_spec_signal, @_ ) ;
441 return $self unless ref $self ;
442
443 my $signal = uc $self->{'signal'} ;
444
445 return "Unknown signal: $signal" unless exists $SIG{ $signal } ;
446
447 $self->{'method'} ||= "sig_\L${signal}_handler" ;
448 $self->{'signal'} = $signal ;
449
450 my $err = $self->_build_core_event( 'signal' ) ;
451 return $err if $err ;
452
453#print "SELF SIG $self\nPID $$\n" ;
454
455 return $self ;
456}
457
458
459############################################################################
460
461package Stem::Event::Timer ;
462
4932dd97 463our @ISA = qw( Stem::Event ) ;
4536f655 464
465=head2 Stem::Event::Timer::new
466
467This class creates an event that will trigger a callback after a time
468period has elapsed. The initial timer delay is set from the 'delay',
469'at' or 'interval' attributes in that order. If the 'interval'
470attribute is not set, the timer will cancel itself after its first
471triggering (it is a one-shot). The 'hard' attribute means that the
472next interval delay starts before the callback to the object is
473made. If a soft timer is selected (hard is 0), the delay starts after
474the callback returns. So the hard timer ignores the time taken by the
475callback and so it is a more accurate timer. The accuracy a soft timer
476is affected by how much time the callback takes.
477
478=head2 Example
479
480 $timer_event = Stem::Event::Timer->new( 'object' => $self,
481 'delay' => 5,
482 'interval' => 10 ) ;
483
484 sub timed_out { print "timer alert\n" } ;
485
486
487=cut
488
489BEGIN {
490
491my $attr_spec_timer = [
492
493 {
494 'name' => 'object',
495 'required' => 1,
496 'type' => 'object',
497 'help' => <<HELP,
498This object gets the method callbacks
499HELP
500 },
501 {
502 'name' => 'method',
503 'default' => 'timed_out',
504 'help' => <<HELP,
505This method is called on the object when the timeout is triggered
506HELP
507 },
508 {
509 'name' => 'delay',
510 'help' => <<HELP,
511Delay this amount of seconds before triggering the first time. If this
512is not set then the 'at' or 'interval' attributes will be used.
513HELP
514 },
515 {
516 'name' => 'interval',
517 'help' => <<HELP,
518Wait this time (in seconds) before any repeated triggers. If not set
519then the timer is a one-shot
520HELP
521 },
522 {
523 'name' => 'at',
524 'help' => <<HELP,
525Trigger in the future at this time (in epoch seconds). It will set the intial
526delay to the different between the current time and the 'at' time.
527HELP
528 },
529 {
530 'name' => 'hard',
531 'type' => 'boolean',
532 'default' => 0,
533 'help' => <<HELP,
534If this is set, the interval time starts when the event is
535triggered. If it is not set, the interval time starts when the object
536callback has finished. So 'hard' timers repeat closer to equal
537intervals while without 'hard' the repeat time is dependant on how
538long the callback takes.
539HELP
540 },
541 {
542 'name' => 'active',
543 'default' => 1,
544 'type' => 'boolean',
545 'help' => <<HELP,
546This flag marks the event as being active. It can be toggled with the
547start/stop methods.
548HELP
549 },
550 {
551 'name' => 'id',
552 'help' => <<HELP,
553The id is passed to the callback method as its only argument. Use it to
554identify different instances of this object.
555HELP
556
557 },
558] ;
559
560sub new {
561
562 my( $class ) = shift ;
563
564 my $self = Stem::Class::parse_args( $attr_spec_timer, @_ ) ;
565 return $self unless ref $self ;
566
567# the delay is either set, or at a future time or the interval
568
569 my $delay = exists( $self->{ 'delay' } ) ?
570 $self->{ 'delay' } :
571 exists( $self->{ 'at' } ) ?
572 $self->{ 'at' } - time() :
573 $self->{'interval'} ;
574
575#print "INT $self->{'interval'} DELAY $delay\n" ;
576
577# squawk if no delay value
578
579 return "No initial delay was specified for timer"
580 unless defined $delay ;
581
582 $self->{'delay'} = $delay ;
583 $self->{'time_left'} = $delay ;
584
585 my $err = $self->_build_core_event( 'timer' ) ;
586 return $err if $err ;
587
588##########
589# check on this logic
590#########
591
592 $self->_stop unless $self->{'active'} ;
593
594 return $self ;
595}
596
597}
598
599sub reset {
600
601 my( $self, $reset_delay ) = @_ ;
602
603 return unless $self->{'active'} ;
604
605# if we don't get passed a delay, use the interval or the delay attribute
606
607 $reset_delay ||= ($self->{'interval'}) ?
608 $self->{'interval'} : $self->{'delay'} ;
609
610# track the new delay and reset the real timer (if we are using one)
611
612 $self->{'time_left'} = $reset_delay ;
613
614 $self->_reset( $self->{core_event}, $reset_delay ) ;
615
616 return ;
617}
618
619sub timer_triggered {
620
621 my( $self ) = @_ ;
622
623#print time(), " TIMER TRIG\n" ;
624#use Carp qw( cluck ) ;
625#cluck ;
626
627# check if this is a one-shot timer
628
629 $self->cancel() unless $self->{'interval'} ;
630
631# reset the timer count before the trigger code for hard timers
632#(trigger on fixed intervals)
633
634 $self->reset( $self->{'interval'} ) if $self->{'hard'};
635
636 $self->trigger() ;
637
638# reset the timer count before the trigger code for soft timers
639#(trigger on at least fixed intervals)
640
641 $self->reset( $self->{'interval'} ) unless $self->{'hard'};
642}
643
644############################################################################
645
646####################################################################
647# common methods for the Read/Write event classes to handle the optional
648# I/O timeouts.
649# these override Stem::Event's methods and then call those via SUPER::
650
651package Stem::Event::IO ;
652
4932dd97 653our @ISA = qw( Stem::Event ) ;
4536f655 654
655sub init_io_timeout {
656
657 my( $self ) = @_ ;
658
659 my $timeout = $self->{'timeout'} ;
660 return unless $timeout ;
661
662 $self->{'io_timer_event'} = Stem::Event::Timer->new(
663 'object' => $self,
664 'interval' => $timeout,
665 ) ;
666
667 return ;
668}
669
670sub cancel {
671
672 my( $self ) = @_ ;
673
674#print "IO CANCEL $self\n" ;
675
676 if ( my $io_timer_event = delete $self->{'io_timer_event'} ) {
677 $io_timer_event->cancel() ;
678 }
679
680 $self->SUPER::cancel() ;
681
682 delete $self->{'fh'} ;
683
684 return ;
685}
686
687sub start {
688
689 my( $self ) = @_ ;
690
691 if ( my $io_timer_event = $self->{'io_timer_event'} ) {
692 $io_timer_event->start() ;
693 }
694
695 $self->SUPER::start() ;
696
697 return ;
698}
699
700sub stop {
701
702 my( $self ) = @_ ;
703
704 $self->{'active'} = 0 ;
705
706 if ( my $io_timer_event = $self->{'io_timer_event'} ) {
707 $io_timer_event->stop() ;
708 }
709
710 $self->SUPER::stop() ;
711
712 return ;
713}
714
715sub timed_out {
716
717 my( $self ) = @_ ;
718
719# $self->{log_type} = "$self->{'event_type'}_timeout" ;
720 $self->trigger( $self->{'timeout_method'} ) ;
721}
722
723#######################################################
724
725package Stem::Event::Read ;
726
4932dd97 727our @ISA = qw( Stem::Event::IO ) ;
4536f655 728
729=head2 Stem::Event::Read::new
730
731This class creates an event that will trigger a callback whenever
732its file descriptor has data to be read. It takes an optional timeout
733value which will trigger a callback to the object if no data has been
734read during that period.
735
736Read events are active when created - a call to the stop method is
737needed to deactivate them.
738
739=cut
740
741BEGIN {
742
743my $attr_spec_read = [
744
745 {
746 'name' => 'object',
747 'required' => 1,
748 'type' => 'object',
749 'help' => <<HELP,
750This object gets the method callbacks
751HELP
752 },
753 {
754 'name' => 'fh',
755 'required' => 1,
756 'type' => 'handle',
757 'help' => <<HELP,
758This file handle is checked if it has data to read
759HELP
760 },
761 {
762 'name' => 'timeout',
763 'help' => <<HELP,
764How long to wait (in seconds) without being readable before calling
765the timeout method
766HELP
767 },
768 {
769 'name' => 'method',
770 'default' => 'readable',
771 'help' => <<HELP,
772This method is called on the object when the file handle has data to read
773HELP
774 },
775 {
776 'name' => 'timeout_method',
777 'default' => 'read_timeout',
778 'help' => <<HELP,
779This method is called on the object when the hasn't been readable
780after the timeout period
781HELP
782 },
783 {
784 'name' => 'active',
785 'default' => 1,
786 'type' => 'boolean',
787 'help' => <<HELP,
788This flag marks the event as being active. It can be toggled with the
789start/stop methods.
790HELP
791 },
792 {
793 'name' => 'id',
794 'help' => <<HELP,
795The id is passed to the callback method as its only argument. Use it to
796identify different instances of this object.
797HELP
798
799 },
800] ;
801
802sub new {
803
804 my( $class ) = shift ;
805
806 my $self = Stem::Class::parse_args( $attr_spec_read, @_ ) ;
807 return $self unless ref $self ;
808
809# return <<ERR unless defined fileno $self->{fh} ;
810# Stem::Event::Read: $self->{fh} is not an open handle
811# ERR
812
813 my $err = $self->_build_core_event( 'read' ) ;
814 return $err if $err ;
815
816 $self->init_io_timeout() ;
817
818 return $self ;
819}
820
821}
822############################################################################
823
824package Stem::Event::Write ;
825
4932dd97 826our @ISA = qw( Stem::Event::IO ) ;
4536f655 827
828=head2 Stem::Event::Write::new
829
830This class creates an event that will trigger a callback whenever
831its file descriptor can be written to. It takes an optional timeout
832value which will trigger a callback to the object if no data has been
833written during that period.
834
835Write events are stopped when created - a call to the start method is
836needed to activate them.
837
838=cut
839
840my $attr_spec_write = [
841
842 {
843 'name' => 'object',
844 'required' => 1,
845 'type' => 'object',
846 'help' => <<HELP,
847This object gets the method callbacks
848HELP
849 },
850 {
851 'name' => 'fh',
852 'required' => 1,
853 'type' => 'handle',
854 'help' => <<HELP,
855This file handle is checked if it is writeable
856HELP
857 },
858 {
859 'name' => 'timeout',
860 'help' => <<HELP,
861How long to wait (in seconds) without being writeable before calling
862the timeout method
863HELP
864 },
865 {
866 'name' => 'method',
867 'default' => 'writeable',
868 'help' => <<HELP,
869This method is called on the object when the file handle is writeable
870HELP
871 },
872 {
873 'name' => 'timeout_method',
874 'default' => 'write_timeout',
875 'help' => <<HELP,
876This method is called on the object when the hasn't been writeable
877after the timeout period
878HELP
879 },
880 {
881 'name' => 'active',
882 'default' => 0,
883 'type' => 'boolean',
884 'help' => <<HELP,
885This flag marks the event as being active. It can be toggled with the
886start/stop methods.
887NOTE: Write events are not active by default.
888HELP
889 },
890 {
891 'name' => 'id',
892 'help' => <<HELP,
893The id is passed to the callback method as its only argument. Use it to
894identify different instances of this object.
895HELP
896
897 },
898] ;
899
900sub new {
901
902 my( $class ) = shift ;
903
904 my $self = Stem::Class::parse_args( $attr_spec_write, @_ ) ;
905 return $self unless ref $self ;
906
907 my $err = $self->_build_core_event( 'write' ) ;
908 return $err if $err ;
909
910#print $self->dump_events() ;
911
912 $self->init_io_timeout() ;
913
914 $self->stop() unless $self->{'active'} ;
915
916#print $self->dump() ;
917
918 return $self ;
919}
920
9211 ;