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 / Sessions.pm < prev    next >
Encoding:
Perl POD Document  |  2003-12-13  |  14.9 KB  |  552 lines

  1. # $Id: Sessions.pm,v 1.14 2003/12/13 05:37:29 rcaputo Exp $
  2.  
  3. # Manage session data structures on behalf of POE::Kernel.
  4.  
  5. package POE::Resources::Sessions;
  6.  
  7. use vars qw($VERSION);
  8. $VERSION = do {my@r=(q$Revision: 1.14 $=~/\d+/g);sprintf"%d."."%04d"x$#r,@r};
  9.  
  10. # These methods are folded into POE::Kernel;
  11. package POE::Kernel;
  12.  
  13. use strict;
  14. ### Session structure.
  15.  
  16. my %kr_sessions;
  17. #  { $session =>
  18. #    [ $blessed_session,         SS_SESSION
  19. #      $total_reference_count,   SS_REFCOUNT
  20. #      $parent_session,          SS_PARENT
  21. #      { $child_session => $blessed_ref,     SS_CHILDREN
  22. #        ...,
  23. #      },
  24. #      { $process_id => $placeholder_value,  SS_PROCESSES
  25. #        ...,
  26. #      },
  27. #      $unique_session_id,       SS_ID
  28. #    ],
  29. #    ...,
  30. #  };
  31.  
  32. sub SS_SESSION    () { 0 }
  33. sub SS_REFCOUNT   () { 1 }
  34. sub SS_PARENT     () { 2 }
  35. sub SS_CHILDREN   () { 3 }
  36. sub SS_PROCESSES  () { 4 }
  37. sub SS_ID         () { 5 }
  38.  
  39. sub _data_ses_preload {
  40.    $POE::Kernel::poe_kernel->[KR_SESSIONS] = \%kr_sessions;
  41. }
  42.  
  43. use POE::API::ResLoader \&_data_ses_preload;
  44.  
  45. ### End-run leak checking.
  46.  
  47. sub _data_ses_finalize {
  48.   my $finalized_ok = 1;
  49.  
  50.   while (my ($ses, $ses_rec) = each(%kr_sessions)) {
  51.     $finalized_ok = 0;
  52.  
  53.     _warn(
  54.       "!!! Leaked session: $ses\n",
  55.       "!!!\trefcnt = $ses_rec->[SS_REFCOUNT]\n",
  56.       "!!!\tparent = $ses_rec->[SS_PARENT]\n",
  57.       "!!!\tchilds = ", join("; ", keys(%{$ses_rec->[SS_CHILDREN]})), "\n",
  58.       "!!!\tprocs  = ", join("; ", keys(%{$ses_rec->[SS_PROCESSES]})),"\n",
  59.     );
  60.   }
  61.  
  62.   return $finalized_ok;
  63. }
  64.  
  65. ### Enter a new session into the back-end stuff.
  66.  
  67. sub _data_ses_allocate {
  68.   my ($self, $session, $sid, $parent) = @_;
  69.  
  70.   $kr_sessions{$session} =
  71.     [ $session,  # SS_SESSION
  72.       0,         # SS_REFCOUNT
  73.       $parent,   # SS_PARENT
  74.       { },       # SS_CHILDREN
  75.       { },       # SS_PROCESSES
  76.       $sid,      # SS_ID
  77.     ];
  78.  
  79.   # For the ID to session reference lookup.
  80.   $self->_data_sid_set($sid, $session);
  81.  
  82.   # Manage parent/child relationship.
  83.   if (defined $parent) {
  84.     if (ASSERT_DATA) {
  85.       unless (exists $kr_sessions{$parent}) {
  86.         _trap "parent $parent does not exist";
  87.       }
  88.     }
  89.  
  90.     if (TRACE_SESSIONS) {
  91.       _warn(
  92.         "<ss> ",
  93.         $self->_data_alias_loggable($session), " has parent ",
  94.         $self->_data_alias_loggable($parent)
  95.       );
  96.     }
  97.  
  98.     $kr_sessions{$parent}->[SS_CHILDREN]->{$session} = $session;
  99.     $self->_data_ses_refcount_inc($parent);
  100.   }
  101. }
  102.  
  103. # Release a session's resources, and remove it.  This doesn't do
  104. # garbage collection for the session itself because that should
  105. # already have happened.
  106. #
  107. # -><- This is yet another place where resources will need to register
  108. # a function.  Every resource's _data_???_clear_session is called
  109. # here.
  110.  
  111. sub _data_ses_free {
  112.   my ($self, $session) = @_;
  113.  
  114.   if (TRACE_SESSIONS) {
  115.     _warn(
  116.       "<ss> freeing ",
  117.       $self->_data_alias_loggable($session)
  118.     );
  119.   }
  120.  
  121.   # Manage parent/child relationships.
  122.  
  123.   my $parent = $kr_sessions{$session}->[SS_PARENT];
  124.   my @children = $self->_data_ses_get_children($session);
  125.  
  126.   if (defined $parent) {
  127.     if (ASSERT_DATA) {
  128.       if ($parent == $session) {
  129.         _trap "session is its own parent";
  130.       }
  131.       unless ($self->_data_ses_is_child($parent, $session)) {
  132.         _trap(
  133.           $self->_data_alias_loggable($session), " isn't a child of ",
  134.           $self->_data_alias_loggable($parent), " (it's a child of ",
  135.           $self->_data_alias_loggable($self->_data_ses_get_parent($session)),
  136.           ")"
  137.         );
  138.       }
  139.       unless (exists $kr_sessions{$parent}) {
  140.         _trap "internal inconsistency ($parent)";
  141.       }
  142.     }
  143.  
  144.     # Remove the departing session from its parent.
  145.  
  146.     _trap "internal inconsistency ($parent/$session)"
  147.       unless delete $kr_sessions{$parent}->[SS_CHILDREN]->{$session};
  148.     undef $kr_sessions{$session}->[SS_PARENT];
  149.  
  150.     if (TRACE_SESSIONS) {
  151.       _cluck(
  152.         "<ss> removed ",
  153.         $self->_data_alias_loggable($session), " from ",
  154.         $self->_data_alias_loggable($parent)
  155.       );
  156.     }
  157.  
  158.     $self->_data_ses_refcount_dec($parent);
  159.  
  160.     # Move the departing session's children to its parent.
  161.  
  162.     foreach (@children) {
  163.       $self->_data_ses_move_child($_, $parent)
  164.     }
  165.   }
  166.   elsif (ASSERT_DATA) {
  167.     _trap "no parent to give children to" if @children;
  168.   }
  169.  
  170.   # Things which do not hold reference counts.
  171.  
  172.   $self->_data_sid_clear($session);            # Remove from SID tables.
  173.   $self->_data_sig_clear_session($session);    # Remove all leftover signals.
  174.  
  175.   # Things which do hold reference counts.
  176.  
  177.   $self->_data_alias_clear_session($session);  # Remove all leftover aliases.
  178.   $self->_data_extref_clear_session($session); # Remove all leftover extrefs.
  179.   $self->_data_handle_clear_session($session); # Remove all leftover handles.
  180.   $self->_data_ev_clear_session($session);     # Remove all leftover events.
  181.  
  182.   # Remove the session itself.
  183.  
  184.   delete $kr_sessions{$session};
  185. }
  186.  
  187. ### Move a session to a new parent.
  188.  
  189. sub _data_ses_move_child {
  190.   my ($self, $session, $new_parent) = @_;
  191.  
  192.   if (TRACE_SESSIONS) {
  193.     _warn(
  194.       "<ss> moving ",
  195.       $self->_data_alias_loggable($session), " to ",
  196.       $self->_data_alias_loggable($new_parent)
  197.     );
  198.   }
  199.  
  200.   if (ASSERT_DATA) {
  201.     _trap() unless exists $kr_sessions{$session};
  202.     _trap() unless exists $kr_sessions{$new_parent};
  203.   }
  204.  
  205.   my $old_parent = $self->_data_ses_get_parent($session);
  206.  
  207.   if (ASSERT_DATA) {
  208.     _trap() unless exists $kr_sessions{$old_parent};
  209.   }
  210.  
  211.   # Remove the session from its old parent.
  212.   delete $kr_sessions{$old_parent}->[SS_CHILDREN]->{$session};
  213.  
  214.   if (TRACE_SESSIONS) {
  215.     _warn(
  216.       "<ss> removed ",
  217.       $self->_data_alias_loggable($session), " from ",
  218.       $self->_data_alias_loggable($old_parent)
  219.     );
  220.   }
  221.  
  222.   $self->_data_ses_refcount_dec($old_parent);
  223.  
  224.   # Change the session's parent.
  225.   $kr_sessions{$session}->[SS_PARENT] = $new_parent;
  226.  
  227.   if (TRACE_SESSIONS) {
  228.     _warn(
  229.       "<ss> changed parent of ",
  230.       $self->_data_alias_loggable($session), " to ",
  231.       $self->_data_alias_loggable($new_parent)
  232.     );
  233.   }
  234.  
  235.   # Add the current session to the new parent's children.
  236.   $kr_sessions{$new_parent}->[SS_CHILDREN]->{$session} = $session;
  237.  
  238.   if (TRACE_SESSIONS) {
  239.     _warn(
  240.       "<ss> added ",
  241.       $self->_data_alias_loggable($session), " as child of ",
  242.       $self->_data_alias_loggable($new_parent)
  243.     );
  244.   }
  245.  
  246.   $self->_data_ses_refcount_inc($new_parent);
  247.  
  248.   # We do not call _data_ses_collect_garbage() here.  This function is
  249.   # called in batch for a departing session, to move its children to
  250.   # its parent.  The GC test would be superfluous here.  Rather, it's
  251.   # up to the caller to do the proper GC test after moving things
  252.   # around.
  253. }
  254.  
  255. ### Get a session's parent.
  256.  
  257. sub _data_ses_get_parent {
  258.   my ($self, $session) = @_;
  259.   if (ASSERT_DATA) {
  260.     _trap("retrieving parent of a nonexistent session")
  261.       unless exists $kr_sessions{$session};
  262.   }
  263.   return $kr_sessions{$session}->[SS_PARENT];
  264. }
  265.  
  266. ### Get a session's children.
  267.  
  268. sub _data_ses_get_children {
  269.   my ($self, $session) = @_;
  270.   if (ASSERT_DATA) {
  271.     _trap("retrieving children of a nonexistent session")
  272.       unless exists $kr_sessions{$session};
  273.   }
  274.   return values %{$kr_sessions{$session}->[SS_CHILDREN]};
  275. }
  276.  
  277. ### Is a session a child of another?
  278.  
  279. sub _data_ses_is_child {
  280.   my ($self, $parent, $child) = @_;
  281.   if (ASSERT_DATA) {
  282.     _trap("testing is-child of a nonexistent parent session")
  283.       unless exists $kr_sessions{$parent};
  284.   }
  285.   return exists $kr_sessions{$parent}->[SS_CHILDREN]->{$child};
  286. }
  287.  
  288. ### Determine whether a session exists.  We should only need to verify
  289. ### this for sessions provided by the outside.  Internally, our code
  290. ### should be so clean it's not necessary.
  291.  
  292. sub _data_ses_exists {
  293.   my ($self, $session) = @_;
  294.   return exists $kr_sessions{$session};
  295. }
  296.  
  297. ### Resolve a session into its reference.
  298.  
  299. sub _data_ses_resolve {
  300.   my ($self, $session) = @_;
  301.   return undef unless exists $kr_sessions{$session}; # Prevents autoviv.
  302.   return $kr_sessions{$session}->[SS_SESSION];
  303. }
  304.  
  305. ### Resolve a session ID into its reference.
  306.  
  307. sub _data_ses_resolve_to_id {
  308.   my ($self, $session) = @_;
  309.   return undef unless exists $kr_sessions{$session}; # Prevents autoviv.
  310.   return $kr_sessions{$session}->[SS_ID];
  311. }
  312.  
  313. ### Decrement a session's main reference count.  This is called by
  314. ### each watcher when the last thing it watches for the session goes
  315. ### away.  In other words, a session's reference count should only
  316. ### enumerate the different types of things being watched; not the
  317. ### number of each.
  318.  
  319. sub _data_ses_refcount_dec {
  320.   my ($self, $session) = @_;
  321.  
  322.   if (TRACE_REFCNT) {
  323.     _warn(
  324.       "<rc> decrementing refcount for ",
  325.       $self->_data_alias_loggable($session)
  326.     );
  327.   }
  328.  
  329.   # -><- Why do we return if the session does not exist, but then confess
  330.   # that there is a problem if the session does not exist?  One of
  331.   # these must go!
  332.   return unless exists $kr_sessions{$session};
  333.  
  334.   if (ASSERT_DATA) {
  335.     _trap() unless exists $kr_sessions{$session};
  336.   }
  337.  
  338.   $kr_sessions{$session}->[SS_REFCOUNT]--;
  339.  
  340.   if (ASSERT_DATA and $kr_sessions{$session}->[SS_REFCOUNT] < 0) {
  341.     _trap(
  342.       $self->_data_alias_loggable($session),
  343.      " reference count went below zero"
  344.    );
  345.   }
  346. }
  347.  
  348. ### Increment a session's main reference count.
  349.  
  350. sub _data_ses_refcount_inc {
  351.   my ($self, $session) = @_;
  352.  
  353.   if (TRACE_REFCNT) {
  354.     _warn(
  355.       "<rc> incrementing refcount for ",
  356.       $self->_data_alias_loggable($session)
  357.     );
  358.   }
  359.  
  360.   if (ASSERT_DATA) {
  361.     _trap "incrementing refcount for nonexistent session"
  362.       unless exists $kr_sessions{$session};
  363.   }
  364.  
  365.   $kr_sessions{$session}->[SS_REFCOUNT]++;
  366. }
  367.  
  368. # Query a session's reference count.  Added for testing purposes.
  369.  
  370. sub _data_ses_refcount {
  371.   my ($self, $session) = @_;
  372.   return $kr_sessions{$session}->[SS_REFCOUNT];
  373. }
  374.  
  375. ### Determine whether a session is ready to be garbage collected.
  376. ### Free the session if it is.
  377.  
  378. sub _data_ses_collect_garbage {
  379.   my ($self, $session) = @_;
  380.  
  381.   if (TRACE_REFCNT) {
  382.     _warn(
  383.       "<rc> testing for idle ",
  384.       $self->_data_alias_loggable($session)
  385.     );
  386.   }
  387.  
  388.   # The next line is necessary for some strange reason.  This feels
  389.   # like a kludge, but I'm currently not smart enough to figure out
  390.   # what it's working around.
  391.  
  392.   if (ASSERT_DATA) {
  393.     _trap() unless exists $kr_sessions{$session};
  394.   }
  395.  
  396.   if (TRACE_REFCNT) {
  397.     my $ss = $kr_sessions{$session};
  398.     _warn(
  399.       "<rc> +----- GC test for ", $self->_data_alias_loggable($session),
  400.       " ($session) -----\n",
  401.       "<rc> | total refcnt  : ", $ss->[SS_REFCOUNT], "\n",
  402.       "<rc> | event count   : ", $self->_data_ev_get_count_to($session), "\n",
  403.       "<rc> | post count    : ", $self->_data_ev_get_count_from($session), "\n",
  404.       "<rc> | child sessions: ", scalar(keys(%{$ss->[SS_CHILDREN]})), "\n",
  405.       "<rc> | handles in use: ", $self->_data_handle_count_ses($session), "\n",
  406.       "<rc> | aliases in use: ", $self->_data_alias_count_ses($session), "\n",
  407.       "<rc> | extra refs    : ", $self->_data_extref_count_ses($session), "\n",
  408.       "<rc> +---------------------------------------------------\n",
  409.     );
  410.     unless ($ss->[SS_REFCOUNT]) {
  411.       _warn(
  412.         "<rc> | ", $self->_data_alias_loggable($session),
  413.         " is garbage; stopping it...\n",
  414.         "<rc> +---------------------------------------------------\n",
  415.       );
  416.     }
  417.   }
  418.  
  419.   if (ASSERT_DATA) {
  420.     my $ss = $kr_sessions{$session};
  421.     my $calc_ref = (
  422.       $self->_data_ev_get_count_to($session) +
  423.       $self->_data_ev_get_count_from($session) +
  424.       scalar(keys(%{$ss->[SS_CHILDREN]})) +
  425.       $self->_data_handle_count_ses($session) +
  426.       $self->_data_extref_count_ses($session) +
  427.       $self->_data_alias_count_ses($session)
  428.     );
  429.  
  430.     # The calculated reference count really ought to match the one
  431.     # POE's been keeping track of all along.
  432.  
  433.     _trap(
  434.       "<dt> ", $self->_data_alias_loggable($session),
  435.        " has a reference count inconsistency",
  436.        " (calc=$calc_ref; actual=$ss->[SS_REFCOUNT])\n"
  437.      ) if $calc_ref != $ss->[SS_REFCOUNT];
  438.   }
  439.  
  440.   return if $kr_sessions{$session}->[SS_REFCOUNT];
  441.  
  442.   $self->_data_ses_stop($session);
  443. }
  444.  
  445. ### Return the number of sessions we know about.
  446.  
  447. sub _data_ses_count {
  448.   return scalar keys %kr_sessions;
  449. }
  450.  
  451. ### Close down a session by force.
  452.  
  453. # Stop a session, dispatching _stop, _parent, and _child as necessary.
  454. #
  455. # Dispatch _stop to a session, removing it from the kernel's data
  456. # structures as a side effect.
  457.  
  458. sub _data_ses_stop {
  459.   my ($self, $session) = @_;
  460.  
  461.   if (TRACE_SESSIONS) {
  462.     _warn("<ss> stopping ", $self->_data_alias_loggable($session));
  463.   }
  464.  
  465.   if (ASSERT_DATA) {
  466.     _trap unless exists $kr_sessions{$session};
  467.   }
  468.  
  469.   # Maintain referential integrity between parents and children.
  470.   # First move the children of the stopping session up to its parent.
  471.   my $parent = $self->_data_ses_get_parent($session);
  472.  
  473.   foreach my $child ($self->_data_ses_get_children($session)) {
  474.     $self->_dispatch_event(
  475.       $parent, $self,
  476.       EN_CHILD, ET_CHILD, [ CHILD_GAIN, $child ],
  477.       __FILE__, __LINE__, time(), -__LINE__
  478.     );
  479.     $self->_dispatch_event(
  480.       $child, $self,
  481.       EN_PARENT, ET_PARENT,
  482.       [ $self->_data_ses_get_parent($child), $parent, ],
  483.       __FILE__, __LINE__, time(), -__LINE__
  484.     );
  485.   }
  486.  
  487.   # If the departing session has a parent, notify it that the session
  488.   # is being lost.
  489.  
  490.   if (defined $parent) {
  491.     $self->_dispatch_event(
  492.       $parent, $self,
  493.       EN_CHILD, ET_CHILD, [ CHILD_LOSE, $session ],
  494.       __FILE__, __LINE__, time(), -__LINE__
  495.     );
  496.   }
  497.  
  498.   # Referential integrity has been dealt with.  Now notify the session
  499.   # that it has been stopped.
  500.   $self->_dispatch_event(
  501.     $session, $self->get_active_session(),
  502.     EN_STOP, ET_STOP, [],
  503.     __FILE__, __LINE__, time(), -__LINE__
  504.   );
  505.  
  506.   # Deallocate the session.
  507.   $self->_data_ses_free($session);
  508.  
  509.   # GC the parent, if there is one.
  510.   if (defined $parent) {
  511.     $self->_data_ses_collect_garbage($parent);
  512.   }
  513.  
  514.   # Stop the main loop if everything is gone.
  515.   unless (keys %kr_sessions) {
  516.     $self->loop_halt();
  517.   }
  518. }
  519.  
  520. 1;
  521.  
  522. __END__
  523.  
  524. =head1 NAME
  525.  
  526. POE::Resources::Sessions - manage session data structures for POE::Kernel
  527.  
  528. =head1 SYNOPSIS
  529.  
  530. Used internally by POE::Kernel.  Better documentation will be
  531. forthcoming.
  532.  
  533. =head1 DESCRIPTION
  534.  
  535. This module encapsulates and provides accessors for POE::Kernel's data
  536. structures that manage sessions themselves.  It is used internally by
  537. POE::Kernel and has no public interface.
  538.  
  539. =head1 SEE ALSO
  540.  
  541. See L<POE::Kernel> and L<POE::Session> for documentation on sessions.
  542.  
  543. =head1 BUGS
  544.  
  545. Probably.
  546.  
  547. =head1 AUTHORS & COPYRIGHTS
  548.  
  549. Please see L<POE> for more information about authors and contributors.
  550.  
  551. =cut
  552.