Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add an example of handling POE sig('DIE') from Reflex.
This is adapted from fREW Schmidt's test program posted to irc.perl.org #poe. He asked whether a timer could handle exceptions from its own callbacks, and this is an attempt to implement it in terms of POE's SIGDIE exception handling.
- Loading branch information
Showing
1 changed file
with
91 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,91 @@ | |||
#!/usr/bin/perl | |||
|
|||
# Build a new kind of Interval timer with a POE exception handler | |||
# built into it. Jams the Reflex role for an interval timer together | |||
# with the role for a signal handler. | |||
# | |||
# The goal is to have a convenient interval timer which can safely | |||
# expose exceptions thrown from its callbacks. It's based on | |||
# http://blog.afoolishmanifesto.com/archives/1682 and a question from | |||
# fREW Schmidt on irc.perl.org #poe. | |||
# | |||
# Unfortunately lower-level event dispatchers (i.e., POE and the event | |||
# loops it uses) don't know enough about higher-level consumers like | |||
# Reflex to map exceptions back to specific objects. The | |||
# RobustInterval catches die() globally via $SIG{__DIE__}. It doesn't | |||
# know which object died, so it reports unhandled exceptions from | |||
# anywhere. | |||
# | |||
# We need to think more carefully about what it means to throw | |||
# exceptions from Reflex event handler callbacks. What extensible, | |||
# sane things can be done with those exceptions? | |||
|
|||
{ | |||
|
|||
package RobustInterval; | |||
|
|||
use Moose; | |||
extends 'Reflex::Base'; | |||
use Reflex::Callbacks qw(make_emitter); | |||
|
|||
# Incorporate an interval timer. | |||
|
|||
has interval => (isa => 'Num', is => 'rw'); | |||
has auto_repeat => (isa => 'Bool', is => 'rw', default => 1); | |||
has auto_start => (isa => 'Bool', is => 'ro', default => 1); | |||
|
|||
with 'Reflex::Role::Interval' => { | |||
att_auto_repeat => "auto_repeat", | |||
att_auto_start => "auto_start", | |||
att_interval => "interval", | |||
cb_tick => make_emitter(on_tick => "tick"), | |||
method_repeat => "repeat_interval", | |||
method_start => "start_interval", | |||
method_stop => "stop_interval", | |||
}; | |||
|
|||
# Incorporate a signal watcher. | |||
|
|||
has signal => (is => 'ro', isa => 'Str', default => 'DIE'); | |||
has active => (is => 'ro', isa => 'Bool', default => 1); | |||
|
|||
with 'Reflex::Role::SigCatcher' => { | |||
att_signal => 'signal', | |||
att_active => 'active', | |||
cb_signal => make_emitter(on_die => "die"), | |||
method_start => 'start_signal', | |||
method_stop => 'stop_signal', | |||
method_pause => 'pause_signal', | |||
method_resume => 'resume_signal', | |||
}; | |||
} | |||
|
|||
### Main. | |||
|
|||
use warnings; | |||
use strict; | |||
|
|||
use Reflex; | |||
|
|||
sub event { | |||
print "looped\n"; | |||
die "lol" if rand() < .5; | |||
} | |||
|
|||
sub stumble { | |||
my ($self) = @_; | |||
|
|||
warn "$self callback died... stumbling on"; | |||
|
|||
$self->resume_signal(); # Resume watching for signals. | |||
$self->repeat_interval(); # Continue the timer. | |||
} | |||
|
|||
my $ct = RobustInterval->new( | |||
interval => 1, | |||
auto_repeat => 1, | |||
on_tick => \&event, | |||
on_die => \&stumble, | |||
); | |||
|
|||
Reflex->run_all(); |