home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / xampp / xampp-perl-addon-1.4.9-installer.exe / FileHandles.pm < prev    next >
Encoding:
Perl POD Document  |  2004-06-03  |  24.7 KB  |  793 lines

  1. # $Id: FileHandles.pm,v 1.10 2004/06/03 18:40:24 rcaputo Exp $
  2.  
  3. # Manage file handles, associated descriptors, and read/write modes
  4. # thereon.
  5.  
  6. package POE::Resources::FileHandles;
  7.  
  8. use vars qw($VERSION);
  9. $VERSION = do {my@r=(q$Revision: 1.10 $=~/\d+/g);sprintf"%d."."%04d"x$#r,@r};
  10.  
  11. # These methods are folded into POE::Kernel;
  12. package POE::Kernel;
  13.  
  14. use strict;
  15.  
  16. ### A local reference to POE::Kernel's queue.
  17.  
  18. my $kr_queue;
  19.  
  20. ### Fileno structure.  This tracks the sessions that are watchin a
  21. ### file, by its file number.  It used to track by file handle, but
  22. ### several handles can point to the same underlying fileno.  This is
  23. ### more unique.
  24.  
  25. my %kr_filenos;
  26.  
  27. sub FNO_MODE_RD      () { MODE_RD } # [ [ (fileno read mode structure)
  28. # --- BEGIN SUB STRUCT 1 ---        #
  29. sub FMO_REFCOUNT     () { 0      }  #     $fileno_total_use_count,
  30. sub FMO_ST_ACTUAL    () { 1      }  #     $requested_file_state (see HS_PAUSED)
  31. sub FMO_ST_REQUEST   () { 2      }  #     $actual_file_state (see HS_PAUSED)
  32. sub FMO_EV_COUNT     () { 3      }  #     $number_of_pending_events,
  33. sub FMO_SESSIONS     () { 4      }  #     { $session_watching_this_handle =>
  34.                                     #       { $handle_watched_as =>
  35. # --- BEGIN SUB STRUCT 2 ---        #
  36. sub HSS_HANDLE       () { 0      }  #         [ $blessed_handle,
  37. sub HSS_SESSION      () { 1      }  #           $blessed_session,
  38. sub HSS_STATE        () { 2      }  #           $event_name,
  39.                                     #         ],
  40.                                     #       },
  41. # --- CEASE SUB STRUCT 2 ---        #     },
  42. # --- CEASE SUB STRUCT 1 ---        #   ],
  43.                                     #
  44. sub FNO_MODE_WR      () { MODE_WR } #   [ (write mode structure is the same)
  45.                                     #   ],
  46.                                     #
  47. sub FNO_MODE_EX      () { MODE_EX } #   [ (expedite mode struct is the same)
  48.                                     #   ],
  49.                                     #
  50. sub FNO_TOT_REFCOUNT () { 3      }  #   $total_number_of_file_watchers,
  51.                                     # ]
  52.  
  53. ### These are the values for FMO_ST_ACTUAL and FMO_ST_REQUEST.
  54.  
  55. sub HS_STOPPED   () { 0x00 }   # The file has stopped generating events.
  56. sub HS_PAUSED    () { 0x01 }   # The file temporarily stopped making events.
  57. sub HS_RUNNING   () { 0x02 }   # The file is running and can generate events.
  58.  
  59. ### Handle to session.
  60.  
  61. my %kr_ses_to_handle;
  62.  
  63.                             #    { $session =>
  64.                             #      $handle =>
  65. # --- BEGIN SUB STRUCT ---  #        [
  66. sub SH_HANDLE     () {  0 } #          $blessed_file_handle,
  67. sub SH_REFCOUNT   () {  1 } #          $total_reference_count,
  68. sub SH_MODECOUNT  () {  2 } #          [ $read_reference_count,     (MODE_RD)
  69.                             #            $write_reference_count,    (MODE_WR)
  70.                             #            $expedite_reference_count, (MODE_EX)
  71. # --- CEASE SUB STRUCT ---  #          ],
  72.                             #        ],
  73.                             #        ...
  74.                             #      },
  75.                             #    },
  76.  
  77. sub _data_handle_preload {
  78.     $poe_kernel->[KR_FILENOS] = \%kr_filenos;
  79. }
  80. use POE::API::ResLoader \&_data_handle_preload;
  81.  
  82. ### Begin-run initialization.
  83.  
  84. sub _data_handle_initialize {
  85.   my ($self, $queue) = @_;
  86.   $kr_queue = $queue;
  87. }
  88.  
  89. ### End-run leak checking.
  90.  
  91. sub _data_handle_finalize {
  92.   my $finalized_ok = 1;
  93.  
  94.   while (my ($fd, $fd_rec) = each(%kr_filenos)) {
  95.     my ($rd, $wr, $ex, $tot) = @$fd_rec;
  96.     $finalized_ok = 0;
  97.  
  98.     _warn "!!! Leaked fileno: $fd (total refcnt=$tot)\n";
  99.  
  100.     _warn(
  101.       "!!!\tRead:\n",
  102.       "!!!\t\trefcnt  = $rd->[FMO_REFCOUNT]\n",
  103.       "!!!\t\tev cnt  = $rd->[FMO_EV_COUNT]\n",
  104.     );
  105.     while (my ($ses, $ses_rec) = each(%{$rd->[FMO_SESSIONS]})) {
  106.       _warn "!!!\t\tsession = $ses\n";
  107.       while (my ($handle, $hnd_rec) = each(%{$ses_rec})) {
  108.         _warn(
  109.           "!!!\t\t\thandle  = $hnd_rec->[HSS_HANDLE]\n",
  110.           "!!!\t\t\tsession = $hnd_rec->[HSS_SESSION]\n",
  111.           "!!!\t\t\tevent   = $hnd_rec->[HSS_STATE]\n",
  112.         );
  113.       }
  114.     }
  115.  
  116.     _warn(
  117.       "!!!\tWrite:\n",
  118.       "!!!\t\trefcnt  = $wr->[FMO_REFCOUNT]\n",
  119.       "!!!\t\tev cnt  = $wr->[FMO_EV_COUNT]\n",
  120.     );
  121.     while (my ($ses, $ses_rec) = each(%{$wr->[FMO_SESSIONS]})) {
  122.       _warn "!!!\t\tsession = $ses\n";
  123.       while (my ($handle, $hnd_rec) = each(%{$ses_rec})) {
  124.         _warn(
  125.           "!!!\t\t\thandle  = $hnd_rec->[HSS_HANDLE]\n",
  126.           "!!!\t\t\tsession = $hnd_rec->[HSS_SESSION]\n",
  127.           "!!!\t\t\tevent   = $hnd_rec->[HSS_STATE]\n",
  128.         );
  129.       }
  130.     }
  131.  
  132.     _warn(
  133.       "!!!\tException:\n",
  134.       "!!!\t\trefcnt  = $ex->[FMO_REFCOUNT]\n",
  135.       "!!!\t\tev cnt  = $ex->[FMO_EV_COUNT]\n",
  136.     );
  137.     while (my ($ses, $ses_rec) = each(%{$ex->[FMO_SESSIONS]})) {
  138.       _warn "!!!\t\tsession = $ses\n";
  139.       while (my ($handle, $hnd_rec) = each(%{$ses_rec})) {
  140.         _warn(
  141.           "!!!\t\t\thandle  = $hnd_rec->[HSS_HANDLE]\n",
  142.           "!!!\t\t\tsession = $hnd_rec->[HSS_SESSION]\n",
  143.           "!!!\t\t\tevent   = $hnd_rec->[HSS_STATE]\n",
  144.         );
  145.       }
  146.     }
  147.   }
  148.  
  149.   while (my ($ses, $hnd_rec) = each(%kr_ses_to_handle)) {
  150.     $finalized_ok = 0;
  151.     _warn "!!! Leaked handle in $ses\n";
  152.     while (my ($hnd, $rc) = each(%$hnd_rec)) {
  153.       _warn(
  154.         "!!!\tHandle: $hnd (tot refcnt=$rc->[SH_REFCOUNT])\n",
  155.         "!!!\t\tRead      refcnt: $rc->[SH_MODECOUNT]->[MODE_RD]\n",
  156.         "!!!\t\tWrite     refcnt: $rc->[SH_MODECOUNT]->[MODE_WR]\n",
  157.         "!!!\t\tException refcnt: $rc->[SH_MODECOUNT]->[MODE_EX]\n",
  158.       );
  159.     }
  160.   }
  161.  
  162.   return $finalized_ok;
  163. }
  164.  
  165. ### Ensure a handle's actual state matches its requested one.  Pause
  166. ### or resume the handle as necessary.
  167.  
  168. sub _data_handle_resume_requested_state {
  169.   my ($self, $handle, $mode) = @_;
  170.   my $fileno = fileno($handle);
  171.  
  172.   # Skip the rest if we aren't watching the file descriptor.  This
  173.   # seems like a kludge: should we even be called if the descriptor
  174.   # isn't watched?
  175.   return unless exists $kr_filenos{$fileno};
  176.  
  177.   my $kr_fno_rec  = $kr_filenos{$fileno}->[$mode];
  178.  
  179.   if (TRACE_FILES) {
  180.     _warn(
  181.       "<fh> decrementing event count in mode ($mode) ",
  182.       "for fileno (", $fileno, ") from count (",
  183.       $kr_fno_rec->[FMO_EV_COUNT], ")"
  184.     );
  185.   }
  186.  
  187.   # If all events for the fileno/mode pair have been delivered, then
  188.   # resume the filehandle's watcher.  This decrements FMO_EV_COUNT
  189.   # because the event has just been dispatched.  This makes sense.
  190.  
  191.   unless (--$kr_fno_rec->[FMO_EV_COUNT]) {
  192.     if ($kr_fno_rec->[FMO_ST_REQUEST] & HS_PAUSED) {
  193.       $self->loop_pause_filehandle($handle, $mode);
  194.       $kr_fno_rec->[FMO_ST_ACTUAL] = HS_PAUSED;
  195.     }
  196.     elsif ($kr_fno_rec->[FMO_ST_REQUEST] & HS_RUNNING) {
  197.       $self->loop_resume_filehandle($handle, $mode);
  198.       $kr_fno_rec->[FMO_ST_ACTUAL] = HS_RUNNING;
  199.     }
  200.     elsif (ASSERT_DATA) {
  201.       _trap();
  202.     }
  203.   }
  204.   elsif (ASSERT_DATA) {
  205.     if ($kr_fno_rec->[FMO_EV_COUNT] < 0) {
  206.       _trap "handle event count went below zero";
  207.     }
  208.   }
  209. }
  210.  
  211. ### Enqueue "select" events for a list of file descriptors in a given
  212. ### access mode.
  213.  
  214. sub _data_handle_enqueue_ready {
  215.   my ($self, $mode, @filenos) = @_;
  216.  
  217.   foreach my $fileno (@filenos) {
  218.     if (ASSERT_DATA) {
  219.       _trap "internal inconsistency: undefined fileno" unless defined $fileno;
  220.     }
  221.  
  222.     my $kr_fno_rec = $kr_filenos{$fileno}->[$mode];
  223.  
  224.     # Gather all the events to emit for this fileno/mode pair.
  225.  
  226.     my @selects = map { values %$_ } values %{ $kr_fno_rec->[FMO_SESSIONS] };
  227.  
  228.     # Emit them.
  229.  
  230.     foreach my $select (@selects) {
  231.       $self->_data_ev_enqueue
  232.         ( $select->[HSS_SESSION], $select->[HSS_SESSION],
  233.           $select->[HSS_STATE], ET_SELECT,
  234.           [ $select->[HSS_HANDLE],  # EA_SEL_HANDLE
  235.             $mode,                  # EA_SEL_MODE
  236.           ],
  237.           __FILE__, __LINE__, time(),
  238.         );
  239.  
  240.       # Count the enqueued event.  This increments FMO_EV_COUNT
  241.       # because an event has just been enqueued.  This makes sense.
  242.  
  243.       unless ($kr_fno_rec->[FMO_EV_COUNT]++) {
  244.         my $handle = $select->[HSS_HANDLE];
  245.         $self->loop_pause_filehandle($handle, $mode);
  246.         $kr_fno_rec->[FMO_ST_ACTUAL] = HS_PAUSED;
  247.       }
  248.  
  249.       if (TRACE_FILES) {
  250.         _warn(
  251.           "<fh> incremented event count in mode ($mode) ",
  252.           "for fileno ($fileno) to count ($kr_fno_rec->[FMO_EV_COUNT])"
  253.         );
  254.       }
  255.     }
  256.   }
  257. }
  258.  
  259. ### Test whether POE is tracking a file handle.
  260.  
  261. sub _data_handle_is_good {
  262.   my ($self, $handle, $mode) = @_;
  263.  
  264.   # Don't bother if the kernel isn't tracking the file.
  265.   return 0 unless exists $kr_filenos{fileno $handle};
  266.  
  267.   # Don't bother if the kernel isn't tracking the file mode.
  268.   return 0 unless $kr_filenos{fileno $handle}->[$mode]->[FMO_REFCOUNT];
  269.  
  270.   return 1;
  271. }
  272.  
  273. ### Add a select to the session, and possibly begin a watcher.
  274.  
  275. sub _data_handle_add {
  276.   my ($self, $handle, $mode, $session, $event) = @_;
  277.   my $fd = fileno($handle);
  278.  
  279.   # First time watching the file descriptor.  Do some heavy setup.
  280.   #
  281.   # NB - This means we can't optimize away the delete() calls here and
  282.   # there, because they probably ensure that the structure exists.
  283.   unless (exists $kr_filenos{$fd}) {
  284.  
  285.     $kr_filenos{$fd} =
  286.       [ [ 0,          # FMO_REFCOUNT    MODE_RD
  287.           HS_PAUSED,  # FMO_ST_ACTUAL
  288.           HS_PAUSED,  # FMO_ST_REQUEST
  289.           0,          # FMO_EV_COUNT
  290.           { },        # FMO_SESSIONS
  291.         ],
  292.         [ 0,          # FMO_REFCOUNT    MODE_WR
  293.           HS_PAUSED,  # FMO_ST_ACTUAL
  294.           HS_PAUSED,  # FMO_ST_REQUEST
  295.           0,          # FMO_EV_COUNT
  296.           { },        # FMO_SESSIONS
  297.         ],
  298.         [ 0,          # FMO_REFCOUNT    MODE_EX
  299.           HS_PAUSED,  # FMO_ST_ACTUAL
  300.           HS_PAUSED,  # FMO_ST_REQUEST
  301.           0,          # FMO_EV_COUNT
  302.           { },        # FMO_SESSIONS
  303.         ],
  304.         0,            # FNO_TOT_REFCOUNT
  305.       ];
  306.  
  307.     if (TRACE_FILES) {
  308.       _warn "<fh> adding fd ($fd) in mode ($mode)";
  309.     }
  310.  
  311.     # For DOSISH systems like OS/2.  Wrapped in eval{} in case it's a
  312.     # tied handle that doesn't support binmode.
  313.     eval { binmode *$handle };
  314.  
  315.     # Turn off blocking unless it's tied or a plain file.
  316.     unless (tied *$handle or -f $handle) {
  317.  
  318.       # RCC 2002-12-19: ActiveState Perl 5.8.0 disliked the Win32 code
  319.       # to make a socket non-blocking, so we're trying IO::Handle's
  320.       # blocking(0) method.  Bonus: It does "the right thing" just
  321.       # about everywhere, so I don't need to add checks for AS Perl
  322.       # 5.8.0, AS Perl 5.6.1, and Everybody else.
  323.  
  324.       # RCC 2003-01-20: Perl 5.005_03 doesn't like blocking(), so
  325.       # we'll only call it in Perl 5.8.0 and beyond.
  326.  
  327.       if ($] >= 5.008) {
  328.         $handle->blocking(0);
  329.       }
  330.       else {
  331.         # Make the handle stop blocking, the POSIX way.
  332.         unless (RUNNING_IN_HELL) {
  333.           my $flags = fcntl($handle, F_GETFL, 0)
  334.             or _trap "fcntl($handle, F_GETFL, etc.) fails: $!\n";
  335.           until (fcntl($handle, F_SETFL, $flags | O_NONBLOCK)) {
  336.             _trap "fcntl($handle, FSETFL, etc) fails: $!"
  337.               unless $! == EAGAIN or $! == EWOULDBLOCK;
  338.           }
  339.         }
  340.         else {
  341.           # Do it the Win32 way.
  342.           my $set_it = "1";
  343.  
  344.           # 126 is FIONBIO (some docs say 0x7F << 16)
  345.           ioctl( $handle,
  346.                  0x80000000 | (4 << 16) | (ord('f') << 8) | 126,
  347.                  $set_it
  348.                )
  349.             or _trap "ioctl($handle, FIONBIO, $set_it) fails: $!\n";
  350.         }
  351.       }
  352.     }
  353.  
  354.     # Turn off buffering.
  355.     CORE::select((CORE::select($handle), $| = 1)[0]);
  356.   }
  357.  
  358.   # Cache some high-level lookups.
  359.   my $kr_fileno  = $kr_filenos{$fd};
  360.   my $kr_fno_rec = $kr_fileno->[$mode];
  361.  
  362.   # The session is already watching this fileno in this mode.
  363.  
  364.   if ($kr_fno_rec->[FMO_SESSIONS]->{$session}) {
  365.  
  366.     # The session is also watching it by the same handle.  Treat this
  367.     # as a "resume" in this mode.
  368.  
  369.     if (exists $kr_fno_rec->[FMO_SESSIONS]->{$session}->{$handle}) {
  370.       if (TRACE_FILES) {
  371.         _warn(
  372.           "<fh> running fileno($fd) mode($mode) " .
  373.           "count($kr_fno_rec->[FMO_EV_COUNT])"
  374.         );
  375.       }
  376.       unless ($kr_fno_rec->[FMO_EV_COUNT]) {
  377.         $self->loop_resume_filehandle($handle, $mode);
  378.         $kr_fno_rec->[FMO_ST_ACTUAL] = HS_RUNNING;
  379.       }
  380.       $kr_fno_rec->[FMO_ST_REQUEST] = HS_RUNNING;
  381.     }
  382.  
  383.     # The session is watching it by a different handle.  It can't be
  384.     # done yet, but maybe later when drivers are added to the mix.
  385.     #
  386.     # TODO - This can occur if someone closes a filehandle without
  387.     # calling select_foo() to deregister it from POE.  In that case,
  388.     # the operating system reuses the file descriptor, but we still
  389.     # have something registered for it here.
  390.  
  391.     else {
  392.       foreach my $hdl_rec (
  393.         values %{$kr_fno_rec->[FMO_SESSIONS]->{$session}}
  394.       ) {
  395.         my $other_handle = $hdl_rec->[HSS_HANDLE];
  396.         unless (defined(fileno $other_handle)) {
  397.           _trap(
  398.             "can't watch $handle: $other_handle (closed) is still ",
  399.             "registered for that file descriptor in mode $mode"
  400.           );
  401.         }
  402.         if (fileno($handle) == fileno($other_handle)) {
  403.           _trap(
  404.             "can't watch $handle: $other_handle (open) is still ",
  405.             "registered for that descriptor in mode $mode"
  406.           );
  407.         }
  408.         _trap "internal inconsistency";
  409.       }
  410.       _trap "can't watch the same handle in the same mode 2+ times yet";
  411.     }
  412.   }
  413.  
  414.   # The session is not watching this fileno in this mode.  Record
  415.   # the session/handle pair.
  416.  
  417.   else {
  418.     $kr_fno_rec->[FMO_SESSIONS]->{$session}->{$handle} =
  419.       [ $handle,   # HSS_HANDLE
  420.         $session,  # HSS_SESSION
  421.         $event,    # HSS_STATE
  422.       ];
  423.  
  424.     # Fix reference counts.
  425.     $kr_fileno->[FNO_TOT_REFCOUNT]++;
  426.     $kr_fno_rec->[FMO_REFCOUNT]++;
  427.  
  428.     # If this is the first time a file is watched in this mode, then
  429.     # have the event loop bridge watch it.
  430.  
  431.     if ($kr_fno_rec->[FMO_REFCOUNT] == 1) {
  432.       $self->loop_watch_filehandle($handle, $mode);
  433.       $kr_fno_rec->[FMO_ST_ACTUAL]  = HS_RUNNING;
  434.       $kr_fno_rec->[FMO_ST_REQUEST] = HS_RUNNING;
  435.     }
  436.   }
  437.  
  438.   # If the session hasn't already been watching the filehandle, then
  439.   # register the filehandle in the session's structure.
  440.  
  441.   unless (exists $kr_ses_to_handle{$session}->{$handle}) {
  442.     $kr_ses_to_handle{$session}->{$handle} =
  443.       [ $handle,  # SH_HANDLE
  444.         0,        # SH_REFCOUNT
  445.         [ 0,      # SH_MODECOUNT / MODE_RD
  446.           0,      # SH_MODECOUNT / MODE_WR
  447.           0       # SH_MODECOUNT / MODE_EX
  448.         ]
  449.       ];
  450.     $self->_data_ses_refcount_inc($session);
  451.   }
  452.  
  453.   # Modify the session's handle structure's reference counts, so the
  454.   # session knows it has a reason to live.
  455.  
  456.   my $ss_handle = $kr_ses_to_handle{$session}->{$handle};
  457.   unless ($ss_handle->[SH_MODECOUNT]->[$mode]) {
  458.     $ss_handle->[SH_MODECOUNT]->[$mode]++;
  459.     $ss_handle->[SH_REFCOUNT]++;
  460.   }
  461. }
  462.  
  463. ### Remove a select from the kernel, and possibly trigger the
  464. ### session's destruction.
  465.  
  466. sub _data_handle_remove {
  467.   my ($self, $handle, $mode, $session) = @_;
  468.   my $fd = fileno($handle);
  469.  
  470.   # Make sure the handle is deregistered with the kernel.
  471.  
  472.   if (exists $kr_filenos{$fd}) {
  473.     my $kr_fileno  = $kr_filenos{$fd};
  474.     my $kr_fno_rec = $kr_fileno->[$mode];
  475.  
  476.     # Make sure the handle was registered to the requested session.
  477.  
  478.     if (
  479.       exists($kr_fno_rec->[FMO_SESSIONS]->{$session}) and
  480.       exists($kr_fno_rec->[FMO_SESSIONS]->{$session}->{$handle})
  481.     ) {
  482.  
  483.       TRACE_FILES and
  484.         _warn "<fh> removing handle ($handle) fileno ($fd) mode ($mode)";
  485.  
  486.       # Remove the handle from the kernel's session record.
  487.  
  488.       my $handle_rec =
  489.         delete $kr_fno_rec->[FMO_SESSIONS]->{$session}->{$handle};
  490.  
  491.       my $kill_session = $handle_rec->[HSS_SESSION];
  492.       my $kill_event   = $handle_rec->[HSS_STATE];
  493.  
  494.       # Remove any events destined for that handle.  Decrement
  495.       # FMO_EV_COUNT for each, because we've removed them.  This makes
  496.       # sense.
  497.       my $my_select = sub {
  498.         return 0 unless $_[0]->[EV_TYPE]    &  ET_SELECT;
  499.         return 0 unless $_[0]->[EV_SESSION] == $kill_session;
  500.         return 0 unless $_[0]->[EV_NAME]    eq $kill_event;
  501.         return 0 unless $_[0]->[EV_ARGS]->[EA_SEL_HANDLE] == $handle;
  502.         return 0 unless $_[0]->[EV_ARGS]->[EA_SEL_MODE]   == $mode;
  503.         return 1;
  504.       };
  505.  
  506.       foreach ($kr_queue->remove_items($my_select)) {
  507.         my ($time, $id, $event) = @$_;
  508.         $self->_data_ev_refcount_dec( @$event[EV_SESSION, EV_SOURCE] );
  509.  
  510.         TRACE_EVENTS and
  511.           _warn "<ev> removing select event $id ``$event->[EV_NAME]''";
  512.  
  513.         $kr_fno_rec->[FMO_EV_COUNT]--;
  514.  
  515.         if (TRACE_FILES) {
  516.           _warn(
  517.             "<fh> fileno $fd mode $mode event count went to ",
  518.             $kr_fno_rec->[FMO_EV_COUNT]
  519.           );
  520.         }
  521.  
  522.         if (ASSERT_DATA) {
  523.           _trap "<dt> fileno $fd mode $mode event count went below zero"
  524.             if $kr_fno_rec->[FMO_EV_COUNT] < 0;
  525.         }
  526.       }
  527.  
  528.       # Decrement the handle's reference count.
  529.  
  530.       $kr_fno_rec->[FMO_REFCOUNT]--;
  531.  
  532.       if (ASSERT_DATA) {
  533.         _trap "<dt> fileno mode refcount went below zero"
  534.           if $kr_fno_rec->[FMO_REFCOUNT] < 0;
  535.       }
  536.  
  537.       # If the "mode" count drops to zero, then stop selecting the
  538.       # handle.
  539.  
  540.       unless ($kr_fno_rec->[FMO_REFCOUNT]) {
  541.         $self->loop_ignore_filehandle($handle, $mode);
  542.         $kr_fno_rec->[FMO_ST_ACTUAL]  = HS_STOPPED;
  543.         $kr_fno_rec->[FMO_ST_REQUEST] = HS_STOPPED;
  544.  
  545.         # The session is not watching handles anymore.  Remove the
  546.         # session entirely the fileno structure.
  547.         delete $kr_fno_rec->[FMO_SESSIONS]->{$session}
  548.           unless keys %{$kr_fno_rec->[FMO_SESSIONS]->{$session}};
  549.       }
  550.  
  551.       # Decrement the kernel record's handle reference count.  If the
  552.       # handle is done being used, then delete it from the kernel's
  553.       # record structure.  This initiates Perl's garbage collection on
  554.       # it, as soon as whatever else in "user space" frees it.
  555.  
  556.       $kr_fileno->[FNO_TOT_REFCOUNT]--;
  557.  
  558.       if (ASSERT_DATA) {
  559.         _trap "<dt> fileno refcount went below zero"
  560.           if $kr_fileno->[FNO_TOT_REFCOUNT] < 0;
  561.       }
  562.  
  563.       unless ($kr_fileno->[FNO_TOT_REFCOUNT]) {
  564.         if (TRACE_FILES) {
  565.           _warn "<fh> deleting handle ($handle) fileno ($fd) entirely";
  566.         }
  567.         delete $kr_filenos{$fd};
  568.       }
  569.     }
  570.     elsif (TRACE_FILES) {
  571.       _warn(
  572.         "<fh> session doesn't own handle ($handle) fileno ($fd) mode ($mode)"
  573.       );
  574.     }
  575.   }
  576.   elsif (TRACE_FILES) {
  577.     _warn(
  578.       "<fh> handle ($handle) fileno ($fd) is not registered with POE::Kernel"
  579.     );
  580.   }
  581.  
  582.   # SS_HANDLES - Remove the select from the session, assuming there is
  583.   # a session to remove it from.  -><- Key it on fileno?
  584.  
  585.   if (
  586.     exists($kr_ses_to_handle{$session}) and
  587.     exists($kr_ses_to_handle{$session}->{$handle})
  588.   ) {
  589.  
  590.     # Remove it from the session's read, write or expedite mode.
  591.  
  592.     my $ss_handle = $kr_ses_to_handle{$session}->{$handle};
  593.     if ($ss_handle->[SH_MODECOUNT]->[$mode]) {
  594.  
  595.       # Hmm... what is this?  Was POE going to support multiple selects?
  596.  
  597.       $ss_handle->[SH_MODECOUNT]->[$mode] = 0;
  598.  
  599.       # Decrement the reference count, and delete the handle if it's done.
  600.  
  601.       $ss_handle->[SH_REFCOUNT]--;
  602.  
  603.       if (ASSERT_DATA) {
  604.         _trap "<dt> refcount went below zero"
  605.           if $ss_handle->[SH_REFCOUNT] < 0;
  606.       }
  607.  
  608.       unless ($ss_handle->[SH_REFCOUNT]) {
  609.         delete $kr_ses_to_handle{$session}->{$handle};
  610.         $self->_data_ses_refcount_dec($session);
  611.         delete $kr_ses_to_handle{$session}
  612.           unless keys %{$kr_ses_to_handle{$session}};
  613.       }
  614.     }
  615.     elsif (TRACE_FILES) {
  616.       _warn(
  617.         "<fh> handle ($handle) fileno ($fd) is not registered with",
  618.         $self->_data_alias_loggable($session)
  619.       );
  620.     }
  621.   }
  622. }
  623.  
  624. ### Resume a filehandle.  If there are no events in the queue for this
  625. ### handle/mode pair, then we go ahead and set the actual state now.
  626. ### Otherwise it must wait until the queue empties.
  627.  
  628. sub _data_handle_resume {
  629.   my ($self, $handle, $mode) = @_;
  630.  
  631.   my $kr_fileno = $kr_filenos{fileno($handle)};
  632.   my $kr_fno_rec = $kr_fileno->[$mode];
  633.  
  634.   if (TRACE_FILES) {
  635.     _warn(
  636.       "<fh> resume test: fileno(" . fileno($handle) . ") mode($mode) " .
  637.       "count($kr_fno_rec->[FMO_EV_COUNT])"
  638.     );
  639.   }
  640.  
  641.   # Resume the handle if there are no events for it.
  642.   unless ($kr_fno_rec->[FMO_EV_COUNT]) {
  643.     $self->loop_resume_filehandle($handle, $mode);
  644.     $kr_fno_rec->[FMO_ST_ACTUAL] = HS_RUNNING;
  645.   }
  646.  
  647.   # Either way we set the handle's requested state to "running".
  648.   $kr_fno_rec->[FMO_ST_REQUEST] = HS_RUNNING;
  649. }
  650.  
  651. ### Pause a filehandle.  If there are no events in the queue for this
  652. ### handle/mode pair, then we go ahead and set the actual state now.
  653. ### Otherwise it must wait until the queue empties.
  654.  
  655. sub _data_handle_pause {
  656.   my ($self, $handle, $mode) = @_;
  657.  
  658.   my $kr_fileno = $kr_filenos{fileno($handle)};
  659.   my $kr_fno_rec = $kr_fileno->[$mode];
  660.  
  661.   if (TRACE_FILES) {
  662.     _warn(
  663.       "<fh> pause test: fileno(" . fileno($handle) . ") mode($mode) " .
  664.       "count($kr_fno_rec->[FMO_EV_COUNT])"
  665.     );
  666.   }
  667.  
  668.   unless ($kr_fno_rec->[FMO_EV_COUNT]) {
  669.     $self->loop_pause_filehandle($handle, $mode);
  670.     $kr_fno_rec->[FMO_ST_ACTUAL] = HS_PAUSED;
  671.   }
  672.  
  673.   # Correct the requested state so it matches the actual one.
  674.  
  675.   $kr_fno_rec->[FMO_ST_REQUEST] = HS_PAUSED;
  676. }
  677.  
  678. ### Return the number of active filehandles in the entire system.
  679.  
  680. sub _data_handle_count {
  681.   return scalar keys %kr_filenos;
  682. }
  683.  
  684. ### Return the number of active handles for a single session.
  685.  
  686. sub _data_handle_count_ses {
  687.   my ($self, $session) = @_;
  688.   return 0 unless exists $kr_ses_to_handle{$session};
  689.   return scalar keys %{$kr_ses_to_handle{$session}};
  690. }
  691.  
  692. ### Clear all the handles owned by a session.
  693.  
  694. sub _data_handle_clear_session {
  695.   my ($self, $session) = @_;
  696.   return unless exists $kr_ses_to_handle{$session}; # avoid autoviv
  697.   my @handles = values %{$kr_ses_to_handle{$session}};
  698.   foreach (@handles) {
  699.     my $handle = $_->[SH_HANDLE];
  700.     my $refcount = $_->[SH_MODECOUNT];
  701.  
  702.     $self->_data_handle_remove($handle, MODE_RD, $session)
  703.       if $refcount->[MODE_RD];
  704.     $self->_data_handle_remove($handle, MODE_WR, $session)
  705.       if $refcount->[MODE_WR];
  706.     $self->_data_handle_remove($handle, MODE_EX, $session)
  707.       if $refcount->[MODE_EX];
  708.   }
  709. }
  710.  
  711. # -><- Testing accessors.  Maybe useful for introspection.  May need
  712. # modification before that.
  713.  
  714. sub _data_handle_fno_refcounts {
  715.   my ($self, $fd) = @_;
  716.   return(
  717.     $kr_filenos{$fd}->[FNO_TOT_REFCOUNT],
  718.     $kr_filenos{$fd}->[FNO_MODE_RD]->[FMO_REFCOUNT],
  719.     $kr_filenos{$fd}->[FNO_MODE_WR]->[FMO_REFCOUNT],
  720.     $kr_filenos{$fd}->[FNO_MODE_EX]->[FMO_REFCOUNT],
  721.   )
  722. }
  723.  
  724. sub _data_handle_fno_evcounts {
  725.   my ($self, $fd) = @_;
  726.   return(
  727.     $kr_filenos{$fd}->[FNO_MODE_RD]->[FMO_EV_COUNT],
  728.     $kr_filenos{$fd}->[FNO_MODE_WR]->[FMO_EV_COUNT],
  729.     $kr_filenos{$fd}->[FNO_MODE_EX]->[FMO_EV_COUNT],
  730.   )
  731. }
  732.  
  733. sub _data_handle_fno_states {
  734.   my ($self, $fd) = @_;
  735.   return(
  736.     $kr_filenos{$fd}->[FNO_MODE_RD]->[FMO_ST_ACTUAL],
  737.     $kr_filenos{$fd}->[FNO_MODE_RD]->[FMO_ST_REQUEST],
  738.     $kr_filenos{$fd}->[FNO_MODE_WR]->[FMO_ST_ACTUAL],
  739.     $kr_filenos{$fd}->[FNO_MODE_WR]->[FMO_ST_REQUEST],
  740.     $kr_filenos{$fd}->[FNO_MODE_EX]->[FMO_ST_ACTUAL],
  741.     $kr_filenos{$fd}->[FNO_MODE_EX]->[FMO_ST_REQUEST],
  742.   );
  743. }
  744.  
  745. sub _data_handle_fno_sessions {
  746.   my ($self, $fd) = @_;
  747.  
  748.   return(
  749.     $kr_filenos{$fd}->[FNO_MODE_RD]->[FMO_SESSIONS],
  750.     $kr_filenos{$fd}->[FNO_MODE_WR]->[FMO_SESSIONS],
  751.     $kr_filenos{$fd}->[FNO_MODE_EX]->[FMO_SESSIONS],
  752.   );
  753. }
  754.  
  755. sub _data_handle_handles {
  756.   my $self = shift;
  757.   return %kr_ses_to_handle;
  758. }
  759.  
  760. 1;
  761.  
  762. __END__
  763.  
  764. =head1 NAME
  765.  
  766. POE::Resources::FileHandles - manage file handles on behalf of POE::Kernel
  767.  
  768. =head1 SYNOPSIS
  769.  
  770. Used internally by POE::Kernel.  Better documentation will be
  771. forthcoming.
  772.  
  773. =head1 DESCRIPTION
  774.  
  775. This module encapsulates low-level file handle management for
  776. POE::Kernel.  It provides accessors to its data structures that
  777. POE::Kernel uses internally.  This module has no public interface.
  778. Move along.
  779.  
  780. =head1 SEE ALSO
  781.  
  782. See L<POE::Kernel> for documentation on file handles and selects.
  783.  
  784. =head1 BUGS
  785.  
  786. Probably.
  787.  
  788. =head1 AUTHORS & COPYRIGHTS
  789.  
  790. Please see L<POE> for more information about authors and contributors.
  791.  
  792. =cut
  793.