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