Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

file 212 lines (161 sloc) 5.724 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
# The data necessary to manage tagged extra/external reference counts
# on sessions, and the accessors to get at them sanely from other
# files.

package POE::Resource::Extrefs;

use vars qw($VERSION);
$VERSION = '1.299'; # NOTE - Should be #.### (three decimal places)

# These methods are folded into POE::Kernel;
package POE::Kernel;

use strict;

### The count of all extra references used in the system.

my %kr_extra_refs;
# ( $session =>
# { $tag => $count,
# ...,
# },
# ...,
# );

### End-run leak checking.

sub _data_extref_finalize {
  my $finalized_ok = 1;
  foreach my $session (keys %kr_extra_refs) {
    $finalized_ok = 0;
    _warn "!!! Leaked extref: $session\n";
    foreach my $tag (keys %{$kr_extra_refs{$session}}) {
      _warn "!!!\t`$tag' = $kr_extra_refs{$session}->{$tag}\n";
    }
  }
  return $finalized_ok;
}

# Increment a session's tagged reference count. If this is the first
# time the tag is used in the session, then increment the session's
# reference count as well. Returns the tag's new reference count.
#
# TODO Allows incrementing reference counts on sessions that don't
# exist, but the public interface catches that.
#
# TODO Need to track extref ownership for signal-based session
# termination. One problem seen is that signals terminate sessions
# out of order. Owners think extra refcounts exist for sessions that
# are no longer around. Ownership trees give us a few benefits: We
# can make sure sessions destruct in a cleaner order. We can detect
# refcount loops and possibly prevent that.

sub _data_extref_inc {
  my ($self, $session, $tag) = @_;
  my $refcount = ++$kr_extra_refs{$session}->{$tag};

  # TODO We could probably get away with only incrementing the
  # session's master refcount once, as long as any extra refcount is
  # positive. Then the session reference count would be a flag
  # instead of a counter.
  $self->_data_ses_refcount_inc($session) if $refcount == 1;

  if (TRACE_REFCNT) {
    _warn(
      "<rc> incremented extref ``$tag'' (now $refcount) for ",
      $self->_data_alias_loggable($session)
    );
  }

  return $refcount;
}

# Decrement a session's tagged reference count, removing it outright
# if the count reaches zero. Return the new reference count or undef
# if the tag doesn't exist.
#
# TODO Allows negative reference counts, and the resulting hilarity.
# Hopefully the public interface won't allow it.

sub _data_extref_dec {
  my ($self, $session, $tag) = @_;

  if (ASSERT_DATA) {
    # Prevents autoviv.
    _trap("<dt> decrementing extref for session without any")
      unless exists $kr_extra_refs{$session};

    unless (exists $kr_extra_refs{$session}->{$tag}) {
      _trap(
        "<dt> decrementing extref for nonexistent tag ``$tag'' in ",
        $self->_data_alias_loggable($session)
      );
    }
  }

  my $refcount = --$kr_extra_refs{$session}->{$tag};

  if (TRACE_REFCNT) {
    _warn(
      "<rc> decremented extref ``$tag'' (now $refcount) for ",
      $self->_data_alias_loggable($session)
    );
  }

  $self->_data_extref_remove($session, $tag) unless $refcount;
  return $refcount;
}

### Remove an extra reference from a session, regardless of its count.

sub _data_extref_remove {
  my ($self, $session, $tag) = @_;

  if (ASSERT_DATA) {
    # Prevents autoviv.
    _trap("<dt> removing extref from session without any")
      unless exists $kr_extra_refs{$session};
    unless (exists $kr_extra_refs{$session}->{$tag}) {
      _trap(
        "<dt> removing extref for nonexistent tag ``$tag'' in ",
        $self->_data_alias_loggable($session)
      );
    }
  }

  delete $kr_extra_refs{$session}->{$tag};
  delete $kr_extra_refs{$session}
    unless scalar keys %{$kr_extra_refs{$session}};
  $self->_data_ses_refcount_dec($session);
}

### Clear all the extra references from a session.

sub _data_extref_clear_session {
  my ($self, $session) = @_;

  # TODO - Should there be a _trap here if the session doesn't exist?

  return unless exists $kr_extra_refs{$session}; # avoid autoviv
  foreach (keys %{$kr_extra_refs{$session}}) {
    $self->_data_extref_remove($session, $_);
  }

  if (ASSERT_DATA) {
    if (exists $kr_extra_refs{$session}) {
      _trap(
        "<dt> extref clear did not remove session ",
        $self->_data_alias_loggable($session)
      );
    }
  }
}

# Fetch the number of sessions with extra references held in the
# entire system.

sub _data_extref_count {
  return scalar keys %kr_extra_refs;
}

# Fetch whether a session has extra references.

sub _data_extref_count_ses {
  my ($self, $session) = @_;
  return 0 unless exists $kr_extra_refs{$session};
  return scalar keys %{$kr_extra_refs{$session}};
}

1;

__END__

=head1 NAME

POE::Resource::Extrefs - internal reference counts manager for POE::Kernel

=head1 SYNOPSIS

There is no public API.

=head1 DESCRIPTION

POE::Resource::Extrefs is a mix-in class for POE::Kernel. It provides
the features to manage session reference counts, specifically the ones
that applications may use. POE::Resource::Extrefs is used internally
by POE::Kernel, so it has no public interface.

=head1 SEE ALSO

See L<POE::Kernel/Public Reference Counters> for the public extref
API.

See L<POE::Kernel/Resources> for for public information about POE
resources.

See L<POE::Resource> for general discussion about resources and the
classes that manage them.

=head1 BUGS

Reference counters have no ownership information, so one entity's
reference counts may conflict with another's. This is usually not a
problem if all entities behave.

=head1 AUTHORS & COPYRIGHTS

Please see L<POE> for more information about authors and contributors.

=cut

# rocco // vim: ts=2 sw=2 expandtab
# TODO - Edit.
Something went wrong with that request. Please try again.