File Coverage

blib/lib/POE/Kernel.pm
Criterion Covered Total %
statement 691 770 89.7
branch 239 286 83.5
condition 58 93 62.3
subroutine 101 112 90.1
pod 49 49 100.0
total 1138 1310 86.8


line stmt bran cond sub pod time code
1             package POE::Kernel;
2              
3 177     177   92416 use strict;
  177         209  
  177         6439  
4              
5 177     177   696 use vars qw($VERSION);
  177         228  
  177         9027  
6             $VERSION = '1.365'; # NOTE - Should be #.### (three decimal places)
7              
8 177     177   4413 use POE::Resource::Clock qw( monotime sleep mono2wall wall2mono walltime time );
  177         295  
  177         11099  
9              
10 177     177   851 use POSIX qw(uname);
  177         237  
  177         1275  
11 177     177   42504 use Errno qw(ESRCH EINTR ECHILD EPERM EINVAL EEXIST EAGAIN EWOULDBLOCK);
  177         279  
  177         11900  
12 177     177   819 use Carp qw(carp croak confess cluck);
  177         285  
  177         9437  
13 177     177   88864 use Sys::Hostname qw(hostname);
  177         324648  
  177         10086  
14 177     177   956 use IO::Handle ();
  177         231  
  177         2340  
15 177     177   672 use File::Spec ();
  177         210  
  177         3049  
16             #use Time::HiRes qw(time sleep);
17              
18             # People expect these to be lexical.
19              
20 177     177   617 use vars qw($poe_kernel $poe_main_window);
  177         201  
  177         17305  
21              
22             #------------------------------------------------------------------------------
23             # A cheezy exporter to avoid using Exporter.
24              
25             my $queue_class;
26              
27             BEGIN {
28 177     177   329 eval {
29 177         34689 require POE::XS::Queue::Array;
30 0         0 POE::XS::Queue::Array->import();
31 0         0 $queue_class = "POE::XS::Queue::Array";
32             };
33 177 50       967 unless ($queue_class) {
34 177         74194 require POE::Queue::Array;
35 177         983 POE::Queue::Array->import();
36 177         11084 $queue_class = "POE::Queue::Array";
37             }
38             }
39              
40             sub import {
41 507     507   3252 my ($class, $args) = ($poe_kernel, @_[1..$#_]);
42 507         1165 my $package = caller();
43              
44 507 100 100     2365 croak "POE::Kernel expects its arguments in a hash ref"
45             if ($args && ref($args) ne 'HASH');
46              
47             {
48 177     177   1027 no strict 'refs';
  177         214  
  177         40975  
  506         673  
49 506         843 *{ $package . '::poe_kernel' } = \$poe_kernel;
  506         2577  
50 506         906 *{ $package . '::poe_main_window' } = \$poe_main_window;
  506         1837  
51             }
52              
53             # Extract the import arguments we're interested in here.
54              
55 506   100     3764 my $loop = delete $args->{loop} || $ENV{POE_EVENT_LOOP};
56              
57             # Don't accept unknown/mistyped arguments.
58              
59 506         1837 my @unknown = sort keys %$args;
60 506 100       1643 croak "Unknown POE::Kernel import arguments: @unknown" if @unknown;
61              
62             # Now do things with them.
63              
64 505 100       109541 unless (UNIVERSAL::can('POE::Kernel', 'poe_kernel_loop')) {
65 177 100       588 if (defined $loop) {
66 6         31 $loop =~ s/^(POE::)?(XS::)?(Loop::)?//;
67 6 50       29 if (defined $2) {
68 0         0 $loop = "POE::XS::Loop::$loop";
69             }
70             else {
71 6         14 $loop = "POE::Loop::$loop";
72             }
73             }
74 177         559 _test_loop($loop);
75             # Bootstrap the kernel. This is inherited from a time when multiple
76             # kernels could be present in the same Perl process.
77 175 50       1786 POE::Kernel->new() if UNIVERSAL::can('POE::Kernel', 'poe_kernel_loop');
78             }
79             }
80              
81             #------------------------------------------------------------------------------
82             # Perform some optional setup.
83              
84             BEGIN {
85 177     177   886 local $SIG{'__DIE__'} = 'DEFAULT';
86              
87             {
88 177     177   978 no strict 'refs';
  177         261  
  177         13147  
  177         288  
89 177 50       820 if ($^O eq 'MSWin32') {
90 0         0 *{ __PACKAGE__ . '::RUNNING_IN_HELL' } = sub { 1 };
  0         0  
  0         0  
91             } else {
92 177     3132   3695 *{ __PACKAGE__ . '::RUNNING_IN_HELL' } = sub { 0 };
  177         5187  
  3132         15773  
93             }
94             }
95             }
96              
97             BEGIN {
98             # The entire BEGIN block is a no-strict-refs zone.
99              
100 177     177   800 no strict 'refs';
  177         233  
  177         48842  
101              
102             # Set up a constant that lets the user deactivate automatic
103             # exception handling.
104              
105 177 100   177   731 unless (defined &CATCH_EXCEPTIONS) {
106 169 100       674 my $catch_exceptions = (
107             (exists $ENV{POE_CATCH_EXCEPTIONS})
108             ? $ENV{POE_CATCH_EXCEPTIONS}
109             : 1
110             );
111              
112 169 100       414 if ($catch_exceptions) {
113 168         422 *CATCH_EXCEPTIONS = sub () { 1 };
114             }
115             else {
116 1         2 *CATCH_EXCEPTIONS = sub () { 0 };
117             }
118             }
119              
120 177 50       633 unless (defined &CHILD_POLLING_INTERVAL) {
121             # That's one second, not a true value.
122 177         295 *CHILD_POLLING_INTERVAL = sub () { 1 };
123             }
124              
125 177 100       538 unless (defined &USE_SIGCHLD) {
126             # Perl >= 5.7.3 has safe signals support
127             # perlipc.pod#Deferred_Signals_(Safe_Signals)
128             # We decided to target 5.8.1 just to be safe :)
129 161 50 33     960 if ( $] >= 5.008001 and not RUNNING_IN_HELL ) {
130 161         332 *USE_SIGCHLD = sub () { 1 };
131             } else {
132 0         0 *USE_SIGCHLD = sub () { 0 };
133             }
134             }
135              
136 177 100       605 unless (defined &USE_SIGNAL_PIPE) {
137 174         267 my $use_signal_pipe;
138 174 50       612 if ( exists $ENV{POE_USE_SIGNAL_PIPE} ) {
139 0         0 $use_signal_pipe = $ENV{POE_USE_SIGNAL_PIPE};
140             }
141              
142 174 50       336 if (RUNNING_IN_HELL) {
143 0 0       0 if ($use_signal_pipe) {
144 0         0 _warn(
145             "Sorry, disabling USE_SIGNAL_PIPE on $^O.\n",
146             "Programs are reported to hang when it's enabled.\n",
147             );
148             }
149              
150             # Must be defined to supersede the default.
151 0         0 $use_signal_pipe = 0;
152             }
153              
154 174 50 33     1034 if ($use_signal_pipe or not defined $use_signal_pipe) {
155 174         7100 *USE_SIGNAL_PIPE = sub () { 1 };
156             }
157             else {
158 0         0 *USE_SIGNAL_PIPE = sub () { 0 };
159             }
160             }
161             }
162              
163             #==============================================================================
164             # Globals, or at least package-scoped things. Data structures were
165             # moved into lexicals in 0.1201.
166              
167             # A reference to the currently active session. Used throughout the
168             # functions that act on the current session.
169             my $kr_active_session;
170             my $kr_active_event;
171             my $kr_active_event_type;
172              
173             # Needs to be lexical so that POE::Resource::Events can see it
174             # change. TODO - Something better? Maybe we call a method in
175             # POE::Resource::Events to trigger the exception there?
176 177     177   915 use vars qw($kr_exception);
  177         217  
  177         137099  
177              
178             # The Kernel's master queue.
179             my $kr_queue;
180              
181             # The current PID, to detect when it changes
182             my $kr_pid;
183              
184             # Filehandle activity modes. They are often used as list indexes.
185             sub MODE_RD () { 0 } # read
186             sub MODE_WR () { 1 } # write
187             sub MODE_EX () { 2 } # exception/expedite
188              
189             #------------------------------------------------------------------------------
190             # Kernel structure. This is the root of a large data tree. Dumping
191             # $poe_kernel with Data::Dumper or something will show most of the
192             # data that POE keeps track of. The exceptions to this are private
193             # storage in some of the leaf objects, such as POE::Wheel. All its
194             # members are described in detail further on.
195              
196             my $kr_id_seq = 0;
197              
198             sub KR_SESSIONS () { 0 } # [ \%kr_sessions,
199             sub KR_FILENOS () { 1 } # \%kr_filenos,
200             sub KR_SIGNALS () { 2 } # \%kr_signals,
201             sub KR_ALIASES () { 3 } # \%kr_aliases,
202             sub KR_ACTIVE_SESSION () { 4 } # \$kr_active_session,
203             sub KR_QUEUE () { 5 } # \$kr_queue,
204             sub KR_ID () { 6 } # $unique_kernel_id,
205             sub KR_SESSION_IDS () { 7 } # \%kr_session_ids,
206             sub KR_SID_SEQ () { 8 } # \$kr_sid_seq,
207             sub KR_EXTRA_REFS () { 9 } # \$kr_extra_refs,
208             sub KR_SIZE () { 10 } # XXX UNUSED ???
209             sub KR_RUN () { 11 } # \$kr_run_warning
210             sub KR_ACTIVE_EVENT () { 12 } # \$kr_active_event
211             sub KR_PIDS () { 13 } # \%kr_pids_to_events
212             sub KR_ACTIVE_EVENT_TYPE () { 14 } # \$kr_active_event_type
213             # ]
214              
215             # This flag indicates that POE::Kernel's run() method was called.
216             # It's used to warn about forgetting $poe_kernel->run().
217              
218             sub KR_RUN_CALLED () { 0x01 } # $kernel->run() called
219             sub KR_RUN_SESSION () { 0x02 } # sessions created
220             sub KR_RUN_DONE () { 0x04 } # run returned
221             my $kr_run_warning = 0;
222              
223             #------------------------------------------------------------------------------
224             # Events themselves.
225              
226             sub EV_SESSION () { 0 } # [ $destination_session,
227             sub EV_SOURCE () { 1 } # $sender_session,
228             sub EV_NAME () { 2 } # $event_name,
229             sub EV_TYPE () { 3 } # $event_type,
230             sub EV_ARGS () { 4 } # \@event_parameters_arg0_etc,
231             #
232             # (These fields go towards the end
233             # because they are optional in some
234             # cases. TODO: Is this still true?)
235             #
236             sub EV_OWNER_FILE () { 5 } # $caller_filename_where_enqueued,
237             sub EV_OWNER_LINE () { 6 } # $caller_line_where_enqueued,
238             sub EV_FROMSTATE () { 7 } # $fromstate
239             sub EV_SEQ () { 8 } # Maintained by POE::Queue (unique event ID)
240             sub EV_WALLTIME () { 9 } # Walltime when event was created (for alarms)
241             sub EV_DELTA () { 10 } # Seconds past walltime for event (for alarms)
242             # ]
243              
244             # These are the names of POE's internal events. They're in constants
245             # so we don't mistype them again.
246              
247             sub EN_CHILD () { '_child' }
248             sub EN_GC () { '_garbage_collect' }
249             sub EN_PARENT () { '_parent' }
250             sub EN_SCPOLL () { '_sigchld_poll' }
251             sub EN_SIGNAL () { '_signal' }
252             sub EN_START () { '_start' }
253             sub EN_STOP () { '_stop' }
254              
255             # These are POE's event classes (types). They often shadow the event
256             # names themselves, but they can encompass a large group of events.
257             # For example, ET_ALARM describes anything enqueued as by an alarm
258             # call. Types are preferred over names because bitmask tests are
259             # faster than string equality tests.
260              
261             sub ET_POST () { 0x0001 } # User events (posted, yielded).
262             sub ET_CALL () { 0x0002 } # User events that weren't enqueued.
263             sub ET_START () { 0x0004 } # _start
264             sub ET_STOP () { 0x0008 } # _stop
265             sub ET_SIGNAL () { 0x0010 } # _signal
266             sub ET_GC () { 0x0020 } # _garbage_collect
267             sub ET_PARENT () { 0x0040 } # _parent
268             sub ET_CHILD () { 0x0080 } # _child
269             sub ET_SCPOLL () { 0x0100 } # _sigchild_poll
270             sub ET_ALARM () { 0x0200 } # Alarm events.
271             sub ET_SELECT () { 0x0400 } # File activity events.
272             sub ET_SIGCLD () { 0x0800 } # sig_child() events.
273             sub ET_SIGDIE () { 0x1000 } # SIGDIE exception events.
274              
275             # A mask for all events generated by/for users.
276             sub ET_MASK_USER () { ~(ET_GC | ET_SCPOLL) }
277              
278             # A mask for all events that are delayed by a dispatch time.
279             sub ET_MASK_DELAYED () { ET_ALARM | ET_SCPOLL }
280              
281             # Temporary signal subtypes, used during signal dispatch semantics
282             # deprecation and reformation.
283              
284             sub ET_SIGNAL_RECURSIVE () { 0x2000 } # Explicitly requested signal.
285              
286             # A hash of reserved names. It's used to test whether someone is
287             # trying to use an internal event directly.
288              
289             my %poes_own_events = (
290             +EN_CHILD => 1,
291             +EN_GC => 1,
292             +EN_PARENT => 1,
293             +EN_SCPOLL => 1,
294             +EN_SIGNAL => 1,
295             +EN_START => 1,
296             +EN_STOP => 1,
297             +EN_STAT => 1,
298             );
299              
300             # These are ways a child may come or go.
301             # TODO - It would be useful to split 'lose' into two types. One to
302             # indicate that the child has stopped, and one to indicate that it was
303             # given away.
304              
305             sub CHILD_GAIN () { 'gain' } # The session was inherited from another.
306             sub CHILD_LOSE () { 'lose' } # The session is no longer this one's child.
307             sub CHILD_CREATE () { 'create' } # The session was created as a child of this.
308              
309             # Argument offsets for different types of internally generated events.
310             # TODO Exporting (EXPORT_OK) these would let people stop depending on
311             # positions for them.
312              
313             sub EA_SEL_HANDLE () { 0 }
314             sub EA_SEL_MODE () { 1 }
315             sub EA_SEL_ARGS () { 2 }
316              
317             #------------------------------------------------------------------------------
318             # Debugging and configuration constants.
319              
320             # Shorthand for defining a trace constant.
321             sub _define_trace {
322 177     177   940 no strict 'refs';
  177         235  
  177         21695  
323 177     177   384 foreach my $name (@_) {
324 1416 50       1107 next if defined *{"TRACE_$name"}{CODE};
  1416         5010  
325 1416         2563 my $trace_value = &TRACE_DEFAULT;
326 1416         1460 my $trace_name = "TRACE_$name";
327 1416     0   7294 *$trace_name = sub () { $trace_value };
  0         0  
328             }
329             }
330              
331             # Debugging flags for subsystems. They're done as double evals here
332             # so that someone may define them before using POE::Kernel (or POE),
333             # and the pre-defined value will take precedence over the defaults
334             # here.
335              
336             my $trace_file_handle;
337              
338             BEGIN {
339             # Shorthand for defining an assert constant.
340             sub _define_assert {
341 177     177   856 no strict 'refs';
  177         237  
  177         31259  
342 177     177   333 foreach my $name (@_) {
343 885 100       700 next if defined *{"ASSERT_$name"}{CODE};
  885         4186  
344 882         1189 my $assert_value = &ASSERT_DEFAULT;
345 882         1011 my $assert_name = "ASSERT_$name";
346 882     0   189189 *$assert_name = sub () { $assert_value };
  0         0  
347             }
348             }
349              
350             # Assimilate POE_TRACE_* and POE_ASSERT_* environment variables.
351             # Environment variables override everything else.
352 177     177   1209 while (my ($var, $val) = each %ENV) {
353 3905 100       11458 next unless $var =~ /^POE_([A-Z_]+)$/;
354              
355 11         30 my $const = $1;
356              
357 177 100 33 177   943 next unless $const =~ /^(?:TRACE|ASSERT)_/ or do { no strict 'refs'; defined &$const };
  177         218  
  177         98121  
  11         39  
  11         77  
358              
359             # Copy so we don't hurt our environment.
360 1         1 my $value = $val;
361 1         4 ($value) = ($value =~ /^([-\@\w.]+)$/); # Untaint per rt.cpan.org 81550
362 1         2 $value =~ tr['"][]d;
363 1 50       4 $value = 0 + $value if $value =~ /^\s*-?\d+(?:\.\d+)?\s*$/;
364              
365 177     177   2618 no strict 'refs';
  177         268  
  177         12559  
366 1         3 local $^W = 0;
367 1         5 local $SIG{__WARN__} = sub { }; # redefine
  0         0  
368 1         14 *$const = sub () { $value };
  0         0  
369             }
370              
371             # TRACE_FILENAME is special.
372             {
373 177     177   824 no strict 'refs';
  177         256  
  177         31028  
  177         308  
374 177 50       526 my $trace_filename = TRACE_FILENAME() if defined &TRACE_FILENAME;
375 177 50       493 if (defined $trace_filename) {
376 0 0       0 open $trace_file_handle, ">$trace_filename"
377             or die "can't open trace file `$trace_filename': $!";
378 0         0 CORE::select((CORE::select($trace_file_handle), $| = 1)[0]);
379             }
380             }
381             # TRACE_DEFAULT changes the default value for other TRACE_*
382             # constants. Since define_trace() uses TRACE_DEFAULT internally, it
383             # can't be used to define TRACE_DEFAULT itself.
384              
385 177 100       692 defined &TRACE_DEFAULT or *TRACE_DEFAULT = sub () { 0 };
386              
387 177         507 _define_trace qw(
388             EVENTS FILES PROFILE REFCNT RETVALS SESSIONS SIGNALS STATISTICS
389             );
390              
391             # See the notes for TRACE_DEFAULT, except read ASSERT and assert
392             # where you see TRACE and trace.
393              
394 177 100       760 defined &ASSERT_DEFAULT or *ASSERT_DEFAULT = sub () { 0 };
395              
396 177         531 _define_assert qw(DATA EVENTS FILES RETVALS USAGE);
397             }
398              
399             # An "idle" POE::Kernel may still have events enqueued. These events
400             # regulate polling for signals, profiling, and perhaps other aspects of
401             # POE::Kernel's internal workings.
402             #
403             # XXX - There must be a better mechanism.
404             #
405             my $idle_queue_size;
406              
407 3     3   13 sub _idle_queue_grow { $idle_queue_size++; }
408 2     2   6 sub _idle_queue_shrink { $idle_queue_size--; }
409 5     5   39 sub _idle_queue_size { $idle_queue_size; }
410 173     173   364 sub _idle_queue_reset { $idle_queue_size = 0; }
411              
412             #------------------------------------------------------------------------------
413             # Helpers to carp, croak, confess, cluck, warn and die with whatever
414             # trace file we're using today. _trap is reserved for internal
415             # errors.
416              
417             sub _trap {
418 24     24   470 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
419 24   33     118 local *STDERR = $trace_file_handle || *STDERR;
420              
421 24         3248 confess(
422             "=== $$ === Please address any warnings or errors above this message,\n",
423             "=== $$ === and try again. If there are no previous messages, or they\n",
424             "=== $$ === are from within POE, then please mail them along with the\n",
425             "=== $$ === following information to bug-POE\@rt.cpan.org:\n",
426             "---\n@_\n-----\n"
427             );
428             }
429              
430             sub _croak {
431 1     1   691 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
432 1   33     9 local *STDERR = $trace_file_handle || *STDERR;
433 1         6 my $message = join("", @_);
434 1         12 $message =~ s/^/=== $$ === /mg;
435 1         7 croak $message;
436             }
437              
438             sub _confess {
439 101     101   974 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
440 101   33     409 local *STDERR = $trace_file_handle || *STDERR;
441 101         242 my $message = join("", @_);
442 101         788 $message =~ s/^/=== $$ === /mg;
443 101         12051 confess $message;
444             }
445              
446             sub _cluck {
447 4788     4788   9287 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
448 4788   33     19539 local *STDERR = $trace_file_handle || *STDERR;
449 4788         9474 my $message = join("", @_);
450 4788         34530 $message =~ s/^/=== $$ === /mg;
451 4788         658174 cluck $message;
452             }
453              
454             sub _carp {
455 4499     4499   8542 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
456 4499   33     18240 local *STDERR = $trace_file_handle || *STDERR;
457 4499         8948 my $message = join("", @_);
458 4499         29738 $message =~ s/^/=== $$ === /mg;
459 4499         800392 carp $message;
460             }
461              
462             sub _warn {
463 51508     51508   165515 my ($package, $file, $line) = caller();
464 51508         122701 my $message = join("", @_);
465 51508 100       213706 $message .= " at $file line $line\n" unless $message =~ /\n$/;
466 51508         400105 $message =~ s/^/=== $$ === /mg;
467 51508         2440559 warn $message;
468             }
469              
470             sub _die {
471 1     1   555 my ($package, $file, $line) = caller();
472 1         4 my $message = join("", @_);
473 1 50       10 $message .= " at $file line $line\n" unless $message =~ /\n$/;
474 1         11 $message =~ s/^/=== $$ === /mg;
475 1   33     9 local *STDERR = $trace_file_handle || *STDERR;
476 1         5 die $message;
477             }
478              
479             #------------------------------------------------------------------------------
480             # Adapt POE::Kernel's personality to whichever event loop is present.
481              
482             sub _find_loop {
483 30904     30904   30761 my ($mod) = @_;
484              
485 30904         35875 foreach my $dir (@INC) {
486 338509 100       3231719 return 1 if (-r "$dir/$mod");
487             }
488 30855         88955 return 0;
489             }
490              
491             sub _load_loop {
492 177     177   404 my $loop = shift;
493              
494 177     8   1353 *poe_kernel_loop = sub { return "$loop" };
  8         7094  
495              
496             # Modules can die with "not really dying" if they've loaded
497             # something else. This exception prevents the rest of the
498             # originally used module from being parsed, so the module it's
499             # handed off to takes over.
500 177         13032 eval "require $loop";
501 177 100 66     1794 if ($@ and $@ !~ /not really dying/) {
502 2         47 die(
503             "*\n",
504             "* POE can't use $loop:\n",
505             "* $@\n",
506             "*\n",
507             );
508             }
509             }
510              
511             sub _test_loop {
512 177     177   321 my $used_first = shift;
513 177         747 local $SIG{__DIE__};
514              
515             # First see if someone wants to load a POE::Loop or XS version
516             # explicitly.
517 177 100       589 if (defined $used_first) {
518 6         17 _load_loop($used_first);
519 4         17 return;
520             }
521              
522 171         6291 foreach my $file (keys %INC) {
523 15391 50       33324 next if (substr ($file, -3) ne '.pm');
524 15391         64536 my @split_dirs = File::Spec->splitdir($file);
525              
526             # Create a module name by replacing the path separators with
527             # underscores and removing ".pm"
528 15391         25171 my $module = join("_", @split_dirs);
529 15391         15696 substr($module, -3) = "";
530              
531             # Skip the module name if it isn't legal.
532 15391 50       38544 next if $module =~ /[^\w\.]/;
533              
534             # Try for the XS version first. If it fails, try the plain
535             # version. If that fails, we're up a creek.
536 15391         20232 $module = "POE/XS/Loop/$module.pm";
537 15391 50       18875 unless (_find_loop($module)) {
538 15391         40594 $module =~ s|XS/||;
539 15391 100       23774 next unless (_find_loop($module));
540             }
541              
542 49 50 33     285 if (defined $used_first and $used_first ne $module) {
543 0         0 die(
544             "*\n",
545             "* POE can't use multiple event loops at once.\n",
546             "* You used $used_first and $module.\n",
547             "* Specify the loop you want as an argument to POE\n",
548             "* use POE qw(Loop::Select);\n",
549             "* or;\n",
550             "* use POE::Kernel { loop => 'Select' };\n",
551             "*\n",
552             );
553             }
554              
555 49         170 $used_first = $module;
556             }
557              
558             # No loop found. Default to our internal select() loop.
559 171 100       3010 unless (defined $used_first) {
560 122         340 $used_first = "POE/XS/Loop/Select.pm";
561 122 50       361 unless (_find_loop($used_first)) {
562 122         558 $used_first =~ s/XS\///;
563             }
564             }
565              
566 171         445 substr($used_first, -3) = "";
567 171         663 $used_first =~ s|/|::|g;
568 171         669 _load_loop($used_first);
569             }
570              
571             #------------------------------------------------------------------------------
572             # Include resource modules here. Later, when we have the option of XS
573             # versions, we'll adapt this to include them if they're available.
574              
575 177     177   71249 use POE::Resources;
  177         343  
  177         1426925  
576              
577             ###############################################################################
578             # Helpers.
579              
580             ### Resolve $whatever into a session reference, trying every method we
581             ### can until something succeeds.
582              
583             sub _resolve_session {
584 3847     3847   4448 my ($self, $whatever) = @_;
585 3847         3498 my $session;
586              
587             # Resolve against sessions.
588 3847         10072 $session = $self->_data_ses_resolve($whatever);
589 3847 100       10337 return $session if defined $session;
590              
591             # Resolve against IDs.
592 205         807 $session = $self->_data_sid_resolve($whatever);
593 205 100       493 return $session if defined $session;
594              
595             # Resolve against aliases.
596 122         431 $session = $self->_data_alias_resolve($whatever);
597 122 100       377 return $session if defined $session;
598              
599             # Resolve against the Kernel itself. Use "eq" instead of "==" here
600             # because $whatever is often a string.
601 6 50       19 return $whatever if $whatever eq $self;
602              
603             # We don't know what it is.
604 6         11 return undef;
605             }
606              
607             ### Test whether POE has become idle.
608              
609             sub _test_if_kernel_is_idle {
610 2712     2712   3506 my $self = shift;
611              
612 2712         2579 if (TRACE_REFCNT) {
613             _warn(
614             " ,----- Kernel Activity -----\n",
615             " | Events : ", $kr_queue->get_item_count(),
616             " (vs. idle size = ", $idle_queue_size, ")\n",
617             " | Files : ", $self->_data_handle_count(), "\n",
618             " | Extra : ", $self->_data_extref_count(), "\n",
619             " | Procs : ", $self->_data_sig_kernel_awaits_pids(), "\n",
620             " | Sess : ", $self->_data_ses_count(), "\n",
621             " `---------------------------\n",
622             " ..."
623             );
624             }
625              
626 2712         4935 if( ASSERT_DATA ) {
627             if( $kr_pid != $$ ) {
628             _trap(
629             "New process detected. " .
630             "You must call ->has_forked() in the child process."
631             );
632             }
633             }
634              
635             # Not yet idle, or SO idle that there's nothing to receive the
636             # event. Try to order these from most to least likely to be true so
637             # that the tests short-circuit quickly.
638              
639             return if (
640 2712 50 100     11394 $kr_queue->get_item_count() > $idle_queue_size or
      100        
      100        
      66        
641             $self->_data_handle_count() or
642             $self->_data_extref_count() or
643             $self->_data_sig_kernel_awaits_pids() or
644             !$self->_data_ses_count()
645             );
646              
647 1148         4221 $self->_data_ev_enqueue(
648             $self, $self, EN_SIGNAL, ET_SIGNAL, [ 'IDLE' ],
649             __FILE__, __LINE__, undef
650             );
651             }
652              
653             ### Explain why a session could not be resolved.
654              
655             sub _explain_resolve_failure {
656 556     4   1473 my ($self, $whatever, $nonfatal) = @_;
657 1213         4410 local $Carp::CarpLevel = 2;
658              
659 86         694 if (ASSERT_DATA and !$nonfatal) {
660             _trap "
Cannot resolve ``$whatever'' into a session reference";
661             }
662              
663 4         11 $! = ESRCH;
664 0         0 TRACE_RETVALS and _carp " session not resolved: $!";
665 0         0 ASSERT_RETVALS and _carp " session not resolved: $!";
666             }
667              
668             ### Explain why a function is returning unsuccessfully.
669              
670             sub _explain_return {
671 15     15   18 my ($self, $message) = @_;
672 15         25 local $Carp::CarpLevel = 2;
673              
674 15         28 ASSERT_RETVALS and _confess " $message";
675 6         6 TRACE_RETVALS and _carp " $message";
676             }
677              
678             ### Explain how the user made a mistake calling a function.
679              
680             sub _explain_usage {
681 25     25   43 my ($self, $message) = @_;
682 25         35 local $Carp::CarpLevel = 2;
683              
684 25         70 ASSERT_USAGE and _confess " $message";
685 13         30 ASSERT_RETVALS and _confess " $message";
686 1         2 TRACE_RETVALS and _carp " $message";
687             }
688              
689             #==============================================================================
690             # SIGNALS
691             #==============================================================================
692              
693             #------------------------------------------------------------------------------
694             # Register or remove signals.
695              
696             # Public interface for adding or removing signal handlers.
697              
698             sub sig {
699 270     270 1 22882 my ($self, $signal, $event_name, @args) = ($poe_kernel, @_[1..$#_]);
700              
701 270         446 if (ASSERT_USAGE) {
702             _confess " must call sig() from a running session"
703             if $kr_active_session == $self;
704             _confess " undefined signal in sig()" unless defined $signal;
705             _carp(
706             " The '$event_name' event is one of POE's own. Its " .
707             "effect cannot be achieved assigning it to a signal"
708             ) if defined($event_name) and exists($poes_own_events{$event_name});
709             };
710              
711 270 100       809 if (defined $event_name) {
712 268         705 $self->_data_sig_add($kr_active_session, $signal, $event_name, \@args);
713             }
714             else {
715 265         1513 $self->_data_sig_remove($kr_active_session->ID, $signal);
716             }
717             }
718              
719             # Public interface for posting signal events.
720             # TODO - Like post(), signal() should return
721              
722             sub signal {
723 285     21 1 2289 my ($self, $dest_session, $signal, @etc) = ($poe_kernel, @_[1..$#_]);
724              
725 176         860 if (ASSERT_USAGE) {
726             _confess " undefined destination in signal()"
727             unless defined $dest_session;
728             _confess " undefined signal in signal()" unless defined $signal;
729             };
730              
731 130         428 my $session = $self->_resolve_session($dest_session);
732 20 100       50 unless (defined $session) {
733 17         63 $self->_explain_resolve_failure($dest_session);
734 16         46 return;
735             }
736              
737             $self->_data_ev_enqueue(
738 2         12 $session, $kr_active_session,
739             EN_SIGNAL, ET_SIGNAL, [ $signal, @etc ],
740             (caller)[1,2], $kr_active_event
741             );
742 2         6 return 1;
743             }
744              
745             # Public interface for flagging signals as handled. This will replace
746             # the handlers' return values as an implicit flag. Returns undef so
747             # it may be used as the last function in an event handler.
748              
749             sub sig_handled {
750 140     124 1 3781 my $self = $poe_kernel;
751 140         533 $self->_data_sig_handled();
752              
753 124 100       634 if ($kr_active_event eq EN_SIGNAL) {
754 0         0 _die(
755             ",----- DEPRECATION ERROR -----\n",
756             "| ", $self->_data_alias_loggable($kr_active_session->ID), ":\n",
757             "| handled a _signal event. You must register a handler with sig().\n",
758             "`-----------------------------\n",
759             );
760             }
761             }
762              
763             # Attach a window or widget's destroy/closure to the UIDESTROY signal.
764              
765             sub signal_ui_destroy {
766 0     0 1 0 my ($self, $window) = @_;
767 0         0 $self->loop_attach_uidestroy($window);
768             }
769              
770             # Handle child PIDs being reaped. Added 2006-09-15.
771              
772             sub sig_child {
773 206     206 1 21581 my ($self, $pid, $event_name, @args) = ($poe_kernel, @_[1..$#_]);
774              
775 206         439 if (ASSERT_USAGE) {
776             _confess " must call sig_chld() from a running session"
777             if $kr_active_session == $self;
778             _confess " undefined process ID in sig_chld()" unless defined $pid;
779             _carp(
780             " The '$event_name' event is one of POE's own. Its " .
781             "effect cannot be achieved assigning it to a signal"
782             ) if defined($event_name) and exists($poes_own_events{$event_name});
783             };
784              
785 206 100       804 if (defined $event_name) {
    50          
786 206         2194 $self->_data_sig_pid_watch($kr_active_session, $pid, $event_name, \@args);
787             }
788             elsif ($self->_data_sig_pids_is_ses_watching($kr_active_session->ID, $pid)) {
789 108         1133 $self->_data_sig_pid_ignore($kr_active_session->ID, $pid);
790             }
791             }
792              
793             #==============================================================================
794             # KERNEL
795             #==============================================================================
796              
797             sub new {
798 283     175 1 694 my $type = shift;
799              
800             # Prevent multiple instances, no matter how many times it's called.
801             # This is a backward-compatibility enhancement for programs that
802             # have used versions prior to 0.06. It also provides a convenient
803             # single entry point into the entirety of POE's state: point a
804             # Dumper module at it, and you'll see a hideous tree of knowledge.
805             # Be careful, though. Its apples bite back.
806 282 100       2171 unless (defined $poe_kernel) {
807              
808             # Create our master queue.
809 175         1549 $kr_queue = $queue_class->new();
810              
811             # Remember the PID
812 175         761 $kr_pid = $$;
813              
814             # TODO - Should KR_ACTIVE_SESSIONS and KR_ACTIVE_EVENT be handled
815             # by POE::Resource::Sessions?
816             # TODO - Should the subsystems be split off into separate real
817             # objects, such as KR_QUEUE is?
818              
819 175         897 my $self = $poe_kernel = bless [
820             undef, # KR_SESSIONS - from POE::Resource::Sessions
821             undef, # KR_FILENOS - from POE::Resource::FileHandles
822             undef, # KR_SIGNALS - from POE::Resource::Signals
823             undef, # KR_ALIASES - from POE::Resource::Aliases
824             \$kr_active_session, # KR_ACTIVE_SESSION
825             $kr_queue, # KR_QUEUE - reference to an object
826             undef, # KR_ID
827             undef, # KR_SESSION_IDS - from POE::Resource::SIDS
828             undef, # KR_SID_SEQ - from POE::Resource::SIDS
829             undef, # KR_EXTRA_REFS
830             undef, # KR_SIZE
831             \$kr_run_warning, # KR_RUN
832             \$kr_active_event, # KR_ACTIVE_EVENT
833             undef, # KR_PIDS
834             \$kr_active_event_type, # KR_ACTIVE_EVENT_TYPE
835             ], $type;
836              
837 175         1151 POE::Resources->load();
838              
839 175         896 $self->_recalc_id();
840 175         956 $self->_data_sid_set($self->[KR_ID], $self);
841              
842             # Initialize subsystems. The order is important.
843              
844             # We need events before sessions, and the kernel's session before
845             # it can start polling for signals.
846 175         822 $self->_data_ev_initialize($kr_queue);
847 175         1174 $self->_initialize_kernel_session();
848 174         583 $self->_data_sig_initialize();
849 173         814 $self->_data_alias_initialize();
850              
851             # These other subsystems don't have strange interactions.
852 173         724 $self->_data_handle_initialize($kr_queue);
853              
854 173         518 _idle_queue_reset();
855             }
856              
857             # Return the global instance.
858 173         3544 $poe_kernel;
859             }
860              
861             sub CLONE {
862 0     0   0 _data_ses_clone();
863             }
864              
865             #------------------------------------------------------------------------------
866             # Send an event to a session right now. Used by _disp_select to
867             # expedite select() events, and used by run() to deliver posted events
868             # from the queue.
869              
870             # Dispatch an event to its session. A lot of work goes on here.
871              
872 251     251   50146 sub _dummy_sigdie_handler { 1 }
873              
874             sub _dispatch_signal_event {
875             my (
876 467     467   1179 $self,
877             $session, $source_session, $event, $type, $etc,
878             $file, $line, $fromstate, $priority, $seq
879             ) = @_;
880              
881             # TODO - Regrettably, duplicate checking code in:
882             # _dispatch_signal_event(), _dispatch_event().
883              
884 467         1681 if (ASSERT_EVENTS) {
885             _confess " undefined dest session" unless defined $session;
886             _confess " undefined source session" unless defined $source_session;
887             };
888              
889 467         1072 if (TRACE_EVENTS) {
890             my $log_session = $session;
891             $log_session = $self->_data_alias_loggable($session->ID) unless (
892             $type & ET_START
893             );
894             my $string_etc = join(" ", map { defined() ? $_ : "(undef)" } @$etc);
895             _warn(
896             " Dispatching event $seq ``$event'' ($string_etc) from ",
897             $self->_data_alias_loggable($source_session->ID), " to $log_session"
898             );
899             }
900              
901 467         6762 my $signal = $etc->[0];
902              
903 467         627 if (TRACE_SIGNALS) {
904             _warn(
905             " dispatching ET_SIGNAL ($signal) to ",
906             $self->_data_alias_loggable($session->ID)
907             );
908             }
909              
910             # Step 1a: Reset the handled-signal flags.
911              
912 467         1025 local @POE::Kernel::kr_signaled_sessions;
913 467         1103 local $POE::Kernel::kr_signal_total_handled;
914 467         1058 local $POE::Kernel::kr_signal_type;
915              
916 582         2096 $self->_data_sig_reset_handled($signal);
917              
918             # Step 1b: Collect a list of sessions to receive the signal.
919              
920 467         1247 my @touched_sessions = ($session);
921 467         2501 my $touched_index = 0;
922 467         965 while ($touched_index < @touched_sessions) {
923 878         2264 my $next_target = $touched_sessions[$touched_index]->ID;
924 878         3319 push @touched_sessions, $self->_data_ses_get_children($next_target);
925 1032         1958 $touched_index++;
926             }
927              
928             # Step 1c: The DIE signal propagates up through parents, too.
929              
930 621 100       1578 if ($signal eq "DIE") {
931 509         1664 my $next_target = $self->_data_ses_get_parent($session->ID);
932 355   100     842 while (defined($next_target) and $next_target != $self) {
933 187         286 unshift @touched_sessions, $next_target;
934 187         620 $next_target = $self->_data_ses_get_parent($next_target->ID);
935             }
936             }
937              
938             # Step 2: Propagate the signal to the explicit watchers in the
939             # child tree. Ensure the full tree is touched regardless
940             # whether there are explicit watchers.
941              
942 673 100       1844 if ($self->_data_sig_explicitly_watched($signal)) {
943 582         1542 my %signal_watchers = $self->_data_sig_watchers($signal);
944              
945 744         1731 $touched_index = @touched_sessions;
946 373         1271 while ($touched_index--) {
947 198         323 my $target_session = $touched_sessions[$touched_index];
948 198         582 $self->_data_sig_touched_session($target_session);
949              
950 355         444 my $target_sid = $target_session->ID;
951 355 100       1047 next unless exists $signal_watchers{$target_sid};
952 530         1280 my ($target_event, $target_etc) = @{$signal_watchers{$target_sid}};
  504         1915  
953              
954 351         492 if (TRACE_SIGNALS) {
955             _warn(
956             " propagating explicit signal $target_event ($signal) ",
957             "(@$target_etc) to ", $self->_data_alias_loggable($target_sid)
958             );
959             }
960              
961             # ET_SIGNAL_RECURSIVE is used here to avoid repropagating
962             # the signal ad nauseam.
963             $self->_dispatch_event(
964 351         1170 $target_session, $self,
965             $target_event, ET_SIGNAL_RECURSIVE | $type, [ @$etc, @$target_etc ],
966             $file, $line, $fromstate, monotime(), -__LINE__
967             );
968             }
969             }
970             else {
971 653         909 $touched_index = @touched_sessions;
972 653         2505 while ($touched_index--) {
973 890         1916 $self->_data_sig_touched_session($touched_sessions[$touched_index]);
974             }
975             }
976              
977             # Step 3: Check to see if the signal was handled.
978              
979 511         1678 $self->_data_sig_free_terminated_sessions();
980              
981             # If the signal was SIGDIE, then propagate the exception.
982              
983 285         666 my $handled_session_count = (_data_sig_handled_status())[0];
984 450 100 100     1395 if ($signal eq "DIE" and !$handled_session_count) {
985 337 100       890 $kr_exception = $etc->[1]{error_str} . (
986             (defined $kr_exception)
987             ? "Additional error thrown in handler for previous error:\n$kr_exception"
988             : ''
989             );
990             }
991              
992             # Signal completely dispatched. Thanks for flying!
993 447         1932 return;
994             }
995              
996             sub _dispatch_event {
997             my (
998 8113     7946   22053 $self,
999             $session, $source_session, $event, $type, $etc,
1000             $file, $line, $fromstate, $priority, $seq
1001             ) = @_;
1002              
1003 8137         8045 if (ASSERT_EVENTS) {
1004             _confess " undefined dest session" unless defined $session;
1005             _confess " undefined source session" unless defined $source_session;
1006             };
1007              
1008 7975         13856 if (TRACE_EVENTS) {
1009             my $log_session = $session;
1010             $log_session = $self->_data_alias_loggable($session->ID) unless (
1011             $type & ET_START
1012             );
1013             my $string_etc = join(" ", map { defined() ? $_ : "(undef)" } @$etc);
1014             _warn(
1015             " Dispatching event $seq ``$event'' ($string_etc) from ",
1016             $self->_data_alias_loggable($source_session->ID), " to $log_session"
1017             );
1018             }
1019              
1020             ### Pre-dispatch processing.
1021              
1022             # Some sessions don't do anything in _start and expect their
1023             # creators to provide a start-up event. This means we can't
1024             # &_collect_garbage at _start time. Instead, an ET_GC event is
1025             # posted as part of session allocation. Simply dispatching it
1026             # will trigger a GC sweep.
1027              
1028 8112 100       15621 return 0 if $type & ET_GC;
1029              
1030             # Preprocess signals. This is where _signal is translated into
1031             # its registered handler's event name, if there is one.
1032              
1033 8010         9012 if (TRACE_EVENTS) {
1034             _warn(
1035             " dispatching event $seq ``$event'' to ",
1036             $self->_data_alias_loggable($session->ID)
1037             );
1038             if ($event eq EN_SIGNAL) {
1039             _warn(" signal($etc->[0])");
1040             }
1041             }
1042              
1043             # Prepare to call the appropriate handler. Push the current active
1044             # session on Perl's call stack.
1045              
1046 8007         12556 my ($hold_active_session, $hold_active_event, $hold_active_event_type) = (
1047             $kr_active_session, $kr_active_event, $kr_active_event_type
1048             );
1049             (
1050 7786         19439 $kr_active_session, $kr_active_event, $kr_active_event_type
1051             ) = ($session, $event, $type);
1052              
1053             # We only care about the return value and calling context if it's
1054             # ET_CALL.
1055              
1056 7588         12253 my $return;
1057 8934         19708 my $wantarray = wantarray();
1058              
1059 7588 100       18944 confess 'please report this stacktrace to bug-poe@rt.cpan.org' unless (
1060             defined $session
1061             );
1062              
1063             # Quiet SIGDIE if it's DEFAULT. If it's something special, then
1064             # someone had better know what they're doing.
1065              
1066 7588         27474 my $old_sig_die = $SIG{__DIE__};
1067 7316 100 66     18685 $SIG{__DIE__} = \&_dummy_sigdie_handler if (
1068             not defined $old_sig_die or $old_sig_die eq 'DEFAULT'
1069             );
1070              
1071 7285         22366 eval {
1072 7316 100       31748 if ($wantarray) {
    100          
1073 2039         2874 $return = [
1074             $session->_invoke_state(
1075             $source_session, $event, $etc, $file, $line, $fromstate
1076             )
1077             ];
1078             }
1079             elsif (defined $wantarray) {
1080 5814         11452 $return = $session->_invoke_state(
1081             $source_session, $event, $etc, $file, $line, $fromstate
1082             );
1083             }
1084             else {
1085 5175         12429 $session->_invoke_state(
1086             $source_session, $event, $etc, $file, $line, $fromstate
1087             );
1088             }
1089             };
1090              
1091             # An exception happened?
1092             # It was intially thrown under the $SIG{__DIE__} conditions that the
1093             # user wanted. Any formatting, logging, etc. is already done.
1094              
1095 5953 100 66     32630 if (ref($@) or $@ ne '') {
1096 5018         8308 if (CATCH_EXCEPTIONS) {
1097 5572         27675 if (TRACE_EVENTS) {
1098             _warn(
1099             " exception occurred in $event when invoked on ",
1100             $self->_data_alias_loggable($session->ID)
1101             );
1102             }
1103              
1104             # Exceptions in _stop are rethrown unconditionally.
1105             # We can't enqueue them--the session is about to go away.
1106             # Also if the active session has been forced back to $self via
1107             # POE::Kernel->stop().
1108 3695 100 66     8407 if ($type & (ET_STOP | ET_SIGDIE) or $kr_active_session eq $self) {
1109             # Propagate the exception up to the safe rethrow point.
1110 3681         20147 $kr_exception = $@;
1111             }
1112             else {
1113 3759         5443 $self->_data_ev_enqueue(
1114             $session, $self, EN_SIGNAL, ET_SIGDIE, [
1115             'DIE' => {
1116             source_session => $source_session,
1117             dest_session => $session,
1118             event => $event,
1119             file => $file,
1120             line => $line,
1121             from_state => $fromstate,
1122             error_str => $@,
1123             },
1124             ], __FILE__, __LINE__, undef
1125             );
1126             }
1127             }
1128             else {
1129             # Propagate the exception up to the safe rethrow point.
1130             $kr_exception = $@;
1131             }
1132             }
1133              
1134             # Global $sig{__DIE__} changed? For shame!
1135             # TODO - This warning is only needed if a SIGDIE handler is active.
1136             # TODO - Likewise, setting a SIGDIE with a __DIE__ handler in play
1137             # will be tricky or impossible. There should be some message.
1138              
1139 5356 100 100     19443 if (
      66        
1140             (not defined $old_sig_die or $old_sig_die eq 'DEFAULT') and
1141             $SIG{__DIE__} ne \&_dummy_sigdie_handler
1142             ) {
1143 28         93 _warn(
1144             " Event handler redefined global __DIE__ signal handler.\n",
1145             " This may conflict with CATCH_EXCEPTIONS handling.\n",
1146             " If global redefinition is necessary, do it in global code.\n",
1147             );
1148              
1149 2728         17516 $SIG{__DIE__} = $old_sig_die;
1150             }
1151              
1152             # Clear out the event arguments list, in case there are POE-ish
1153             # things in it. This allows them to destruct happily before we set
1154             # the current session back.
1155              
1156 4642         17858 @$etc = ( );
1157              
1158             # Stringify the handler's return value if it belongs in the POE
1159             # namespace. $return's scope exists beyond the post-dispatch
1160             # processing, which includes POE's garbage collection. The scope
1161             # bleed was known to break determinism in surprising ways.
1162              
1163 5315 100 100     194797 if (defined $return and substr(ref($return), 0, 5) eq 'POE::') {
1164 1904         3143 $return = "$return";
1165             }
1166              
1167             # Pop the active session and event, now that they're no longer
1168             # active.
1169              
1170 3580         8349 ($kr_active_session, $kr_active_event, $kr_active_event_type) = (
1171             $hold_active_session, $hold_active_event, $hold_active_event_type
1172             );
1173              
1174 1964         2748 if (TRACE_EVENTS) {
1175             my $string_ret = $return;
1176             $string_ret = "undef" unless defined $string_ret;
1177             _warn(" event $seq ``$event'' returns ($string_ret)\n");
1178             }
1179              
1180             # Return doesn't matter unless ET_CALL, ET_START or ET_STOP.
1181 3484 100       10727 return unless $type & (ET_CALL | ET_START | ET_STOP);
1182              
1183             # Return what the handler did. This is used for call().
1184 2023 100       2744 return( $wantarray ? @$return : $return );
1185             }
1186              
1187             #------------------------------------------------------------------------------
1188             # POE's main loop! Now with Tk and Event support!
1189              
1190             # Do pre-run start-up. Initialize the event loop, and allocate a
1191             # session structure to represent the Kernel.
1192              
1193             sub _initialize_kernel_session {
1194 2145     269   6823 my $self = shift;
1195              
1196 4443         23901 $self->loop_initialize();
1197              
1198 302         466 $kr_exception = undef;
1199 396         740 $kr_active_session = $self;
1200 3889         8243 $self->_data_ses_allocate($self, $self->[KR_ID], undef);
1201             }
1202              
1203             # Do post-run cleanup.
1204              
1205             sub _finalize_kernel {
1206 3844     189   10304 my $self = shift;
1207              
1208             # Disable signal watching since there's now no place for them to go.
1209 329         1187 foreach ($self->_data_sig_get_safe_signals()) {
1210 10081         19674 $self->loop_ignore_signal($_);
1211             }
1212              
1213             # Remove the kernel session's signal watcher.
1214 3844         5871 $self->_data_sig_remove($self->ID, "IDLE");
1215              
1216             # The main loop is done, no matter which event library ran it.
1217             # sig before loop so that it clears the signal_pipe file handler
1218 3768         4597 $self->_data_sig_finalize();
1219 3750         7723 $self->loop_finalize();
1220 3733         12237 $self->_data_extref_finalize();
1221 3716         27282 $self->_data_sid_finalize();
1222 1003         5076 $self->_data_alias_finalize();
1223 189         859 $self->_data_handle_finalize();
1224 189         791 $self->_data_ev_finalize();
1225 189         791 $self->_data_ses_finalize();
1226             }
1227              
1228             sub run_while {
1229 0     0 1 0 my ($self, $scalar_ref) = ($poe_kernel, @_[1..$#_]);
1230 0   0     0 1 while $$scalar_ref and $self->run_one_timeslice();
1231             }
1232              
1233             sub run_one_timeslice {
1234 0     0 1 0 my $self = $poe_kernel;
1235              
1236 0 100       0 unless ($self->_data_ses_count()) {
1237 0         0 $self->_finalize_kernel();
1238 0         0 $kr_run_warning |= KR_RUN_DONE;
1239 0 100       0 $kr_exception and $self->_rethrow_kr_exception();
1240 0         0 return;
1241             }
1242              
1243 0         0 $self->loop_do_timeslice();
1244 0 100       0 $kr_exception and $self->_rethrow_kr_exception();
1245              
1246 0         0 return 1;
1247             }
1248              
1249             sub run {
1250             # So run() can be called as a class method.
1251 200 100   200 1 10845 POE::Kernel->new unless defined $poe_kernel;
1252 200         380 my $self = $poe_kernel;
1253              
1254             # Flag that run() was called.
1255 200         901 $kr_run_warning |= KR_RUN_CALLED;
1256              
1257             # TODO is this check expensive? ( do people run() more than 1 time? )
1258 200 100       1743 if( $kr_pid != $$ ) {
1259 0         0 if ( ASSERT_USAGE ) {
1260             _warn "Detected a fork, automatically calling ->has_forked()";
1261             }
1262 0         0 $self->has_forked;
1263             }
1264              
1265             # Don't run the loop if we have no sessions
1266             # Loop::Event will blow up, so we're doing this sanity check
1267 34 50       316 if ( $self->_data_ses_count() == 0 ) {
1268             # Emit noise only if we are under debug mode
1269 166         1203 if ( ASSERT_DATA ) {
1270             _warn("Not running the event loop because we have no sessions!\n");
1271             }
1272             } else {
1273             # All signals must be explicitly watched now. We do it here because
1274             # it's too early in initialize_kernel_session.
1275 33         179 $self->_data_sig_add($self, "IDLE", EN_SIGNAL);
1276              
1277             # Run the loop!
1278 35         172 $self->loop_run();
1279              
1280             # Cleanup
1281 189         1460 $self->_finalize_kernel();
1282             }
1283              
1284             # Clean up afterwards.
1285 189         1002 $kr_run_warning |= KR_RUN_DONE;
1286              
1287 189 100       956 $kr_exception and $self->_rethrow_kr_exception();
1288             }
1289              
1290             sub _rethrow_kr_exception {
1291 178     11   380 my $self = shift;
1292              
1293             # It's quite common to see people wrap POE::Kernel->run() in an eval
1294             # block and start things again if an exception is caught.
1295             #
1296             # This little lexical dance is actually important. It allows
1297             # $kr_exception to be cleared if the die() is caught.
1298              
1299 176         869 my $exception = $kr_exception;
1300 11         17 $kr_exception = undef;
1301              
1302             # The die is cast.
1303 11         45 die $exception;
1304             }
1305              
1306             # Stops the kernel cold. XXX Experimental!
1307             # No events happen as a result of this, all structures are cleaned up
1308             # except the kernel's. Even the current session and POE::Kernel are
1309             # cleaned up, which may introduce inconsistencies in the current
1310             # session... as _dispatch_event() attempts to clean up for a defunct
1311             # session.
1312              
1313             sub stop {
1314             # So stop() can be called as a class method.
1315 18     18 1 46 my $self = $poe_kernel;
1316              
1317             # May be called when the kernel's already stopped. Avoid problems
1318             # trying to find child sessions when the kernel isn't registered.
1319 18 100       49 if ($self->_data_ses_exists($self->ID)) {
1320 7         18 my @children = ($self);
1321 7         13 foreach my $session (@children) {
1322 19         37 push @children, $self->_data_ses_get_children($session->ID);
1323             }
1324              
1325             # Don't stop believin'. Nor the POE::Kernel singleton.
1326 7         15 shift @children;
1327              
1328             # Walk backwards to avoid inconsistency errors.
1329 7         15 foreach my $session (reverse @children) {
1330 12         37 $self->_data_ses_free($session->ID);
1331             }
1332             }
1333              
1334             # Roll back whether sessions were started.
1335 18         76 $kr_run_warning &= ~KR_RUN_SESSION;
1336              
1337             # So new sessions will not be child of the current defunct session.
1338 18         32 $kr_active_session = $self;
1339              
1340             # The GC mark list may prevent sessions from DESTROYing.
1341             # Clean it up.
1342 18         58 $self->_data_ses_gc_sweep();
1343              
1344             # Running stop() is recommended in a POE::Wheel::Run coderef
1345             # Program, before setting up for the next POE::Kernel->run(). When
1346             # the PID has changed, imply _data_sig_has_forked() during stop().
1347              
1348 18 50       69 $poe_kernel->has_forked() if $kr_pid != $$;
1349              
1350             # TODO - If we're polling for signals, then the reset gets it wrong.
1351             # The reset doesn't count sigchld polling. If we must put this
1352             # back, it MUST account for all internal events currently in play,
1353             # or the child process will stall if it reruns POE::Kernel's loop.
1354             #_idle_queue_reset();
1355              
1356 18         78 return;
1357             }
1358              
1359             # Less invasive form of ->stop() + ->run()
1360             sub has_forked {
1361 4 50   4 1 4793 if( $kr_pid == $$ ) {
1362 0         0 if ( ASSERT_USAGE ) {
1363             _warn "You should only call ->has_forked() from the child process.";
1364             }
1365 0         0 return;
1366             }
1367              
1368             # So has_forked() can be called as a class method.
1369 0         0 my $self = $poe_kernel;
1370              
1371 4         72 $kr_pid = $$;
1372 4         95 $self->_recalc_id();
1373              
1374             # reset some stuff for the signals
1375 4         179 $poe_kernel->_data_sig_has_forked;
1376             }
1377              
1378             #------------------------------------------------------------------------------
1379              
1380             sub DESTROY {
1381 4     0   84 my $self = shift;
1382              
1383             # Warn that a session never had the opportunity to run if one was
1384             # created but run() was never called.
1385              
1386 0 0       0 unless ($kr_run_warning & KR_RUN_CALLED) {
1387 0 0       0 if ($kr_run_warning & KR_RUN_SESSION) {
1388 0         0 _warn(
1389             "Sessions were started, but POE::Kernel's run() method was never\n",
1390             "called to execute them. This usually happens because an error\n",
1391             "occurred before POE::Kernel->run() could be called. Please fix\n",
1392             "any errors above this notice, and be sure that POE::Kernel->run()\n",
1393             "is called. See documentation for POE::Kernel's run() method for\n",
1394             "another way to disable this warning.\n",
1395             );
1396             }
1397             }
1398             }
1399              
1400             #------------------------------------------------------------------------------
1401             # _invoke_state is what _dispatch_event calls to dispatch a transition
1402             # event. This is the kernel's _invoke_state so it can receive events.
1403             # These are mostly signals, which are propagated down in
1404             # _dispatch_event.
1405              
1406             sub _invoke_state {
1407 1572     1572   2819 my ($self, $source_session, $event, $etc) = @_;
1408              
1409             # This is an event loop to poll for child processes without needing
1410             # to catch SIGCHLD.
1411              
1412 1572 100       5061 if ($event eq EN_SCPOLL) {
    100          
1413 360         2288 $self->_data_sig_handle_poll_event($etc->[0]);
1414             }
1415              
1416             # A signal was posted. Because signals propagate depth-first, this
1417             # _invoke_state is called last in the dispatch. If the signal was
1418             # SIGIDLE, then post a SIGZOMBIE if the main queue is still idle.
1419              
1420             elsif ($event eq EN_SIGNAL) {
1421 189 50       1522 if ($etc->[0] eq 'IDLE') {
1422 189 50 33     683 unless (
1423             $kr_queue->get_item_count() > $idle_queue_size or
1424             $self->_data_handle_count()
1425             ) {
1426 189         1186 $self->_data_ev_enqueue(
1427             $self, $self, EN_SIGNAL, ET_SIGNAL, [ 'ZOMBIE' ],
1428             __FILE__, __LINE__, undef
1429             );
1430             }
1431             }
1432             }
1433              
1434 1572         3202 return 0;
1435             }
1436              
1437             #==============================================================================
1438             # SESSIONS
1439             #==============================================================================
1440              
1441             # Dispatch _start to a session, allocating it in the kernel's data
1442             # structures as a side effect.
1443              
1444             sub session_alloc {
1445 799     799 1 3238 my ($self, $session, @args) = ($poe_kernel, @_[1..$#_]);
1446              
1447             # If we already returned, then we must reinitialize. This is so
1448             # $poe_kernel->run() will work correctly more than once.
1449 799 100       2098 if ($kr_run_warning & KR_RUN_DONE) {
1450 94         132 $kr_run_warning &= ~KR_RUN_DONE;
1451 94         357 $self->_initialize_kernel_session();
1452 94         429 $self->_data_sig_initialize();
1453             }
1454              
1455 799         926 if (ASSERT_DATA) {
1456             if (defined $session->ID) {
1457             _trap(
1458             " ", $self->_data_alias_loggable($session->ID),
1459             " already allocated\a"
1460             );
1461             }
1462             }
1463              
1464             # Register that a session was created.
1465 799         2143 $kr_run_warning |= KR_RUN_SESSION;
1466              
1467             # Allocate the session's data structure. This must be done before
1468             # we dispatch anything regarding the new session.
1469 155         897 my $new_sid = $self->_data_sid_allocate();
1470 798         1499 $session->_set_id($new_sid);
1471 798         3080 $self->_data_ses_allocate($session, $new_sid, $kr_active_session->ID);
1472              
1473 798         2666 my $loggable = $self->_data_alias_loggable($new_sid);
1474              
1475             # Tell the new session that it has been created. Catch the _start
1476             # state's return value so we can pass it to the parent with the
1477             # _child create.
1478             #
1479             # TODO - Void the context if the parent has no _child handler?
1480              
1481 798         2775 my $return = $self->_dispatch_event(
1482             $session, $kr_active_session,
1483             EN_START, ET_START, \@args,
1484             __FILE__, __LINE__, undef, monotime(), -__LINE__
1485             );
1486              
1487 784 100       3542 unless($self->_data_ses_exists($new_sid)) {
1488 644         4280 if(TRACE_SESSIONS) {
1489             _warn(" ", $loggable, " disappeared during ", EN_START);
1490             }
1491 594         3072 return $return;
1492             }
1493              
1494             # If the child has not detached itself---that is, if its parent is
1495             # the currently active session---then notify the parent with a
1496             # _child create event. Otherwise skip it, since we'd otherwise
1497             # throw a create without a lose.
1498             $self->_dispatch_event(
1499 126         427 $self->_data_ses_get_parent($session->ID), $self,
1500             EN_CHILD, ET_CHILD, [ CHILD_CREATE, $session, $return ],
1501             __FILE__, __LINE__, undef, monotime(), -__LINE__
1502             );
1503              
1504 140 50       538 unless ($self->_data_ses_exists($new_sid)) {
1505 249         711 if (TRACE_SESSIONS) {
1506             _warn(" ", $loggable, " disappeared during ", EN_CHILD, " dispatch");
1507             }
1508 594         2084 return $return;
1509             }
1510              
1511             # Enqueue a delayed garbage-collection event so the session has time
1512             # to do its thing before it goes.
1513             $self->_data_ev_enqueue(
1514 485         2620 $session, $session, EN_GC, ET_GC, [],
1515             __FILE__, __LINE__, undef
1516             );
1517             }
1518              
1519             # Detach a session from its parent. This breaks the parent/child
1520             # relationship between the current session and its parent. Basically,
1521             # the current session is given to the Kernel session. Unlike with
1522             # _stop, the current session's children follow their parent.
1523              
1524             sub detach_myself {
1525 11     11 1 1356 my $self = $poe_kernel;
1526              
1527 260         2721 if (ASSERT_USAGE) {
1528             _confess " must call detach_myself() from a running session"
1529             if $kr_active_session == $self;
1530             }
1531              
1532             # Can't detach from the kernel.
1533 11 100       41 if ($self->_data_ses_get_parent($kr_active_session->ID) == $self) {
1534 369         5222 $! = EPERM;
1535 0         0 return;
1536             }
1537              
1538 0         0 my $old_parent = $self->_data_ses_get_parent($kr_active_session->ID);
1539              
1540             # Tell the old parent session that the child is departing.
1541             # But not if the active event is ET_START, since that would generate
1542             # a CHILD_LOSE without a CHILD_CREATE.
1543 10 50       33 $self->_dispatch_event(
1544             $old_parent, $self,
1545             EN_CHILD, ET_CHILD, [ CHILD_LOSE, $kr_active_session, undef ],
1546             (caller)[1,2], undef, monotime(), -__LINE__
1547             )
1548             unless $kr_active_event_type & ET_START;
1549              
1550             # Tell the new parent (kernel) that it's gaining a child.
1551             # (Actually it doesn't care, so we don't do that here, but this is
1552             # where the code would go if it ever does in the future.)
1553              
1554             # Tell the current session that its parentage is changing.
1555 10         91 $self->_dispatch_event(
1556             $kr_active_session, $self,
1557             EN_PARENT, ET_PARENT, [ $old_parent, $self ],
1558             (caller)[1,2], undef, monotime(), -__LINE__
1559             );
1560              
1561 10         87 $self->_data_ses_move_child($kr_active_session->ID, $self->ID);
1562              
1563             # Success!
1564 10         46 return 1;
1565             }
1566              
1567             # Detach a child from this, the parent. The session being detached
1568             # must be a child of the current session.
1569              
1570             sub detach_child {
1571 20     10 1 2526 my ($self, $child) = ($poe_kernel, @_[1..$#_]);
1572              
1573 10         15 if (ASSERT_USAGE) {
1574             _confess " must call detach_child() from a running session"
1575             if $kr_active_session == $self;
1576             }
1577              
1578 10         49 my $child_session = $self->_resolve_session($child);
1579 9 100       22 unless (defined $child_session) {
1580 9         21 $self->_explain_resolve_failure($child);
1581 0         0 return;
1582             }
1583              
1584             # Can't detach if it belongs to the kernel. TODO We shouldn't need
1585             # to check for this.
1586 0 50       0 if ($kr_active_session == $self) {
1587 8         27 $! = EPERM;
1588 0         0 return;
1589             }
1590              
1591             # Can't detach if it's not a child of the current session.
1592 0 50       0 unless (
1593             $self->_data_ses_is_child($kr_active_session->ID, $child_session->ID)
1594             ) {
1595 8         25 $! = EPERM;
1596 0         0 return;
1597             }
1598              
1599             # Tell the current session that the child is departing.
1600             $self->_dispatch_event(
1601 0         0 $kr_active_session, $self,
1602             EN_CHILD, ET_CHILD, [ CHILD_LOSE, $child_session, undef ],
1603             (caller)[1,2], undef, monotime(), -__LINE__
1604             );
1605              
1606             # Tell the new parent (kernel) that it's gaining a child.
1607             # (Actually it doesn't care, so we don't do that here, but this is
1608             # where the code would go if it ever does in the future.)
1609              
1610             # Tell the child session that its parentage is changing.
1611 8         70 $self->_dispatch_event(
1612             $child_session, $self,
1613             EN_PARENT, ET_PARENT, [ $kr_active_session, $self ],
1614             (caller)[1,2], undef, monotime(), -__LINE__
1615             );
1616              
1617 8         75 $self->_data_ses_move_child($child_session->ID, $self->ID);
1618              
1619             # Success!
1620 8         38 return 1;
1621             }
1622              
1623             ### Helpful accessors.
1624              
1625             sub get_active_session {
1626 986     978 1 8049 return $kr_active_session;
1627             }
1628              
1629             sub get_active_event {
1630 0     0 1 0 return $kr_active_event;
1631             }
1632              
1633             # FIXME - Should this exist?
1634             sub get_event_count {
1635 0     0 1 0 return $kr_queue->get_item_count();
1636             }
1637              
1638             # FIXME - Should this exist?
1639             sub get_next_event_time {
1640 0     0 1 0 return $kr_queue->get_next_priority();
1641             }
1642              
1643             #==============================================================================
1644             # EVENTS
1645             #==============================================================================
1646              
1647             #------------------------------------------------------------------------------
1648             # Post an event to the queue.
1649              
1650             sub post {
1651 325     325 1 16934 my ($self, $dest_session, $event_name, @etc) = ($poe_kernel, @_[1..$#_]);
1652              
1653 325         746 if (ASSERT_USAGE) {
1654             _confess " destination is undefined in post()"
1655             unless defined $dest_session;
1656             _confess " event is undefined in post()" unless defined $event_name;
1657             _carp(
1658             " The '$event_name' event is one of POE's own. Its " .
1659             "effect cannot be achieved by posting it"
1660             ) if exists $poes_own_events{$event_name};
1661             };
1662              
1663             # Attempt to resolve the destination session reference against
1664             # various things.
1665              
1666 325         752 my $session = $self->_resolve_session($dest_session);
1667 324 100       701 unless (defined $session) {
1668 316         808 $self->_explain_resolve_failure($dest_session);
1669 315         881 return;
1670             }
1671              
1672             # Enqueue the event for "now", which simulates FIFO in our
1673             # time-ordered queue.
1674              
1675             $self->_data_ev_enqueue(
1676 322         737 $session, $kr_active_session, $event_name, ET_POST, \@etc,
1677             (caller)[1,2], $kr_active_event
1678             );
1679 7         17 return 1;
1680             }
1681              
1682             #------------------------------------------------------------------------------
1683             # Post an event to the queue for the current session.
1684              
1685             sub yield {
1686 752     752 1 4041938 my ($self, $event_name, @etc) = ($poe_kernel, @_[1..$#_]);
1687              
1688 1067         2741 if (ASSERT_USAGE) {
1689             _confess " must call yield() from a running session"
1690             if $kr_active_session == $self;
1691             _confess " event name is undefined in yield()"
1692             unless defined $event_name;
1693             _carp(
1694             " The '$event_name' event is one of POE's own. Its " .
1695             "effect cannot be achieved by yielding it"
1696             ) if exists $poes_own_events{$event_name};
1697             };
1698              
1699 1067         3440 $self->_data_ev_enqueue(
1700             $kr_active_session, $kr_active_session, $event_name, ET_POST, \@etc,
1701             (caller)[1,2], $kr_active_event
1702             );
1703              
1704 751         1594 undef;
1705             }
1706              
1707             #------------------------------------------------------------------------------
1708             # Call an event handler directly.
1709              
1710             sub call {
1711 4173     3480 1 28278 my ($self, $dest_session, $event_name, @etc) = ($poe_kernel, @_[1..$#_]);
1712              
1713 4173         7502 if (ASSERT_USAGE) {
1714             _confess " destination is undefined in call()"
1715             unless defined $dest_session;
1716             _confess " event is undefined in call()" unless defined $event_name;
1717             _carp(
1718             " The '$event_name' event is one of POE's own. Its " .
1719             "effect cannot be achieved by calling it"
1720             ) if exists $poes_own_events{$event_name};
1721             };
1722              
1723             # Attempt to resolve the destination session reference against
1724             # various things.
1725              
1726 4173         9522 my $session = $self->_resolve_session($dest_session);
1727 3479 100       6464 unless (defined $session) {
1728 1030         2448 $self->_explain_resolve_failure($dest_session);
1729 1029         2284 return;
1730             }
1731              
1732             # Dispatch the event right now, bypassing the queue altogether.
1733             # This tends to be a Bad Thing to Do.
1734              
1735             # TODO The difference between synchronous and asynchronous events
1736             # should be made more clear in the documentation, so that people
1737             # have a tendency not to abuse them. I discovered in xws that
1738             # mixing the two types makes it harder than necessary to write
1739             # deterministic programs, but the difficulty can be ameliorated if
1740             # programmers set some base rules and stick to them.
1741              
1742 3477 100       5845 if (wantarray) {
1743 2 100       18 my @return_value = (
1744             ($session == $kr_active_session)
1745             ? $session->_invoke_state(
1746             $session, $event_name, \@etc, (caller)[1,2],
1747             $kr_active_event
1748             )
1749             : $self->_dispatch_event(
1750             $session, $kr_active_session,
1751             $event_name, ET_CALL, \@etc,
1752             (caller)[1,2], $kr_active_event, monotime(), -__LINE__
1753             )
1754             );
1755              
1756 1 50       457 $kr_exception and $self->_rethrow_kr_exception();
1757              
1758 1030         1900 $! = 0;
1759 1         4 return @return_value;
1760             }
1761              
1762 2446 100       4560 if (defined wantarray) {
1763 2 50       21 my $return_value = (
1764             $session == $kr_active_session
1765             ? $session->_invoke_state(
1766             $session, $event_name, \@etc, (caller)[1,2],
1767             $kr_active_event
1768             )
1769             : $self->_dispatch_event(
1770             $session, $kr_active_session,
1771             $event_name, ET_CALL, \@etc,
1772             (caller)[1,2], $kr_active_event, monotime(), -__LINE__
1773             )
1774             );
1775              
1776 2 50       841 $kr_exception and $self->_rethrow_kr_exception();
1777              
1778 1031         1927 $! = 0;
1779 167         1126 return $return_value;
1780             }
1781              
1782 2609 100       5395 if ($session == $kr_active_session) {
1783 2600         10823 $session->_invoke_state(
1784             $session, $event_name, \@etc, (caller)[1,2],
1785             $kr_active_event
1786             );
1787             }
1788             else {
1789 174         502 $self->_dispatch_event(
1790             $session, $kr_active_session,
1791             $event_name, ET_CALL, \@etc,
1792             (caller)[1,2], $kr_active_event, monotime(), -__LINE__
1793             );
1794             }
1795              
1796 3297 50       27180 $kr_exception and $self->_rethrow_kr_exception();
1797              
1798 3231         8166 $! = 0;
1799 2499         10538 return;
1800             }
1801              
1802             #==============================================================================
1803             # DELAYED EVENTS
1804             #==============================================================================
1805              
1806             sub alarm {
1807 3565     2701 1 59060 my ($self, $event_name, $time, @etc) = ($poe_kernel, @_[1..$#_]);
1808              
1809 3565         4166 if (ASSERT_USAGE) {
1810             _confess " must call alarm() from a running session"
1811             if $kr_active_session == $self;
1812             _confess " event name is undefined in alarm()"
1813             unless defined $event_name;
1814             _carp(
1815             " The '$event_name' event is one of POE's own. Its " .
1816             "effect cannot be achieved by setting an alarm for it"
1817             ) if exists $poes_own_events{$event_name};
1818             };
1819              
1820 3565 100       10114 unless (defined $event_name) {
1821 2539         4672 $self->_explain_return("invalid parameter to alarm() call");
1822 2537         5657 return EINVAL;
1823             }
1824              
1825 2698         4380 $self->_data_ev_clear_alarm_by_name($kr_active_session->ID(), $event_name);
1826              
1827             # Add the new alarm if it includes a time. Calling _data_ev_enqueue
1828             # directly is faster than calling alarm_set to enqueue it.
1829 161 50       473 if (defined $time) {
1830 0         0 $self->_data_ev_enqueue
1831             ( $kr_active_session, $kr_active_session,
1832             $event_name, ET_ALARM, [ @etc ],
1833             (caller)[1,2], $kr_active_event, $time,
1834             );
1835             }
1836             else {
1837             # The event queue has become empty? Stop the time watcher.
1838 2698 100       5056 $self->loop_pause_time_watcher() unless $kr_queue->get_item_count();
1839             }
1840              
1841 2698         7013 return 0;
1842             }
1843              
1844             # Add an alarm without clobbering previous alarms of the same name.
1845             sub alarm_add {
1846 1258     16 1 8900 my ($self, $event_name, $time, @etc) = ($poe_kernel, @_[1..$#_]);
1847              
1848 1311         3132 if (ASSERT_USAGE) {
1849             _confess " must call alarm_add() from a running session"
1850             if $kr_active_session == $self;
1851             _confess " undefined event name in alarm_add()"
1852             unless defined $event_name;
1853             _confess " undefined time in alarm_add()" unless defined $time;
1854             _carp(
1855             " The '$event_name' event is one of POE's own. Its " .
1856             "effect cannot be achieved by adding an alarm for it"
1857             ) if exists $poes_own_events{$event_name};
1858             };
1859              
1860 2553 100 66     8384 unless (defined $event_name and defined $time) {
1861 15         31 $self->_explain_return("invalid parameter to alarm_add() call");
1862 13         22 return EINVAL;
1863             }
1864              
1865             $self->_data_ev_enqueue
1866 12         27 ( $kr_active_session, $kr_active_session,
1867             $event_name, ET_ALARM, [ @etc ],
1868             (caller)[1,2], $kr_active_event, $time,
1869             );
1870              
1871 12         44 return 0;
1872             }
1873              
1874             # Add a delay, which is like an alarm relative to the current time.
1875             sub delay {
1876 1102     1102 1 19584 my ($self, $event_name, $delay, @etc) = ($poe_kernel, @_[1..$#_]);
1877 1102         2837 my $pri = monotime();
1878              
1879 1114         1262 if (ASSERT_USAGE) {
1880             _confess " must call delay() from a running session"
1881             if $kr_active_session == $self;
1882             _confess " undefined event name in delay()" unless defined $event_name;
1883             _carp(
1884             " The '$event_name' event is one of POE's own. Its " .
1885             "effect cannot be achieved by setting a delay for it"
1886             ) if exists $poes_own_events{$event_name};
1887             };
1888              
1889 1114 100       3287 unless (defined $event_name) {
1890 286         768 $self->_explain_return("invalid parameter to delay() call");
1891 284         870 return EINVAL;
1892             }
1893              
1894 1099 100       2301 if (defined $delay) {
1895 654         1974 $self->_data_ev_clear_alarm_by_name($kr_active_session->ID(), $event_name);
1896              
1897             # Add the new alarm if it includes a time. Calling _data_ev_enqueue
1898             # directly is faster than calling alarm_set to enqueue it.
1899 654         6050 $self->_data_ev_enqueue
1900             ( $kr_active_session, $kr_active_session,
1901             $event_name, ET_ALARM, [ @etc ],
1902             (caller)[1,2], $kr_active_event, undef, $delay, $pri+$delay
1903             );
1904             }
1905             else {
1906 445         1120 $self->alarm($event_name);
1907             }
1908              
1909 1046         2934 return 0;
1910             }
1911              
1912             # Add a delay without clobbering previous delays of the same name.
1913             sub delay_add {
1914 241     10 1 3513 my ($self, $event_name, $delay, @etc) = ($poe_kernel, @_[1..$#_]);
1915 63         212 my $pri = monotime();
1916              
1917 294         1005 if (ASSERT_USAGE) {
1918             _confess " must call delay_add() from a running session"
1919             if $kr_active_session == $self;
1920             _confess " undefined event name in delay_add()"
1921             unless defined $event_name;
1922             _confess " undefined time in delay_add()" unless defined $delay;
1923             _carp(
1924             " The '$event_name' event is one of POE's own. Its " .
1925             "effect cannot be achieved by adding a delay for it"
1926             ) if exists $poes_own_events{$event_name};
1927             };
1928              
1929 10 100 66     46 unless (defined $event_name and defined $delay) {
1930 9         35 $self->_explain_return("invalid parameter to delay_add() call");
1931 7         25 return EINVAL;
1932             }
1933              
1934             $self->_data_ev_enqueue
1935 6         24 ( $kr_active_session, $kr_active_session,
1936             $event_name, ET_ALARM, [ @etc ],
1937             (caller)[1,2], $kr_active_event, undef, $delay, $pri+$delay
1938             );
1939              
1940 6         51 return 0;
1941             }
1942              
1943             #------------------------------------------------------------------------------
1944             # New style alarms.
1945              
1946             # Set an alarm. This does more *and* less than plain alarm(). It
1947             # only sets alarms (that's the less part), but it also returns an
1948             # alarm ID (that's the more part).
1949              
1950             sub alarm_set {
1951 241     241 1 7186 my ($self, $event_name, $time, @etc) = ($poe_kernel, @_[1..$#_]);
1952              
1953 241         273 if (ASSERT_USAGE) {
1954             _confess " must call alarm_set() from a running session"
1955             if $kr_active_session == $self;
1956             }
1957              
1958 247 100       623 unless (defined $event_name) {
1959 245         465 $self->_explain_usage("undefined event name in alarm_set()");
1960 1         5 $! = EINVAL;
1961 0         0 return;
1962             }
1963              
1964 1 100       3 unless (defined $time) {
1965 238         430 $self->_explain_usage("undefined time in alarm_set()");
1966 1         4 $! = EINVAL;
1967 0         0 return;
1968             }
1969              
1970 0         0 if (ASSERT_USAGE) {
1971             _carp(
1972             " The '$event_name' event is one of POE's own. Its " .
1973             "effect cannot be achieved by setting an alarm for it"
1974             ) if exists $poes_own_events{$event_name};
1975             }
1976              
1977 236         200 return $self->_data_ev_enqueue
1978             ( $kr_active_session, $kr_active_session, $event_name, ET_ALARM, [ @etc ],
1979             (caller)[1,2], $kr_active_event, $time,
1980             );
1981             }
1982              
1983             # Remove an alarm by its ID. TODO Now that alarms and events have
1984             # been recombined, this will remove an event by its ID. However,
1985             # nothing returns an event ID, so nobody knows what to remove.
1986              
1987             sub alarm_remove {
1988 466     230 1 6424 my ($self, $alarm_id) = ($poe_kernel, @_[1..$#_]);
1989              
1990 466         1413 if (ASSERT_USAGE) {
1991             _confess " must call alarm_remove() from a running session"
1992             if $kr_active_session == $self;
1993             }
1994              
1995 230 100       495 unless (defined $alarm_id) {
1996 221         396 $self->_explain_usage("undefined alarm id in alarm_remove()");
1997 1         4 $! = EINVAL;
1998 0         0 return;
1999             }
2000              
2001 8         20 my ($time, $event) =
2002             $self->_data_ev_clear_alarm_by_id($kr_active_session->ID(), $alarm_id);
2003 227 100       449 return unless defined $time;
2004              
2005             # In a list context, return the alarm that was removed. In a scalar
2006             # context, return a reference to the alarm that was removed. In a
2007             # void context, return nothing. Either way this returns a defined
2008             # value when someone needs something useful from it.
2009              
2010 227 100       468 return unless defined wantarray;
2011 219 100       875 return ( $event->[EV_NAME], $time, $event->[EV_ARGS] ) if wantarray;
2012 4         17 return [ $event->[EV_NAME], $time, $event->[EV_ARGS] ];
2013             }
2014              
2015             # Move an alarm to a new time. This virtually removes the alarm and
2016             # re-adds it somewhere else. In reality, adjust_priority() is
2017             # optimized for this sort of thing.
2018              
2019             sub alarm_adjust {
2020 37     35 1 9304 my ($self, $alarm_id, $delta) = ($poe_kernel, @_[1..$#_]);
2021              
2022 35         42 if (ASSERT_USAGE) {
2023             _confess " must call alarm_adjust() from a running session"
2024             if $kr_active_session == $self;
2025             }
2026              
2027 35 100       103 unless (defined $alarm_id) {
2028 33         74 $self->_explain_usage("undefined alarm id in alarm_adjust()");
2029 1         6 $! = EINVAL;
2030 0         0 return;
2031             }
2032              
2033 1 100       4 unless (defined $delta) {
2034 32         67 $self->_explain_usage("undefined alarm delta in alarm_adjust()");
2035 1         4 $! = EINVAL;
2036 0         0 return;
2037             }
2038              
2039             my $my_alarm = sub {
2040 30     3758   142 $_[0]->[EV_SESSION] == $kr_active_session;
2041 0         0 };
2042            
2043 3758         9247 return $self->_data_ev_adjust( $alarm_id, $my_alarm, undef, $delta );
2044             }
2045              
2046             # A convenient function for setting alarms relative to now. It also
2047             # uses whichever time() POE::Kernel can find, which may be
2048             # Time::HiRes'.
2049              
2050             sub delay_set {
2051             # Always always always grab time() ASAP, so that the eventual
2052             # time we set the delay for is as close as possible to the time
2053             # at which they ASKED for the delay, not when we actually set it.
2054 50     20 1 5768 my $t = walltime();
2055 20         65 my $pri = monotime();
2056              
2057             # And now continue as normal
2058 20         99 my ($self, $event_name, $seconds, @etc) = ($poe_kernel, @_[1..$#_]);
2059              
2060 20         37 if (ASSERT_USAGE) {
2061             _confess " must call delay_set() from a running session"
2062             if $kr_active_session == $self;
2063             }
2064              
2065 20 100       74 unless (defined $event_name) {
2066 10         68 $self->_explain_usage("undefined event name in delay_set()");
2067 1         6 $! = EINVAL;
2068 0         0 return;
2069             }
2070              
2071 9         10 if (ASSERT_USAGE) {
2072             _carp(
2073             " The '$event_name' event is one of POE's own. Its " .
2074             "effect cannot be achieved by setting a delay for it"
2075             ) if exists $poes_own_events{$event_name};
2076             }
2077              
2078 17 100       57 unless (defined $seconds) {
2079 9         38 $self->_explain_usage("undefined seconds in delay_set()");
2080 8         30 $! = EINVAL;
2081 1         6 return;
2082             }
2083              
2084 8         163 return $self->_data_ev_enqueue
2085             ( $kr_active_session, $kr_active_session, $event_name, ET_ALARM, [ @etc ],
2086             (caller)[1,2], $kr_active_event, $t, $seconds, $pri+$seconds
2087             );
2088             }
2089              
2090             # Move a delay to a new offset from time(). As with alarm_adjust(),
2091             # this is optimized internally for this sort of activity.
2092              
2093             sub delay_adjust {
2094             # Always always always grab time() ASAP, so that the eventual
2095             # time we set the delay for is as close as possible to the time
2096             # at which they ASKED for the delay, not when we actually set it.
2097 5     5 1 2534 my $t = walltime();
2098 12         128 my $pri = monotime();
2099              
2100             # And now continue as normal
2101 5         22 my ($self, $alarm_id, $seconds) = ($poe_kernel, @_[1..$#_]);
2102              
2103 5         8 if (ASSERT_USAGE) {
2104             _confess " must call delay_adjust() from a running session"
2105             if $kr_active_session == $self;
2106             }
2107              
2108 5 100       23 unless (defined $alarm_id) {
2109 3         12 $self->_explain_usage("undefined delay id in delay_adjust()");
2110 1         5 $! = EINVAL;
2111 0         0 return;
2112             }
2113              
2114 1 100       6 unless (defined $seconds) {
2115 2         8 $self->_explain_usage("undefined delay seconds in delay_adjust()");
2116 1         6 $! = EINVAL;
2117 0         0 return;
2118             }
2119              
2120             my $my_delay = sub {
2121 0     0   0 $_[0]->[EV_SESSION] == $kr_active_session;
2122 0         0 };
2123              
2124 0         0 if (TRACE_EVENTS) {
2125             _warn(" adjusted event $alarm_id by $seconds seconds from $t");
2126             }
2127              
2128 0         0 return $self->_data_ev_set($alarm_id, $my_delay, $t, $pri, $seconds );
2129             }
2130              
2131             # Remove all alarms for the current session.
2132              
2133             sub alarm_remove_all {
2134 4     4 1 647 my $self = $poe_kernel;
2135              
2136 4         8 if (ASSERT_USAGE) {
2137             _confess " must call alarm_remove_all() from a running session"
2138             if $kr_active_session == $self;
2139             }
2140              
2141             # This should never happen, actually.
2142 4 100       19 _trap "unknown session in alarm_remove_all call" unless (
2143             $self->_data_ses_exists($kr_active_session->ID)
2144             );
2145              
2146             # Free every alarm owned by the session. This code is ripped off
2147             # from the _stop code to flush everything.
2148              
2149 2         8 my @removed = $self->_data_ev_clear_alarm_by_session(
2150             $kr_active_session->ID()
2151             );
2152              
2153 2 50       8 return unless defined wantarray;
2154 2 50       11 return @removed if wantarray;
2155 0         0 return \@removed;
2156             }
2157              
2158             #==============================================================================
2159             # SELECTS
2160             #==============================================================================
2161              
2162             sub _internal_select {
2163 2722     2722   4334 my ($self, $session, $handle, $event_name, $mode, $args) = @_;
2164              
2165             # If an event is included, then we're defining a filehandle watcher.
2166              
2167 2722 100       4782 if ($event_name) {
2168 1158         15617 $self->_data_handle_add($handle, $mode, $session, $event_name, $args);
2169             }
2170             else {
2171 1564         4076 $self->_data_handle_remove($handle, $mode, $session->ID);
2172             }
2173             }
2174              
2175             # A higher-level select() that manipulates read, write and expedite
2176             # selects together.
2177              
2178             sub select {
2179 112     112 1 1996 my ($self, $handle, $event_r, $event_w, $event_e, @args) = (
2180             $poe_kernel, @_[1..$#_]
2181             );
2182              
2183 112         140 if (ASSERT_USAGE) {
2184             _confess " must call select() from a running session"
2185             if $kr_active_session == $self;
2186             _confess " undefined filehandle in select()" unless defined $handle;
2187             _confess " invalid filehandle in select()"
2188             unless defined fileno($handle);
2189             foreach ($event_r, $event_w, $event_e) {
2190             next unless defined $_;
2191             _carp(
2192             " The '$_' event is one of POE's own. Its " .
2193             "effect cannot be achieved by setting a file watcher to it"
2194             ) if exists($poes_own_events{$_});
2195             }
2196             }
2197              
2198             $self->_internal_select(
2199 112         350 $kr_active_session, $handle, $event_r, MODE_RD, \@args
2200             );
2201 111         343 $self->_internal_select(
2202             $kr_active_session, $handle, $event_w, MODE_WR, \@args
2203             );
2204 110         320 $self->_internal_select(
2205             $kr_active_session, $handle, $event_e, MODE_EX, \@args
2206             );
2207 109         187 return 0;
2208             }
2209              
2210             # Only manipulate the read select.
2211             sub select_read {
2212 1786     1504 1 15315 my ($self, $handle, $event_name, @args) = ($poe_kernel, @_[1..$#_]);
2213              
2214 1506         1694 if (ASSERT_USAGE) {
2215             _confess " must call select_read() from a running session"
2216             if $kr_active_session == $self;
2217             _confess " undefined filehandle in select_read()"
2218             unless defined $handle;
2219             _confess " invalid filehandle in select_read()"
2220             unless defined fileno($handle);
2221             _carp(
2222             " The '$event_name' event is one of POE's own. Its " .
2223             "effect cannot be achieved by setting a file watcher to it"
2224             ) if defined($event_name) and exists($poes_own_events{$event_name});
2225             };
2226              
2227 1598         4177 $self->_internal_select(
2228             $kr_active_session, $handle, $event_name, MODE_RD, \@args
2229             );
2230 1597         5996 return 0;
2231             }
2232              
2233             # Only manipulate the write select.
2234             sub select_write {
2235 1838     901 1 8232 my ($self, $handle, $event_name, @args) = ($poe_kernel, @_[1..$#_]);
2236              
2237 1833         5165 if (ASSERT_USAGE) {
2238             _confess " must call select_write() from a running session"
2239             if $kr_active_session == $self;
2240             _confess " undefined filehandle in select_write()"
2241             unless defined $handle;
2242             _confess " invalid filehandle in select_write()"
2243             unless defined fileno($handle);
2244             _carp(
2245             " The '$event_name' event is one of POE's own. Its " .
2246             "effect cannot be achieved by setting a file watcher to it"
2247             ) if defined($event_name) and exists($poes_own_events{$event_name});
2248             };
2249              
2250 1739         5906 $self->_internal_select(
2251             $kr_active_session, $handle, $event_name, MODE_WR, \@args
2252             );
2253 1738         7158 return 0;
2254             }
2255              
2256             # Only manipulate the expedite select.
2257             sub select_expedite {
2258 673     3 1 3348 my ($self, $handle, $event_name, @args) = ($poe_kernel, @_[1..$#_]);
2259              
2260 672         10154 if (ASSERT_USAGE) {
2261             _confess " must call select_expedite() from a running session"
2262             if $kr_active_session == $self;
2263             _confess " undefined filehandle in select_expedite()"
2264             unless defined $handle;
2265             _confess " invalid filehandle in select_expedite()"
2266             unless defined fileno($handle);
2267             _carp(
2268             " The '$event_name' event is one of POE's own. Its " .
2269             "effect cannot be achieved by setting a file watcher to it"
2270             ) if defined($event_name) and exists($poes_own_events{$event_name});
2271             };
2272              
2273 672         3124 $self->_internal_select(
2274             $kr_active_session, $handle, $event_name, MODE_EX, \@args
2275             );
2276 671         1941 return 0;
2277             }
2278              
2279             # Turn off a handle's write mode bit without doing
2280             # garbage-collection things.
2281             sub select_pause_write {
2282 819     818 1 4559 my ($self, $handle) = ($poe_kernel, @_[1..$#_]);
2283              
2284 818         13097 if (ASSERT_USAGE) {
2285             _confess " must call select_pause_write() from a running session"
2286             if $kr_active_session == $self;
2287             _confess " undefined filehandle in select_pause_write()"
2288             unless defined $handle;
2289             _confess " invalid filehandle in select_pause_write()"
2290             unless defined fileno($handle);
2291             };
2292              
2293 818 100       2440 return 0 unless $self->_data_handle_is_good($handle, MODE_WR);
2294              
2295 817         2686 $self->_data_handle_pause($handle, MODE_WR);
2296              
2297 816         2435 return 1;
2298             }
2299              
2300             # Turn on a handle's write mode bit without doing garbage-collection
2301             # things.
2302             sub select_resume_write {
2303 1113     496 1 7757 my ($self, $handle) = ($poe_kernel, @_[1..$#_]);
2304              
2305 1113         5111 if (ASSERT_USAGE) {
2306             _confess " must call select_resume_write() from a running session"
2307             if $kr_active_session == $self;
2308             _confess " undefined filehandle in select_resume_write()"
2309             unless defined $handle;
2310             _confess " invalid filehandle in select_resume_write()"
2311             unless defined fileno($handle);
2312             };
2313              
2314 1113 100       3251 return 0 unless $self->_data_handle_is_good($handle, MODE_WR);
2315              
2316 495         1235 $self->_data_handle_resume($handle, MODE_WR);
2317              
2318 494         1448 return 1;
2319             }
2320              
2321             # Turn off a handle's read mode bit without doing garbage-collection
2322             # things.
2323             sub select_pause_read {
2324 472     71 1 2749 my ($self, $handle) = ($poe_kernel, @_[1..$#_]);
2325              
2326 472         1690 if (ASSERT_USAGE) {
2327             _confess " must call select_pause_read() from a running session"
2328             if $kr_active_session == $self;
2329             _confess " undefined filehandle in select_pause_read()"
2330             unless defined $handle;
2331             _confess " invalid filehandle in select_pause_read()"
2332             unless defined fileno($handle);
2333             };
2334              
2335 472 100       1234 return 0 unless $self->_data_handle_is_good($handle, MODE_RD);
2336              
2337 70         162 $self->_data_handle_pause($handle, MODE_RD);
2338              
2339 69         191 return 1;
2340             }
2341              
2342             # Turn on a handle's read mode bit without doing garbage-collection
2343             # things.
2344             sub select_resume_read {
2345 137     69 1 1807 my ($self, $handle) = ($poe_kernel, @_[1..$#_]);
2346              
2347 137         286 if (ASSERT_USAGE) {
2348             _confess " must call select_resume_read() from a running session"
2349             if $kr_active_session == $self;
2350             _confess " undefined filehandle in select_resume_read()"
2351             unless defined $handle;
2352             _confess " invalid filehandle in select_resume_read()"
2353             unless defined fileno($handle);
2354             };
2355              
2356 137 100       336 return 0 unless $self->_data_handle_is_good($handle, MODE_RD);
2357              
2358 68         138 $self->_data_handle_resume($handle, MODE_RD);
2359              
2360 67         209 return 1;
2361             }
2362              
2363             #==============================================================================
2364             # Aliases: These functions expose the internal alias accessors with
2365             # extra fun parameter/return value checking.
2366             #==============================================================================
2367              
2368             ### Set an alias in the current session.
2369              
2370             sub alias_set {
2371 259     193 1 5124 my ($self, $name) = ($poe_kernel, @_[1..$#_]);
2372              
2373 259         433 if (ASSERT_USAGE) {
2374             _confess " undefined alias in alias_set()" unless defined $name;
2375             };
2376              
2377             # Don't overwrite another session's alias.
2378 259         642 my $existing_session = $self->_data_alias_resolve($name);
2379 192 100       728 if (defined $existing_session) {
2380 181 100       426 if ($existing_session != $kr_active_session) {
2381 42         94 $self->_explain_usage("alias '$name' is in use by another session");
2382 1         8 return EEXIST;
2383             }
2384 4         7 return 0;
2385             }
2386              
2387 51         123 $self->_data_alias_add($kr_active_session, $name);
2388 146         473 return 0;
2389             }
2390              
2391             ### Remove an alias from the current session.
2392              
2393             sub alias_remove {
2394 200     65 1 2394 my ($self, $name) = ($poe_kernel, @_[1..$#_]);
2395              
2396 65         79 if (ASSERT_USAGE) {
2397             _confess " undefined alias in alias_remove()" unless defined $name;
2398             };
2399              
2400 65         155 my $existing_session = $self->_data_alias_resolve($name);
2401              
2402 64 100       183 unless (defined $existing_session) {
2403 55         136 $self->_explain_usage("alias '$name' does not exist");
2404 2         10 return ESRCH;
2405             }
2406              
2407 9 100       23 if ($existing_session != $kr_active_session) {
2408 53         130 $self->_explain_usage("alias '$name' does not belong to current session");
2409 1         6 return EPERM;
2410             }
2411              
2412 8         21 $self->_data_alias_remove($kr_active_session, $name);
2413 59         172 return 0;
2414             }
2415              
2416             ### Resolve an alias into a session.
2417              
2418             sub alias_resolve {
2419 62     11 1 2460 my ($self, $name) = ($poe_kernel, @_[1..$#_]);
2420              
2421 11         11 if (ASSERT_USAGE) {
2422             _confess " undefined alias in alias_resolve()" unless defined $name;
2423             };
2424              
2425 11         29 return $self->_resolve_session($name);
2426             }
2427              
2428             ### List the aliases for a given session.
2429              
2430             sub alias_list {
2431 18     8 1 716 my ($self, $search_session) = ($poe_kernel, @_[1..$#_]);
2432 8   66     37 my $session =
2433             $self->_resolve_session($search_session || $kr_active_session);
2434              
2435 8 100       16 unless (defined $session) {
2436 0         0 $self->_explain_resolve_failure($search_session, "nonfatal");
2437 0         0 return;
2438             }
2439              
2440             # Return whatever can be found.
2441 8         20 my @alias_list = $self->_data_alias_list($session->ID);
2442 8 100       37 return wantarray() ? @alias_list : $alias_list[0];
2443             }
2444              
2445             #==============================================================================
2446             # Kernel and Session IDs
2447             #==============================================================================
2448              
2449             # Return the Kernel's "unique" ID. There's only so much uniqueness
2450             # available; machines on separate private 10/8 networks may have
2451             # identical kernel IDs. The chances of a collision are vanishingly
2452             # small.
2453              
2454             # The Kernel and Session IDs are based on Philip Gwyn's code. I hope
2455             # he still can recognize it.
2456              
2457             sub _recalc_id {
2458 179     179   486 my $self = shift;
2459              
2460 179         1132 my $old_id = $self->[KR_ID];
2461              
2462 179         323 my $hostname = eval { (uname)[1] };
  179         2517  
2463 179 50       881 $hostname = hostname() unless defined $hostname;
2464              
2465 537         2129 my $new_id = $self->[KR_ID] = join(
2466             "-", $hostname,
2467 537         2065 map { unpack "H*", $_ }
2468 179         1040 map { pack "N", $_ }
2469             (monotime(), $$, ++$kr_id_seq)
2470             );
2471              
2472 179 100       833 if (defined $old_id) {
2473 4         132 $self->_data_sig_relocate_kernel_id($old_id, $new_id);
2474 4         79 $self->_data_ses_relocate_kernel_id($old_id, $new_id);
2475 4         82 $self->_data_sid_relocate_kernel_id($old_id, $new_id);
2476 4         101 $self->_data_handle_relocate_kernel_id($old_id, $new_id);
2477 4         67 $self->_data_ev_relocate_kernel_id($old_id, $new_id);
2478 4         69 $self->_data_alias_relocate_kernel_id($old_id, $new_id);
2479             }
2480             }
2481              
2482 14719     14719 1 94601 sub ID { $poe_kernel->[KR_ID] }
2483              
2484             # Resolve an ID to a session reference. This function is virtually
2485             # moot now that _resolve_session does it too. This explicit call will
2486             # be faster, though, so it's kept for things that can benefit from it.
2487              
2488             sub ID_id_to_session {
2489 6     6 1 367 my ($self, $id) = ($poe_kernel, @_[1..$#_]);
2490              
2491 6         10 if (ASSERT_USAGE) {
2492             _confess " undefined ID in ID_id_to_session()" unless defined $id;
2493             };
2494              
2495 6         20 my $session = $self->_data_sid_resolve($id);
2496 5 100       13 return $session if defined $session;
2497              
2498 5         20 $self->_explain_return("ID does not exist");
2499 0         0 $! = ESRCH;
2500 0         0 return;
2501             }
2502              
2503             # Resolve a session reference to its corresponding ID.
2504              
2505             sub ID_session_to_id {
2506 33     33 1 863 my ($self, $session) = ($poe_kernel, @_[1..$#_]);
2507              
2508 33         43 if (ASSERT_USAGE) {
2509             _confess " undefined session in ID_session_to_id()"
2510             unless defined $session;
2511             };
2512              
2513 33         94 my $id = $self->_data_ses_resolve_to_id($session);
2514 32 100       115 if (defined $id) {
2515 31         72 $! = 0;
2516 31         64 return $id;
2517             }
2518              
2519 32         106 $self->_explain_return("session ($session) does not exist");
2520 0         0 $! = ESRCH;
2521 0         0 return;
2522             }
2523              
2524             #==============================================================================
2525             # Extra reference counts, to keep sessions alive when things occur.
2526             # They take session IDs because they may be called from resources at
2527             # times where the session reference is otherwise unknown.
2528             #==============================================================================
2529              
2530             sub refcount_increment {
2531 105     105 1 1478 my ($self, $session_id, $tag) = ($poe_kernel, @_[1..$#_]);
2532              
2533 105         271 if (ASSERT_USAGE) {
2534             _confess " undefined session ID in refcount_increment()"
2535             unless defined $session_id;
2536             _confess " undefined reference count tag in refcount_increment()"
2537             unless defined $tag;
2538             };
2539              
2540 105 100       331 unless ($self->_data_ses_exists($session_id)) {
2541 103         295 $self->_explain_return("session id $session_id does not exist");
2542 101         835 $! = ESRCH;
2543 0         0 return;
2544             }
2545              
2546 1         7 my $refcount = $self->_data_extref_inc($session_id, $tag);
2547             # TODO trace it here
2548 1         2 return $refcount;
2549             }
2550              
2551             sub refcount_decrement {
2552 205     104 1 3124 my ($self, $session_id, $tag) = ($poe_kernel, @_[1..$#_]);
2553              
2554 205         1769 if (ASSERT_USAGE) {
2555             _confess " undefined session ID in refcount_decrement()"
2556             unless defined $session_id;
2557             _confess " undefined reference count tag in refcount_decrement()"
2558             unless defined $tag;
2559             };
2560              
2561 104 100       310 unless ($self->_data_ses_exists($session_id)) {
2562 102         264 $self->_explain_return("session id $session_id does not exist");
2563 100         5517 $! = ESRCH;
2564 0         0 return;
2565             }
2566              
2567 1         8 my $refcount = $self->_data_extref_dec($session_id, $tag);
2568              
2569             # TODO trace it here
2570 1         4 return $refcount;
2571             }
2572              
2573             #==============================================================================
2574             # HANDLERS
2575             #==============================================================================
2576              
2577             # Add or remove event handlers from sessions.
2578             sub state {
2579 2137     2037 1 9006 my ($self, $event, $state_code, $state_alias) = ($poe_kernel, @_[1..$#_]);
2580 2137 100       7795 $state_alias = $event unless defined $state_alias;
2581              
2582 2037         1975 if (ASSERT_USAGE) {
2583             _confess " must call state() from a running session"
2584             if $kr_active_session == $self;
2585             _confess " undefined event name in state()" unless defined $event;
2586             _confess " can't call state() outside a session" if (
2587             $kr_active_session == $self
2588             );
2589             };
2590              
2591 2037 100 66     7680 if (
2592             (ref($kr_active_session) ne '') &&
2593             (ref($kr_active_session) ne 'POE::Kernel')
2594             ) {
2595 2029         6599 $kr_active_session->_register_state($event, $state_code, $state_alias);
2596 2028         4638 return 0;
2597             }
2598              
2599             # TODO A terminal signal (such as UIDESTROY) kills a session. The
2600             # Kernel deallocates the session, which cascades destruction to its
2601             # HEAP. That triggers a Wheel's destruction, which calls
2602             # $kernel->state() to remove a state from the session. The session,
2603             # though, is already gone. If TRACE_RETVALS and/or ASSERT_RETVALS
2604             # is set, this causes a warning or fatal error.
2605              
2606 1246         7835 $self->_explain_return("session ($kr_active_session) does not exist");
2607 1245         6136 return ESRCH;
2608             }
2609              
2610             1;
2611              
2612             __END__