Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add user continuation parameters to sig() and sig_child().

Roll back most of the lexical FIFO event arrays.  Going through a
POE::Queue for this will be a bit slower, but POE::XS::Queue::Array
may be fast enough.

Continue to guarantee FIFO event ordering despite the rollback to
dumping them in a time-ordered queue.  Detect minor time regressions,
and use the last time value plus a small number when needed.
  • Loading branch information...
commit 058fb2126d9dd76ef173402ba80563c67c916dfa 1 parent a0abb62
@rcaputo authored
View
80 lib/POE/Kernel.pm
@@ -673,7 +673,7 @@ sub _test_if_kernel_is_idle {
if (TRACE_REFCNT) {
_warn(
"<rc> ,----- Kernel Activity -----\n",
- "<rc> | Events : ", $self->_data_ev_get_pending_count(),
+ "<rc> | Events : ", $kr_queue->get_item_count(),
" (vs. idle size = ", $idle_queue_size, ")\n",
"<rc> | Files : ", $self->_data_handle_count(), "\n",
"<rc> | Extra : ", $self->_data_extref_count(), "\n",
@@ -697,7 +697,7 @@ sub _test_if_kernel_is_idle {
# that the tests short-circuit quickly.
return if (
- $self->_data_ev_get_pending_count() > $idle_queue_size or
+ $kr_queue->get_item_count() > $idle_queue_size or
$self->_data_handle_count() or
$self->_data_extref_count() or
$self->_data_sig_child_procs() or
@@ -756,7 +756,7 @@ sub _explain_usage {
# Public interface for adding or removing signal handlers.
sub sig {
- my ($self, $signal, $event_name) = @_;
+ my ($self, $signal, $event_name, @args) = @_;
if (ASSERT_USAGE) {
_confess "<us> must call sig() from a running session"
@@ -769,7 +769,7 @@ sub sig {
};
if (defined $event_name) {
- $self->_data_sig_add($kr_active_session, $signal, $event_name);
+ $self->_data_sig_add($kr_active_session, $signal, $event_name, \@args);
}
else {
$self->_data_sig_remove($kr_active_session, $signal);
@@ -830,7 +830,7 @@ sub signal_ui_destroy {
# Handle child PIDs being reaped. Added 2006-09-15.
sub sig_child {
- my ($self, $pid, $event_name) = @_;
+ my ($self, $pid, $event_name, @args) = @_;
if (ASSERT_USAGE) {
_confess "<us> must call sig_chld() from a running session"
@@ -843,7 +843,7 @@ sub sig_child {
};
if (defined $event_name) {
- $self->_data_sig_pid_watch($kr_active_session, $pid, $event_name);
+ $self->_data_sig_pid_watch($kr_active_session, $pid, $event_name, \@args);
}
elsif ($self->_data_sig_pids_is_ses_watching($kr_active_session, $pid)) {
$self->_data_sig_pid_ignore($kr_active_session, $pid);
@@ -1016,12 +1016,12 @@ sub _dispatch_event {
$self->_data_sig_touched_session($target_session);
next unless exists $signal_watchers{$target_session};
- my $target_event = $signal_watchers{$target_session};
+ my ($target_event, $target_etc) = @{$signal_watchers{$target_session}};
if (TRACE_SIGNALS) {
_warn(
"<sg> propagating explicit signal $target_event ($signal) ",
- "to ", $self->_data_alias_loggable($target_session)
+ "(@$target_etc) to ", $self->_data_alias_loggable($target_session)
);
}
@@ -1029,7 +1029,7 @@ sub _dispatch_event {
# the signal ad nauseam.
$self->_dispatch_event(
$target_session, $self,
- $target_event, ET_SIGNAL_RECURSIVE, [ @$etc ],
+ $target_event, ET_SIGNAL_RECURSIVE, [ @$etc, @$target_etc ],
$file, $line, $fromstate, time(), -__LINE__
);
}
@@ -1432,7 +1432,7 @@ sub _invoke_state {
elsif ($event eq EN_SIGNAL) {
if ($etc->[0] eq 'IDLE') {
unless (
- $self->_data_ev_get_pending_count() > $idle_queue_size or
+ $kr_queue->get_item_count() > $idle_queue_size or
$self->_data_handle_count()
) {
$self->_data_ev_enqueue(
@@ -1645,12 +1645,12 @@ sub get_active_event {
# FIXME - Should this exist?
sub get_event_count {
- return shift()->_data_ev_get_pending_count();
+ return $kr_queue->get_item_count();
}
# FIXME - Should this exist?
sub get_next_event_time {
- return shift()->_data_ev_get_next_due_time();
+ return $kr_queue->get_next_priority();
}
#==============================================================================
@@ -1848,7 +1848,7 @@ sub alarm {
}
else {
# The event queue has become empty? Stop the time watcher.
- $self->loop_pause_time_watcher() unless $self->_data_ev_get_pending_count();
+ $self->loop_pause_time_watcher() unless $kr_queue->get_item_count();
}
return 0;
@@ -2865,8 +2865,6 @@ An application creates an event with L</post>, L</yield>, L</call> or
even L</signal>. POE::Kernel creates events in response external
stimulus (signals, select, etc).
-TODO - discuss the POE::Kernel queue
-
=head3 Event Handlers
An event is handled by a function called an I<event handler>, which is
@@ -4021,8 +4019,6 @@ starts or stops a watcher that looks for write-readiness. That is,
when EVENT_NAME is delivered, it means that FILE_HANDLE is ready to be
written to.
-TODO - Practical example here.
-
select_write() does not return anything significant.
=head3 select_expedite FILE_HANDLE [, EVENT_NAME [, ADDITIONAL_PARAMETERS] ]
@@ -4038,8 +4034,6 @@ because it is often ahead of a socket's normal data.
select_expedite() does not return anything significant.
-TODO - Practical example here.
-
=head3 select_pause_read FILE_HANDLE
select_pause_read() is a lightweight way to pause a FILE_HANDLE input
@@ -4054,8 +4048,6 @@ paused FILE_HANDLE will not prematurely stop the current session.
select_pause_read() does not return anything significant.
-TODO - Practical example here.
-
=head3 select_resume_read FILE_HANDLE
select_resume_read() resumes a FILE_HANDLE input watcher that was
@@ -4067,24 +4059,18 @@ call will become available after select_resume_read() is called.
select_resume_read() does not return anything significant.
-TODO - Practical example here.
-
=head3 select_pause_write FILE_HANDLE
select_pause_write() pauses a FILE_HANDLE output watcher the same way
select_pause_read() does for input. Please see select_pause_read()
for further discussion.
-TODO - Practical example here.
-
=head3 select_resume_write FILE_HANDLE
select_resume_write() resumes a FILE_HANDLE output watcher the same
way that select_resume_read() does for input. See
select_resume_read() for further discussion.
-TODO - Practical example here.
-
=head3 select FILE_HANDLE [, EV_READ [, EV_WRITE [, EV_EXPEDITE [, ARGS] ] ] ]
POE::Kernel's select() method sets or clears a FILE_HANDLE's read,
@@ -4680,13 +4666,18 @@ or
And finally the methods themselves.
-=head3 sig SIGNAL_NAME [, EVENT_NAME]
+=head3 sig SIGNAL_NAME [, EVENT_NAME [, LIST] ]
sig() registers or unregisters an EVENT_NAME event for a particular
-SIGNAL_NAME. The event is registered if EVENT_NAME is defined, otherwise
-the SIGNAL_NAME handler is unregistered. This means that a session can
-register only one handler per SIGNAL_NAME; subsequent registration attempts
-will replace the old handler.
+SIGNAL_NAME, with an optional LIST of parameters that will be passed
+to the signal's handler---after any data that comes wit the signal.
+
+If EVENT_NAME is defined, the signal handler is registered. Otherwise
+it's unregistered.
+
+Each session can register only one handler per SIGNAL_NAME.
+Subsequent registrations will replace previous ones. Multiple
+sessions may however watch the same signal.
SIGNAL_NAMEs are generally the same as members of C<%SIG>, with two
exceptions. First, C<CLD> is an alias for C<CHLD> (although see
@@ -4717,17 +4708,22 @@ other sessions.
sig() does not return a meaningful value.
-=head3 sig_child PROCESS_ID [, EVENT_NAME]
+=head3 sig_child PROCESS_ID [, EVENT_NAME [, LIST] ]
sig_child() is a convenient way to deliver an EVENT_NAME event when a
-particular PROCESS_ID has exited. The watcher can be cleared
-prematurely by calling sig_child() with just the PROCESS_ID.
+particular PROCESS_ID has exited. An optional LIST of parameters will
+be passed to the signal handler after the waitpid() information.
+
+The watcher can be cleared at any time by calling sig_child() with
+just the PROCESS_ID.
A session may register as many sig_child() handlers as necessary, but
a session may only have one per PROCESS_ID.
sig_child() watchers are one-shot. They automatically unregister
-themselves once the EVENT_NAME has been delivered.
+themselves once the EVENT_NAME has been delivered. There's no point
+in continuing to watch for a signal that will never come again. Other
+signal handlers persist until they are cleared.
sig_child() watchers keep a session alive for as long as they are
active. This is unique among signal watchers.
@@ -4741,15 +4737,15 @@ sig_child() does not return a meaningful value.
sub forked_parent {
my( $heap, $pid, $details ) = @_[ HEAP, ARG0, ARG1 ];
- $heap->{$pid} = $details;
- $poe_kernel->sig_child( $pid, 'sig_child' );
+ $poe_kernel->sig_child( $pid, 'sig_child', $details );
}
sub sig_child {
- my( $heap, $sig, $pid, $exit_val ) = @_[ HEAP, ARG0, ARG1, ARG2 ];
+ my( $heap, $sig, $pid, $exit_val, $details ) = @_[ HEAP, ARG0..ARG3 ];
my $details = delete $heap->{ $pid };
warn "$$: Child $pid exited"
- # ....
+ # .... also, $details has been passed from forked_parent()
+ # through sig_child()
}
=head3 sig_handled
@@ -4829,10 +4825,6 @@ thereafter.
Detecting widget destruction is specific to each toolkit.
-=head3 TODO
-
-TODO - See if there is anything to migrate over from POE::Session?
-
=head2 Event Handler Management
Event handler management methods let sessions hot swap their event
View
101 lib/POE/Resource/Events.pm
@@ -48,8 +48,8 @@ sub _data_ev_finalize {
### Enqueue an event.
-my @new_fifo;
-my @old_fifo;
+sub FIFO_TIME_EPSILON () { 0.000001 }
+my $last_fifo_time = time();
sub _data_ev_enqueue {
my (
@@ -70,15 +70,15 @@ sub _data_ev_enqueue {
my $event_to_enqueue = [ @_[1..8] ];
my $new_id;
- my $old_head_priority = $self->_data_ev_get_next_due_time();
+ my $old_head_priority = $kr_queue->get_next_priority();
- if ($type & ET_MASK_DELAYED) {
- $new_id = $kr_queue->enqueue($time, $event_to_enqueue);
- }
- else {
- push @new_fifo, [ $time, $new_id = -1, $event_to_enqueue ];
+ unless ($type & ET_MASK_DELAYED) {
+ $time = $last_fifo_time + FIFO_TIME_EPSILON if $time <= $last_fifo_time;
+ $last_fifo_time = $time;
}
+ $new_id = $kr_queue->enqueue($time, $event_to_enqueue);
+
if (TRACE_EVENTS) {
_warn(
"<ev> enqueued event $new_id ``$event'' from ",
@@ -133,33 +133,6 @@ sub _data_ev_clear_session {
last PENDING unless $pending_count;
- if (@old_fifo) {
- my $i = @old_fifo;
- while ($i--) {
- next unless $old_fifo[$i][2][EV_SESSION] == $session;
- my $event = splice(@old_fifo, $i, 1);
- $self->_data_ev_refcount_dec(
- $event->[2][EV_SOURCE],
- $event->[2][EV_SESSION],
- );
- $pending_count--;
- }
- last PENDING unless $pending_count;
- }
-
- if (@new_fifo) {
- my $i = @new_fifo;
- while ($i--) {
- next unless $new_fifo[$i][2][EV_SESSION] == $session;
- my $event = splice(@new_fifo, $i, 1);
- $self->_data_ev_refcount_dec(
- $event->[2][EV_SOURCE],
- $event->[2][EV_SESSION],
- );
- $pending_count--;
- }
- }
-
croak "lingering pending count: $pending_count" if $pending_count;
}
@@ -182,33 +155,6 @@ sub _data_ev_clear_session {
last SENT unless $sent_count;
- if (@old_fifo) {
- my $i = @old_fifo;
- while ($i--) {
- next unless $old_fifo[$i][2][EV_SOURCE] == $session;
- my $event = splice(@old_fifo, $i, 1);
- $self->_data_ev_refcount_dec(
- $event->[2][EV_SOURCE],
- $event->[2][EV_SESSION],
- );
- $sent_count--;
- }
- last SENT unless $sent_count;
- }
-
- if (@new_fifo) {
- my $i = @new_fifo;
- while ($i--) {
- next unless $new_fifo[$i][2][EV_SOURCE] == $session;
- my $event = splice(@new_fifo, $i, 1);
- $self->_data_ev_refcount_dec(
- $event->[2][EV_SOURCE],
- $event->[2][EV_SESSION],
- );
- $sent_count--;
- }
- }
-
croak "lingering sent count: $sent_count" if $sent_count;
}
@@ -311,16 +257,6 @@ sub _data_ev_refcount_dec {
);
}
-sub _data_ev_get_pending_count {
- return @old_fifo + @new_fifo + $kr_queue->get_item_count();
-}
-
-sub _data_ev_get_next_due_time {
- return $old_fifo[0][0] if @old_fifo;
- return $new_fifo[0][0] if @new_fifo;
- return $kr_queue->get_next_priority();
-}
-
### Fetch the number of pending events sent to a session.
sub _data_ev_get_count_to {
@@ -350,25 +286,6 @@ sub _data_ev_dispatch_due {
}
}
- @old_fifo = splice(@new_fifo);
- while (@old_fifo) {
- my ($due_time, $id, $event) = @{shift @old_fifo};
-
- if (TRACE_EVENTS) {
- _warn("<ev> dispatching event $id ($event->[EV_NAME])");
- }
-
- # TODO - Why can't we reverse these two lines?
- # TODO - Reversing them could avoid entering and removing GC marks.
- $self->_data_ev_refcount_dec($event->[EV_SOURCE], $event->[EV_SESSION]);
- $self->_dispatch_event(@$event, $due_time, $id);
-
- # Stop the system if an unhandled exception occurred.
- # This wipes out all sessions and associated resources.
- next unless $POE::Kernel::kr_exception;
- POE::Kernel->stop();
- }
-
my $now = time();
my $next_time;
while (
@@ -407,7 +324,7 @@ sub _data_ev_dispatch_due {
# Sweep for dead sessions. The sweep may alter the next queue time.
$self->_data_ses_gc_sweep();
- $next_time = $self->_data_ev_get_next_due_time();
+ $next_time = $kr_queue->get_next_priority();
# Tell the event loop to wait for the next event, if there is one.
# Otherwise we're going to wait indefinitely for some other event.
View
33 lib/POE/Resource/Signals.pm
@@ -20,7 +20,7 @@ use POSIX qw(:sys_wait_h sigprocmask SIG_SETMASK);
my %kr_signals;
# ( $signal_name =>
-# { $session_reference => $event_name,
+# { $session_reference => [ $event_name, $event_args, ],
# ...,
# },
# ...,
@@ -28,7 +28,7 @@ my %kr_signals;
my %kr_sessions_to_signals;
# ( $session =>
-# { $signal_name => $event_name,
+# { $signal_name => [ $event_name, $event_args ],
# ...,
# },
# ...,
@@ -39,6 +39,7 @@ my %kr_pids_to_events;
# { $session =>
# [ $blessed_session, # PID_SESSION
# $event_name, # PID_EVENT
+# $args, # PID_ARGS
# ]
# }
# }
@@ -48,6 +49,7 @@ my %kr_sessions_to_pids;
sub PID_SESSION () { 0 }
sub PID_EVENT () { 1 }
+sub PID_ARGS () { 2 }
# Bookkeeping per dispatched signal.
@@ -184,16 +186,18 @@ sub _data_sig_finalize {
while (my ($sig, $sig_rec) = each(%kr_signals)) {
$finalized_ok = 0;
_warn "!!! Leaked signal $sig\n";
- while (my ($ses, $event) = each(%{$kr_signals{$sig}})) {
- _warn "!!!\t$ses = $event\n";
+ while (my ($ses, $ses_rec) = each(%{$kr_signals{$sig}})) {
+ my ($event, $args) = @$ses_rec;
+ _warn "!!!\t$ses = $event (@$args)\n";
}
}
- while (my ($ses, $sig_rec) = each(%kr_sessions_to_signals)) {
+ while (my ($ses, $ses_rec) = each(%kr_sessions_to_signals)) {
$finalized_ok = 0;
_warn "!!! Leaked signal cross-reference: $ses\n";
- while (my ($sig, $event) = each(%{$kr_signals{$ses}})) {
- _warn "!!!\t$sig = $event\n";
+ while (my ($sig, $sig_rec) = each(%{$kr_signals{$ses}})) {
+ my ($event, $args) = @$sig_rec;
+ _warn "!!!\t$sig = $event (@$args)\n";
}
}
@@ -206,8 +210,8 @@ sub _data_sig_finalize {
while (my ($pid, $ses_rec) = each(%kr_pids_to_events)) {
$finalized_ok = 0;
_warn "!!! Leaked PID to event map: $pid\n";
- while (my ($ses, $event_rec) = each %$ses_rec) {
- _warn "!!!\t$ses -> $event_rec->[PID_EVENT]\n";
+ while (my ($ses, $ev_rec) = each %$ses_rec) {
+ _warn "!!!\t$ses -> $ev_rec->[PID_EVENT] (@{$ev_rec->[PID_ARGS]})\n";
}
}
@@ -240,11 +244,11 @@ sub _data_sig_finalize {
### Add a signal to a session.
sub _data_sig_add {
- my ($self, $session, $signal, $event) = @_;
+ my ($self, $session, $signal, $event, $args) = @_;
- $kr_sessions_to_signals{$session}->{$signal} = $event;
+ $kr_sessions_to_signals{$session}->{$signal} = [ $event, $args || [] ];
$self->_data_sig_signal_watch($session, $signal);
- $kr_signals{$signal}->{$session} = $event;
+ $kr_signals{$signal}->{$session} = [ $event, $args || [] ];
}
sub _data_sig_signal_watch {
@@ -316,11 +320,12 @@ sub _data_sig_clear_session {
### Watch and ignore PIDs.
sub _data_sig_pid_watch {
- my ($self, $session, $pid, $event) = @_;
+ my ($self, $session, $pid, $event, $args) = @_;
$kr_pids_to_events{$pid}{$session} = [
$session, # PID_SESSION
$event, # PID_EVENT
+ $args, # PID_ARGS
];
$self->_data_sig_signal_watch($session, "CHLD");
@@ -551,7 +556,7 @@ sub _data_sig_handle_poll_event {
while (my ($ses_key, $ses_rec) = each %{$kr_pids_to_events{$pid}}) {
$self->_data_ev_enqueue(
$ses_rec->[PID_SESSION], $self, $ses_rec->[PID_EVENT], ET_SIGCLD,
- [ 'CHLD', $pid, $? ],
+ [ 'CHLD', $pid, $?, @{$ses_rec->[PID_ARGS]} ],
__FILE__, __LINE__, undef, time(),
);
push @sessions_to_clear, $ses_rec->[PID_SESSION];
View
26 t/20_resources/00_base/signals.pm
@@ -46,8 +46,8 @@ sub create_session {
# Add some signals for testing.
my ($ses_1, $sid_1) = create_session();
-$poe_kernel->_data_sig_add($ses_1, "signal-1", "event-1");
-$poe_kernel->_data_sig_add($ses_1, "signal-2", "event-2");
+$poe_kernel->_data_sig_add($ses_1, "signal-1", "event-1", [ 1, 2, 3 ]);
+$poe_kernel->_data_sig_add($ses_1, "signal-2", "event-2", [ 4, 5, 6 ]);
my ($ses_2, $sid_2) = create_session();
$poe_kernel->_data_sig_add($ses_2, "signal-2", "event-3");
@@ -93,7 +93,7 @@ ok(
# Single watcher test...
{ my %watchers = $poe_kernel->_data_sig_watchers("signal-1");
ok(
- eq_hash(\%watchers, { $ses_1 => "event-1" }),
+ eq_hash(\%watchers, { $ses_1 => [ "event-1", [ 1, 2, 3 ] ] }),
"signal-1 maps to session 1 and event-1"
);
}
@@ -103,8 +103,8 @@ ok(
ok(
eq_hash(
\%watchers, {
- $ses_1 => "event-2",
- $ses_2 => "event-3",
+ $ses_1 => [ "event-2", [ 4, 5, 6 ] ],
+ $ses_2 => [ "event-3", [ ] ],
}
),
"signal-2 maps to session 1 and event-2; session 2 and event-3"
@@ -120,7 +120,7 @@ $poe_kernel->_data_sig_remove($ses_1, "signal-2");
{ my %watchers = $poe_kernel->_data_sig_watchers("signal-1");
ok(
- eq_hash(\%watchers, { $ses_1 => "event-1" }),
+ eq_hash(\%watchers, { $ses_1 => [ "event-1", [ 1, 2, 3 ] ] }),
"signal-1 still maps to session 1 and event-1"
);
}
@@ -129,7 +129,7 @@ $poe_kernel->_data_sig_remove($ses_1, "signal-2");
{ my %watchers = $poe_kernel->_data_sig_watchers("signal-2");
ok(
- eq_hash(\%watchers, { $ses_2 => "event-3" }),
+ eq_hash(\%watchers, { $ses_2 => [ "event-3", [ ] ] }),
"signal-2 still maps to session 2 and event-3"
);
}
@@ -147,11 +147,11 @@ $poe_kernel->_data_sig_add($ses_1, "signal-6", "event-3");
ok(
eq_hash(
\%watchers,
- { "signal-1", "event-1",
- "signal-3", "event-3",
- "signal-4", "event-3",
- "signal-5", "event-3",
- "signal-6", "event-3",
+ { "signal-1", [ "event-1", [ 1, 2, 3 ] ],
+ "signal-3", [ "event-3", [ ] ],
+ "signal-4", [ "event-3", [ ] ],
+ "signal-5", [ "event-3", [ ] ],
+ "signal-6", [ "event-3", [ ] ],
}
),
"several signal watchers were added correctly"
@@ -162,7 +162,7 @@ $poe_kernel->_data_sig_clear_session($ses_1);
{ my %watchers = $poe_kernel->_data_sig_watchers("signal-2");
ok(
- eq_hash(\%watchers, { $ses_2 => "event-3" }),
+ eq_hash(\%watchers, { $ses_2 => [ "event-3", [ ] ] }),
"cleared session isn't watching signal-2"
);
}
Please sign in to comment.
Something went wrong with that request. Please try again.