1# The data necessary to manage tagged extra/external reference counts 2# on sessions, and the accessors to get at them sanely from other 3# files. 4 5package POE::Resource::Extrefs; 6 7use vars qw($VERSION); 8$VERSION = '1.368'; # NOTE - Should be #.### (three decimal places) 9 10# These methods are folded into POE::Kernel; 11package POE::Kernel; 12 13use strict; 14 15### The count of all extra references used in the system. 16 17my %kr_extra_refs; 18# ( $session_id => 19# { $tag => $count, 20# ..., 21# }, 22# ..., 23# ); 24 25sub _data_extref_relocate_kernel_id { 26 my ($self, $old_id, $new_id) = @_; 27 return unless exists $kr_extra_refs{$old_id}; 28 $kr_extra_refs{$new_id} = delete $kr_extra_refs{$old_id}; 29} 30 31### End-run leak checking. 32 33sub _data_extref_finalize { 34 my $finalized_ok = 1; 35 foreach my $session_id (keys %kr_extra_refs) { 36 $finalized_ok = 0; 37 _warn "!!! Leaked extref: $session_id\n"; 38 foreach my $tag (keys %{$kr_extra_refs{$session_id}}) { 39 _warn "!!!\t`$tag' = $kr_extra_refs{$session_id}->{$tag}\n"; 40 } 41 } 42 return $finalized_ok; 43} 44 45# Increment a session's tagged reference count. If this is the first 46# time the tag is used in the session, then increment the session's 47# reference count as well. Returns the tag's new reference count. 48# 49# TODO Allows incrementing reference counts on sessions that don't 50# exist, but the public interface catches that. 51# 52# TODO Need to track extref ownership for signal-based session 53# termination. One problem seen is that signals terminate sessions 54# out of order. Owners think extra refcounts exist for sessions that 55# are no longer around. Ownership trees give us a few benefits: We 56# can make sure sessions destruct in a cleaner order. We can detect 57# refcount loops and possibly prevent that. 58 59sub _data_extref_inc { 60 my ($self, $sid, $tag) = @_; 61 my $refcount = ++$kr_extra_refs{$sid}->{$tag}; 62 63 # TODO We could probably get away with only incrementing the 64 # session's master refcount once, as long as any extra refcount is 65 # positive. Then the session reference count would be a flag 66 # instead of a counter. 67 $self->_data_ses_refcount_inc($sid) if $refcount == 1; 68 69 if (TRACE_REFCNT) { 70 _warn( 71 "<rc> incremented extref ``$tag'' (now $refcount) for ", 72 $self->_data_alias_loggable($sid) 73 ); 74 } 75 76 return $refcount; 77} 78 79# Decrement a session's tagged reference count, removing it outright 80# if the count reaches zero. Return the new reference count or undef 81# if the tag doesn't exist. 82# 83# TODO Allows negative reference counts, and the resulting hilarity. 84# Hopefully the public interface won't allow it. 85 86sub _data_extref_dec { 87 my ($self, $sid, $tag) = @_; 88 89 if (ASSERT_DATA) { 90 # Prevents autoviv. 91 _trap("<dt> decrementing extref for session without any") 92 unless exists $kr_extra_refs{$sid}; 93 94 unless (exists $kr_extra_refs{$sid}->{$tag}) { 95 _trap( 96 "<dt> decrementing extref for nonexistent tag ``$tag'' in ", 97 $self->_data_alias_loggable($sid) 98 ); 99 } 100 } 101 102 my $refcount = --$kr_extra_refs{$sid}->{$tag}; 103 104 if (TRACE_REFCNT) { 105 _warn( 106 "<rc> decremented extref ``$tag'' (now $refcount) for ", 107 $self->_data_alias_loggable($sid) 108 ); 109 } 110 111 $self->_data_extref_remove($sid, $tag) unless $refcount; 112 return $refcount; 113} 114 115### Remove an extra reference from a session, regardless of its count. 116 117sub _data_extref_remove { 118 my ($self, $sid, $tag) = @_; 119 120 if (ASSERT_DATA) { 121 # Prevents autoviv. 122 _trap("<dt> removing extref from session without any") 123 unless exists $kr_extra_refs{$sid}; 124 unless (exists $kr_extra_refs{$sid}->{$tag}) { 125 _trap( 126 "<dt> removing extref for nonexistent tag ``$tag'' in ", 127 $self->_data_alias_loggable($sid) 128 ); 129 } 130 } 131 132 delete $kr_extra_refs{$sid}->{$tag}; 133 delete $kr_extra_refs{$sid} unless scalar keys %{$kr_extra_refs{$sid}}; 134 $self->_data_ses_refcount_dec($sid); 135} 136 137### Clear all the extra references from a session. 138 139sub _data_extref_clear_session { 140 my ($self, $sid) = @_; 141 142 # TODO - Should there be a _trap here if the session doesn't exist? 143 144 return unless exists $kr_extra_refs{$sid}; # avoid autoviv 145 foreach (keys %{$kr_extra_refs{$sid}}) { 146 $self->_data_extref_remove($sid, $_); 147 } 148 149 if (ASSERT_DATA) { 150 if (exists $kr_extra_refs{$sid}) { 151 _trap( 152 "<dt> extref clear did not remove session ", 153 $self->_data_alias_loggable($sid) 154 ); 155 } 156 } 157} 158 159# Fetch the number of sessions with extra references held in the 160# entire system. 161 162sub _data_extref_count { 163 return scalar keys %kr_extra_refs; 164} 165 166# Fetch whether a session has extra references. 167 168sub _data_extref_count_ses { 169 my ($self, $sid) = @_; 170 return 0 unless exists $kr_extra_refs{$sid}; 171 return scalar keys %{$kr_extra_refs{$sid}}; 172} 173 1741; 175 176__END__ 177 178=head1 NAME 179 180POE::Resource::Extrefs - internal reference counts manager for POE::Kernel 181 182=head1 SYNOPSIS 183 184There is no public API. 185 186=head1 DESCRIPTION 187 188POE::Resource::Extrefs is a mix-in class for POE::Kernel. It provides 189the features to manage session reference counts, specifically the ones 190that applications may use. POE::Resource::Extrefs is used internally 191by POE::Kernel, so it has no public interface. 192 193=head1 SEE ALSO 194 195See L<POE::Kernel/Public Reference Counters> for the public extref 196API. 197 198See L<POE::Kernel/Resources> for public information about POE 199resources. 200 201See L<POE::Resource> for general discussion about resources and the 202classes that manage them. 203 204=head1 BUGS 205 206Reference counters have no ownership information, so one entity's 207reference counts may conflict with another's. This is usually not a 208problem if all entities behave. 209 210=head1 AUTHORS & COPYRIGHTS 211 212Please see L<POE> for more information about authors and contributors. 213 214=cut 215 216# rocco // vim: ts=2 sw=2 expandtab 217# TODO - Edit. 218