File Coverage

lib/UR/Context/AutoUnloadPool.pm
Criterion Covered Total %
statement 55 59 93.2
branch 10 16 62.5
condition n/a
subroutine 13 13 100.0
pod 2 2 100.0
total 80 90 88.8


line stmt bran cond sub pod time code
1             package UR::Context::AutoUnloadPool;
2              
3 266     266   941 use strict;
  266         320  
  266         6176  
4 266     266   830 use warnings;
  266         311  
  266         9283  
5              
6             require UR;
7             our $VERSION = "0.46"; # UR $VERSION
8              
9 266     266   941 use Scalar::Util qw();
  266         276  
  266         135164  
10              
11             # These are plain Perl objects that get garbage collected in the normal way,
12             # not UR::Objects
13              
14             our @CARP_NOT = qw( UR::Context );
15              
16             my $pool_count = 0;
17 25     25   89 sub _pool_count { $pool_count }
18              
19             sub create {
20 12     12 1 5207 my $class = shift;
21 12         52 my $self = bless { pool => {} }, $class;
22 12         33 $self->_attach_observer();
23 12         11 $pool_count++;
24 12         40 UR::Context::manage_objects_may_go_out_of_scope();
25 12         35 return $self;
26             }
27              
28             sub delete {
29 1     1 1 6 my $self = shift;
30 1         3 delete $self->{pool};
31 1         3 $self->_detach_observer();
32 1         3 $pool_count--;
33 1         3 UR::Context::manage_objects_may_go_out_of_scope();
34 1         7 return 1;
35             }
36              
37             sub _attach_observer {
38 12     12   19 my $self = shift;
39 12         39 Scalar::Util::weaken($self);
40             my $o = UR::Object->add_observer(
41             aspect => 'load',
42             callback => sub {
43 26     26   38 my $loaded = shift;
44              
45 26 100       137 return if ! $loaded->is_prunable();
46 17         51 $self->_object_was_loaded($loaded);
47             }
48 12         91 );
49 12         46 $self->{observer} = $o;
50             }
51              
52             sub _detach_observer {
53 12     12   19 my $self = shift;
54 12         61 delete($self->{observer})->delete();
55             }
56              
57             sub _is_printing_debug {
58 40 50   40   153 $ENV{UR_DEBUG_OBJECT_PRUNING} || $ENV{'UR_DEBUG_OBJECT_RELEASE'};
59             }
60              
61             sub _object_was_loaded {
62 17     17   27 my($self, $o) = @_;
63 17 50       50 if (_is_printing_debug()) {
64 0         0 my($class, $id) = ($o->class, $o->id);
65 0         0 print STDERR Carp::shortmess("MEM AUTORELEASE $class id $id loaded in pool $self\n");
66             }
67 17         64 $self->{pool}->{$o->class}->{$o->id} = undef;
68             }
69              
70             sub _unload_objects {
71 11     11   38 my $self = shift;
72 11 50       35 return unless $self->{pool};
73              
74 11 50       22 print STDERR Carp::shortmess("MEM AUTORELEASE pool $self draining\n") if _is_printing_debug();
75              
76 11         18 foreach my $class_name ( keys %{$self->{pool}} ) {
  11         48  
77 12 50       34 if (_is_printing_debug()) {
78             printf STDERR "MEM AUTORELEASE class $class_name: %s\n",
79 0         0 join(', ', values %{ $self->{pool}->{$class_name}} );
  0         0  
80             }
81 12         20 my $objs_for_class = $UR::Context::all_objects_loaded->{$class_name};
82 12 50       22 next unless $objs_for_class;
83 16         51 my @objs_to_release = grep { ! $_->__changes__ }
84 12         13 @$objs_for_class{ keys %{$self->{pool}->{$class_name}}};
  12         39  
85 12         43 UR::Context->current->_weaken_references_for_objects(\@objs_to_release);
86             }
87 11         33 delete $self->{pool};
88             }
89              
90             sub DESTROY {
91 12     12   2055 local $@;
92              
93 12         16 my $self = shift;
94 12 100       45 return unless ($self->{pool});
95 11         30 $self->_detach_observer();
96 11         40 $self->_unload_objects();
97 11         16 $pool_count--;
98 11         71 UR::Context::manage_objects_may_go_out_of_scope();
99             }
100              
101             1;
102              
103             =pod
104              
105             =head1 NAME
106              
107             UR::Context::AutoUnloadPool - Automatically unload objects when scope ends
108              
109             =head1 SYNOPSIS
110              
111             my $not_unloaded = Some::Class->get(...);
112             do {
113             my $guard = UR::Context::AutoUnloadPool->create();
114             my $object = Some::Class->get(...); # load an object from the database
115             ... # load more things
116             }; # $guard goes out of scope - unloads objects
117              
118             =head1 DESCRIPTION
119              
120             UR Objects retrieved from the database normally live in the object cache for
121             the life of the program. When a UR::Context::AutoUnloadPool is instantiated,
122             it tracks every object loaded during its life. The Pool's destructor calls
123             unload() on those objects.
124              
125             Changed objects and objects loaded before before the Pool is created will not
126             get unloaded.
127              
128             =head1 METHODS
129              
130             =over 4
131              
132             =item create
133              
134             my $guard = UR::Context::AutoUnloadPool->create();
135              
136             Creates a Pool object. All UR Objects loaded from the database during this
137             object's lifetime will get unloaded when the Pool goes out of scope.
138              
139             =item delete
140              
141             $guard->delete();
142              
143             Invalidates the Pool object. No objects are unloaded. When the Pool later
144             goes out of scope, no objects will be unloaded.
145              
146             =back
147              
148             =head1 SEE ALSO
149              
150             UR::Object, UR::Context
151              
152             =cut