POE
view release on metacpan or search on metacpan
lib/POE/Kernel.pm view on Meta::CPAN
sub _explain_usage {
my ($self, $message) = @_;
local $Carp::CarpLevel = 2;
ASSERT_USAGE and _confess "<us> $message";
ASSERT_RETVALS and _confess "<rv> $message";
TRACE_RETVALS and _carp "<rv> $message";
}
#==============================================================================
# SIGNALS
#==============================================================================
#------------------------------------------------------------------------------
# Register or remove signals.
# Public interface for adding or removing signal handlers.
sub sig {
my ($self, $signal, $event_name, @args) = ($poe_kernel, @_[1..$#_]);
if (ASSERT_USAGE) {
_confess "<us> must call sig() from a running session"
if $kr_active_session == $self;
_confess "<us> undefined signal in sig()" unless defined $signal;
_carp(
"<us> The '$event_name' event is one of POE's own. Its " .
"effect cannot be achieved assigning it to a signal"
) if defined($event_name) and exists($poes_own_events{$event_name});
};
if (defined $event_name) {
$self->_data_sig_add($kr_active_session, $signal, $event_name, \@args);
}
else {
$self->_data_sig_remove($kr_active_session->ID, $signal);
}
}
# Public interface for posting signal events.
# TODO - Like post(), signal() should return
sub signal {
my ($self, $dest_session, $signal, @etc) = ($poe_kernel, @_[1..$#_]);
if (ASSERT_USAGE) {
_confess "<us> undefined destination in signal()"
unless defined $dest_session;
_confess "<us> undefined signal in signal()" unless defined $signal;
};
my $session = $self->_resolve_session($dest_session);
unless (defined $session) {
$self->_explain_resolve_failure($dest_session);
return;
}
$self->_data_ev_enqueue(
$session, $kr_active_session,
EN_SIGNAL, ET_SIGNAL, [ $signal, @etc ],
(caller)[1,2], $kr_active_event
);
return 1;
}
# Public interface for flagging signals as handled. This will replace
# the handlers' return values as an implicit flag. Returns undef so
# it may be used as the last function in an event handler.
sub sig_handled {
my $self = $poe_kernel;
$self->_data_sig_handled();
if ($kr_active_event eq EN_SIGNAL) {
_die(
",----- DEPRECATION ERROR -----\n",
"| ", $self->_data_alias_loggable($kr_active_session->ID), ":\n",
"| handled a _signal event. You must register a handler with sig().\n",
"`-----------------------------\n",
);
}
}
# Attach a window or widget's destroy/closure to the UIDESTROY signal.
sub signal_ui_destroy {
my ($self, $window) = @_;
$self->loop_attach_uidestroy($window);
}
# Handle child PIDs being reaped. Added 2006-09-15.
sub sig_child {
my ($self, $pid, $event_name, @args) = ($poe_kernel, @_[1..$#_]);
if (ASSERT_USAGE) {
_confess "<us> must call sig_chld() from a running session"
if $kr_active_session == $self;
_confess "<us> undefined process ID in sig_chld()" unless defined $pid;
_carp(
"<us> The '$event_name' event is one of POE's own. Its " .
"effect cannot be achieved assigning it to a signal"
) if defined($event_name) and exists($poes_own_events{$event_name});
};
if (defined $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->ID, $pid)) {
$self->_data_sig_pid_ignore($kr_active_session->ID, $pid);
}
}
#==============================================================================
# KERNEL
#==============================================================================
sub new {
my $type = shift;
# Prevent multiple instances, no matter how many times it's called.
lib/POE/Kernel.pm view on Meta::CPAN
unless($self->_data_ses_exists($new_sid)) {
if(TRACE_SESSIONS) {
_warn("<ss> ", $loggable, " disappeared during ", EN_START);
}
return $return;
}
# If the child has not detached itself---that is, if its parent is
# the currently active session---then notify the parent with a
# _child create event. Otherwise skip it, since we'd otherwise
# throw a create without a lose.
$self->_dispatch_event(
$self->_data_ses_get_parent($session->ID), $self,
EN_CHILD, ET_CHILD, [ CHILD_CREATE, $session, $return ],
__FILE__, __LINE__, undef, monotime(), -__LINE__
);
unless ($self->_data_ses_exists($new_sid)) {
if (TRACE_SESSIONS) {
_warn("<ss> ", $loggable, " disappeared during ", EN_CHILD, " dispatch");
}
return $return;
}
# Enqueue a delayed garbage-collection event so the session has time
# to do its thing before it goes.
$self->_data_ev_enqueue(
$session, $session, EN_GC, ET_GC, [],
__FILE__, __LINE__, undef
);
}
# Detach a session from its parent. This breaks the parent/child
# relationship between the current session and its parent. Basically,
# the current session is given to the Kernel session. Unlike with
# _stop, the current session's children follow their parent.
sub detach_myself {
my $self = $poe_kernel;
if (ASSERT_USAGE) {
_confess "<us> must call detach_myself() from a running session"
if $kr_active_session == $self;
}
# Can't detach from the kernel.
if ($self->_data_ses_get_parent($kr_active_session->ID) == $self) {
$! = EPERM;
return;
}
my $old_parent = $self->_data_ses_get_parent($kr_active_session->ID);
# Tell the old parent session that the child is departing.
# But not if the active event is ET_START, since that would generate
# a CHILD_LOSE without a CHILD_CREATE.
$self->_dispatch_event(
$old_parent, $self,
EN_CHILD, ET_CHILD, [ CHILD_LOSE, $kr_active_session, undef ],
(caller)[1,2], undef, monotime(), -__LINE__
)
unless $kr_active_event_type & ET_START;
# Tell the new parent (kernel) that it's gaining a child.
# (Actually it doesn't care, so we don't do that here, but this is
# where the code would go if it ever does in the future.)
# Tell the current session that its parentage is changing.
$self->_dispatch_event(
$kr_active_session, $self,
EN_PARENT, ET_PARENT, [ $old_parent, $self ],
(caller)[1,2], undef, monotime(), -__LINE__
);
$self->_data_ses_move_child($kr_active_session->ID, $self->ID);
# Success!
return 1;
}
# Detach a child from this, the parent. The session being detached
# must be a child of the current session.
sub detach_child {
my ($self, $child) = ($poe_kernel, @_[1..$#_]);
if (ASSERT_USAGE) {
_confess "<us> must call detach_child() from a running session"
if $kr_active_session == $self;
}
my $child_session = $self->_resolve_session($child);
unless (defined $child_session) {
$self->_explain_resolve_failure($child);
return;
}
# Can't detach if it belongs to the kernel. TODO We shouldn't need
# to check for this.
if ($kr_active_session == $self) {
$! = EPERM;
return;
}
# Can't detach if it's not a child of the current session.
unless (
$self->_data_ses_is_child($kr_active_session->ID, $child_session->ID)
) {
$! = EPERM;
return;
}
# Tell the current session that the child is departing.
$self->_dispatch_event(
$kr_active_session, $self,
EN_CHILD, ET_CHILD, [ CHILD_LOSE, $child_session, undef ],
(caller)[1,2], undef, monotime(), -__LINE__
);
# Tell the new parent (kernel) that it's gaining a child.
# (Actually it doesn't care, so we don't do that here, but this is
# where the code would go if it ever does in the future.)
# Tell the child session that its parentage is changing.
$self->_dispatch_event(
$child_session, $self,
EN_PARENT, ET_PARENT, [ $kr_active_session, $self ],
(caller)[1,2], undef, monotime(), -__LINE__
);
$self->_data_ses_move_child($child_session->ID, $self->ID);
# Success!
return 1;
}
### Helpful accessors.
sub get_active_session {
return $kr_active_session;
}
sub get_active_event {
return $kr_active_event;
}
# FIXME - Should this exist?
sub get_event_count {
return $kr_queue->get_item_count();
}
# FIXME - Should this exist?
sub get_next_event_time {
return $kr_queue->get_next_priority();
}
#==============================================================================
# EVENTS
#==============================================================================
#------------------------------------------------------------------------------
# Post an event to the queue.
sub post {
my ($self, $dest_session, $event_name, @etc) = ($poe_kernel, @_[1..$#_]);
if (ASSERT_USAGE) {
_confess "<us> destination is undefined in post()"
unless defined $dest_session;
_confess "<us> event is undefined in post()" unless defined $event_name;
_carp(
"<us> The '$event_name' event is one of POE's own. Its " .
"effect cannot be achieved by posting it"
) if exists $poes_own_events{$event_name};
};
# Attempt to resolve the destination session reference against
# various things.
my $session = $self->_resolve_session($dest_session);
unless (defined $session) {
$self->_explain_resolve_failure($dest_session);
return;
}
# Enqueue the event for "now", which simulates FIFO in our
# time-ordered queue.
$self->_data_ev_enqueue(
$session, $kr_active_session, $event_name, ET_POST, \@etc,
(caller)[1,2], $kr_active_event
);
return 1;
}
#------------------------------------------------------------------------------
# Post an event to the queue for the current session.
sub yield {
my ($self, $event_name, @etc) = ($poe_kernel, @_[1..$#_]);
if (ASSERT_USAGE) {
_confess "<us> must call yield() from a running session"
if $kr_active_session == $self;
_confess "<us> event name is undefined in yield()"
unless defined $event_name;
_carp(
"<us> The '$event_name' event is one of POE's own. Its " .
"effect cannot be achieved by yielding it"
) if exists $poes_own_events{$event_name};
};
$self->_data_ev_enqueue(
$kr_active_session, $kr_active_session, $event_name, ET_POST, \@etc,
(caller)[1,2], $kr_active_event
);
undef;
}
#------------------------------------------------------------------------------
# Call an event handler directly.
sub call {
my ($self, $dest_session, $event_name, @etc) = ($poe_kernel, @_[1..$#_]);
if (ASSERT_USAGE) {
_confess "<us> destination is undefined in call()"
unless defined $dest_session;
_confess "<us> event is undefined in call()" unless defined $event_name;
_carp(
"<us> The '$event_name' event is one of POE's own. Its " .
"effect cannot be achieved by calling it"
) if exists $poes_own_events{$event_name};
};
# Attempt to resolve the destination session reference against
# various things.
my $session = $self->_resolve_session($dest_session);
unless (defined $session) {
$self->_explain_resolve_failure($dest_session);
return;
}
# Dispatch the event right now, bypassing the queue altogether.
# This tends to be a Bad Thing to Do.
# TODO The difference between synchronous and asynchronous events
# should be made more clear in the documentation, so that people
# have a tendency not to abuse them. I discovered in xws that
# mixing the two types makes it harder than necessary to write
# deterministic programs, but the difficulty can be ameliorated if
# programmers set some base rules and stick to them.
if (wantarray) {
my @return_value = (
($session == $kr_active_session)
? $session->_invoke_state(
$session, $event_name, \@etc, (caller)[1,2],
$kr_active_event
)
: $self->_dispatch_event(
$session, $kr_active_session,
$event_name, ET_CALL, \@etc,
(caller)[1,2], $kr_active_event, monotime(), -__LINE__
)
);
$kr_exception and $self->_rethrow_kr_exception();
$! = 0;
return @return_value;
}
if (defined wantarray) {
my $return_value = (
$session == $kr_active_session
? $session->_invoke_state(
$session, $event_name, \@etc, (caller)[1,2],
$kr_active_event
)
: $self->_dispatch_event(
$session, $kr_active_session,
$event_name, ET_CALL, \@etc,
(caller)[1,2], $kr_active_event, monotime(), -__LINE__
)
);
$kr_exception and $self->_rethrow_kr_exception();
$! = 0;
return $return_value;
}
if ($session == $kr_active_session) {
$session->_invoke_state(
$session, $event_name, \@etc, (caller)[1,2],
$kr_active_event
);
}
else {
$self->_dispatch_event(
$session, $kr_active_session,
$event_name, ET_CALL, \@etc,
(caller)[1,2], $kr_active_event, monotime(), -__LINE__
);
}
$kr_exception and $self->_rethrow_kr_exception();
$! = 0;
return;
}
#==============================================================================
# DELAYED EVENTS
#==============================================================================
sub alarm {
my ($self, $event_name, $time, @etc) = ($poe_kernel, @_[1..$#_]);
if (ASSERT_USAGE) {
_confess "<us> must call alarm() from a running session"
if $kr_active_session == $self;
_confess "<us> event name is undefined in alarm()"
unless defined $event_name;
_carp(
"<us> The '$event_name' event is one of POE's own. Its " .
"effect cannot be achieved by setting an alarm for it"
) if exists $poes_own_events{$event_name};
};
unless (defined $event_name) {
$self->_explain_return("invalid parameter to alarm() call");
return EINVAL;
}
$self->_data_ev_clear_alarm_by_name($kr_active_session->ID(), $event_name);
# Add the new alarm if it includes a time. Calling _data_ev_enqueue
# directly is faster than calling alarm_set to enqueue it.
if (defined $time) {
$self->_data_ev_enqueue
( $kr_active_session, $kr_active_session,
$event_name, ET_ALARM, [ @etc ],
(caller)[1,2], $kr_active_event, $time,
);
}
else {
# The event queue has become empty? Stop the time watcher.
$self->loop_pause_time_watcher() unless $kr_queue->get_item_count();
}
return 0;
}
# Add an alarm without clobbering previous alarms of the same name.
sub alarm_add {
my ($self, $event_name, $time, @etc) = ($poe_kernel, @_[1..$#_]);
if (ASSERT_USAGE) {
_confess "<us> must call alarm_add() from a running session"
if $kr_active_session == $self;
_confess "<us> undefined event name in alarm_add()"
unless defined $event_name;
_confess "<us> undefined time in alarm_add()" unless defined $time;
_carp(
"<us> The '$event_name' event is one of POE's own. Its " .
"effect cannot be achieved by adding an alarm for it"
) if exists $poes_own_events{$event_name};
};
unless (defined $event_name and defined $time) {
$self->_explain_return("invalid parameter to alarm_add() call");
return EINVAL;
}
$self->_data_ev_enqueue
( $kr_active_session, $kr_active_session,
$event_name, ET_ALARM, [ @etc ],
(caller)[1,2], $kr_active_event, $time,
);
return 0;
}
# Add a delay, which is like an alarm relative to the current time.
sub delay {
my ($self, $event_name, $delay, @etc) = ($poe_kernel, @_[1..$#_]);
my $pri = monotime();
if (ASSERT_USAGE) {
_confess "<us> must call delay() from a running session"
if $kr_active_session == $self;
_confess "<us> undefined event name in delay()" unless defined $event_name;
_carp(
"<us> The '$event_name' event is one of POE's own. Its " .
"effect cannot be achieved by setting a delay for it"
) if exists $poes_own_events{$event_name};
};
unless (defined $event_name) {
$self->_explain_return("invalid parameter to delay() call");
return EINVAL;
}
if (defined $delay) {
$self->_data_ev_clear_alarm_by_name($kr_active_session->ID(), $event_name);
# Add the new alarm if it includes a time. Calling _data_ev_enqueue
# directly is faster than calling alarm_set to enqueue it.
$self->_data_ev_enqueue
( $kr_active_session, $kr_active_session,
$event_name, ET_ALARM, [ @etc ],
(caller)[1,2], $kr_active_event, undef, $delay, $pri+$delay
);
}
else {
$self->alarm($event_name);
}
return 0;
}
# Add a delay without clobbering previous delays of the same name.
sub delay_add {
my ($self, $event_name, $delay, @etc) = ($poe_kernel, @_[1..$#_]);
my $pri = monotime();
if (ASSERT_USAGE) {
_confess "<us> must call delay_add() from a running session"
if $kr_active_session == $self;
_confess "<us> undefined event name in delay_add()"
unless defined $event_name;
_confess "<us> undefined time in delay_add()" unless defined $delay;
_carp(
"<us> The '$event_name' event is one of POE's own. Its " .
"effect cannot be achieved by adding a delay for it"
) if exists $poes_own_events{$event_name};
};
unless (defined $event_name and defined $delay) {
$self->_explain_return("invalid parameter to delay_add() call");
return EINVAL;
}
$self->_data_ev_enqueue
( $kr_active_session, $kr_active_session,
$event_name, ET_ALARM, [ @etc ],
(caller)[1,2], $kr_active_event, undef, $delay, $pri+$delay
);
return 0;
}
#------------------------------------------------------------------------------
# New style alarms.
# Set an alarm. This does more *and* less than plain alarm(). It
# only sets alarms (that's the less part), but it also returns an
# alarm ID (that's the more part).
sub alarm_set {
my ($self, $event_name, $time, @etc) = ($poe_kernel, @_[1..$#_]);
if (ASSERT_USAGE) {
_confess "<us> must call alarm_set() from a running session"
if $kr_active_session == $self;
}
unless (defined $event_name) {
$self->_explain_usage("undefined event name in alarm_set()");
$! = EINVAL;
return;
}
unless (defined $time) {
$self->_explain_usage("undefined time in alarm_set()");
$! = EINVAL;
return;
}
if (ASSERT_USAGE) {
_carp(
"<us> The '$event_name' event is one of POE's own. Its " .
"effect cannot be achieved by setting an alarm for it"
) if exists $poes_own_events{$event_name};
}
return $self->_data_ev_enqueue
( $kr_active_session, $kr_active_session, $event_name, ET_ALARM, [ @etc ],
(caller)[1,2], $kr_active_event, $time,
);
}
# Remove an alarm by its ID. TODO Now that alarms and events have
# been recombined, this will remove an event by its ID. However,
# nothing returns an event ID, so nobody knows what to remove.
sub alarm_remove {
my ($self, $alarm_id) = ($poe_kernel, @_[1..$#_]);
if (ASSERT_USAGE) {
_confess "<us> must call alarm_remove() from a running session"
if $kr_active_session == $self;
}
unless (defined $alarm_id) {
$self->_explain_usage("undefined alarm id in alarm_remove()");
$! = EINVAL;
return;
}
my ($time, $event) =
$self->_data_ev_clear_alarm_by_id($kr_active_session->ID(), $alarm_id);
return unless defined $time;
# In a list context, return the alarm that was removed. In a scalar
# context, return a reference to the alarm that was removed. In a
# void context, return nothing. Either way this returns a defined
# value when someone needs something useful from it.
return unless defined wantarray;
return ( $event->[EV_NAME], $time, $event->[EV_ARGS] ) if wantarray;
return [ $event->[EV_NAME], $time, $event->[EV_ARGS] ];
}
# Move an alarm to a new time. This virtually removes the alarm and
# re-adds it somewhere else. In reality, adjust_priority() is
# optimized for this sort of thing.
sub alarm_adjust {
my ($self, $alarm_id, $delta) = ($poe_kernel, @_[1..$#_]);
if (ASSERT_USAGE) {
_confess "<us> must call alarm_adjust() from a running session"
if $kr_active_session == $self;
}
unless (defined $alarm_id) {
$self->_explain_usage("undefined alarm id in alarm_adjust()");
$! = EINVAL;
return;
}
unless (defined $delta) {
$self->_explain_usage("undefined alarm delta in alarm_adjust()");
$! = EINVAL;
return;
}
my $my_alarm = sub {
$_[0]->[EV_SESSION] == $kr_active_session;
};
return $self->_data_ev_adjust( $alarm_id, $my_alarm, undef, $delta );
}
# A convenient function for setting alarms relative to now. It also
# uses whichever time() POE::Kernel can find, which may be
# Time::HiRes'.
sub delay_set {
# Always always always grab time() ASAP, so that the eventual
# time we set the delay for is as close as possible to the time
# at which they ASKED for the delay, not when we actually set it.
my $t = walltime();
my $pri = monotime();
# And now continue as normal
my ($self, $event_name, $seconds, @etc) = ($poe_kernel, @_[1..$#_]);
if (ASSERT_USAGE) {
_confess "<us> must call delay_set() from a running session"
if $kr_active_session == $self;
}
unless (defined $event_name) {
$self->_explain_usage("undefined event name in delay_set()");
$! = EINVAL;
return;
}
if (ASSERT_USAGE) {
_carp(
"<us> The '$event_name' event is one of POE's own. Its " .
"effect cannot be achieved by setting a delay for it"
) if exists $poes_own_events{$event_name};
}
unless (defined $seconds) {
$self->_explain_usage("undefined seconds in delay_set()");
$! = EINVAL;
return;
}
return $self->_data_ev_enqueue
( $kr_active_session, $kr_active_session, $event_name, ET_ALARM, [ @etc ],
(caller)[1,2], $kr_active_event, $t, $seconds, $pri+$seconds
);
}
# Move a delay to a new offset from time(). As with alarm_adjust(),
# this is optimized internally for this sort of activity.
sub delay_adjust {
# Always always always grab time() ASAP, so that the eventual
# time we set the delay for is as close as possible to the time
# at which they ASKED for the delay, not when we actually set it.
my $t = walltime();
my $pri = monotime();
# And now continue as normal
my ($self, $alarm_id, $seconds) = ($poe_kernel, @_[1..$#_]);
if (ASSERT_USAGE) {
_confess "<us> must call delay_adjust() from a running session"
if $kr_active_session == $self;
}
unless (defined $alarm_id) {
$self->_explain_usage("undefined delay id in delay_adjust()");
$! = EINVAL;
return;
}
unless (defined $seconds) {
$self->_explain_usage("undefined delay seconds in delay_adjust()");
$! = EINVAL;
return;
}
my $my_delay = sub {
$_[0]->[EV_SESSION] == $kr_active_session;
};
if (TRACE_EVENTS) {
_warn("<ev> adjusted event $alarm_id by $seconds seconds from $t");
}
return $self->_data_ev_set($alarm_id, $my_delay, $t, $pri, $seconds );
}
# Remove all alarms for the current session.
sub alarm_remove_all {
my $self = $poe_kernel;
if (ASSERT_USAGE) {
_confess "<us> must call alarm_remove_all() from a running session"
if $kr_active_session == $self;
}
# This should never happen, actually.
_trap "unknown session in alarm_remove_all call" unless (
$self->_data_ses_exists($kr_active_session->ID)
);
# Free every alarm owned by the session. This code is ripped off
( run in 1.878 second using v1.01-cache-2.11-cpan-4e7a2411597 )