1Test2::Tools::Refcount(U3s)er Contributed Perl DocumentatTieosnt2::Tools::Refcount(3)
2
3
4

NAME

6       "Test2::Tools::Refcount" - assert reference counts on objects
7

SYNOPSIS

9          use Test2::Tools::Refcount;
10
11          use Some::Class;
12          my $object = Some::Class->new();
13
14          is_oneref( $object, '$object has a refcount of 1' );
15
16          my $otherref = $object;
17
18          is_refcount( $object, 2, '$object now has 2 references' );
19

DESCRIPTION

21       The Perl garbage collector uses simple reference counting during the
22       normal execution of a program. This means that cycles or unweakened
23       references in other parts of code can keep an object around for longer
24       than intended. To help avoid this problem, the reference count of a new
25       object from its class constructor ought to be 1. This way, the caller
26       can know the object will be properly DESTROYed when it drops all of its
27       references to it.
28
29       This module provides two test functions to help ensure this property
30       holds for an object class, so as to be polite to its callers.
31
32       If the assertion fails; that is, if the actual reference count is
33       different to what was expected, either of the following two modules may
34       be used to assist the developer in finding where the references are.
35
36       •   If Devel::MAT is installed, this test module will use it to dump
37           the state of the memory after a failure. It will create a .pmat
38           file named the same as the unit test, but with the trailing .t
39           suffix replaced with -TEST.pmat where "TEST" is the number of the
40           test that failed (in case there was more than one).
41
42       See the examples below for more information.
43

FUNCTIONS

45   is_refcount
46          is_refcount( $object, $count, $name )
47
48       Test that $object has $count references to it.
49
50   is_oneref
51          is_oneref( $object, $name )
52
53       Assert that the $object has only 1 reference to it.
54
55   refcount
56          $count = refcount( $object )
57
58       Returns the reference count of the given object as used by the test
59       functions.  This is useful for making tests that don't care what the
60       count is before they start, but simply assert that the count hasn't
61       changed by the end.
62
63          use Test2::Tools::Refcount import => [qw( is_refcount refcount )];
64          {
65             my $count = refcount( $object );
66
67             do_something( $object );
68
69             is_refcount( $object, $count, 'do_something() preserves refcount' );
70          }
71

EXAMPLE

73       Suppose, having written a new class "MyBall", you now want to check
74       that its constructor and methods are well-behaved, and don't leak
75       references. Consider the following test script:
76
77          use Test::More tests => 2;
78          use Test2::Tools::Refcount;
79
80          use MyBall;
81
82          my $ball = MyBall->new();
83          is_oneref( $ball, 'One reference after construct' );
84
85          $ball->bounce;
86
87          # Any other code here that might be part of the test script
88
89          is_oneref( $ball, 'One reference just before EOF' );
90
91       The first assertion is just after the constructor, to check that the
92       reference returned by it is the only reference to that object. This
93       fact is important if we ever want "DESTROY" to behave properly. The
94       second call is right at the end of the file, just before the main scope
95       closes. At this stage we expect the reference count also to be one, so
96       that the object is properly cleaned up.
97
98       Suppose, when run, this produces the following output (presuming
99       Devel::MAT::Dumper is available):
100
101          1..2
102          ok 1 - One reference after construct
103          not ok 2 - One reference just before EOF
104          #   Failed test 'One reference just before EOF'
105          #   at ex.pl line 26.
106          #   expected 1 references, found 2
107          # SV address is 0x55e14c310278
108          # Writing heap dump to ex-2.pmat
109          # Looks like you failed 1 test of 2.
110
111       This has written a ex-2.pmat file we can load using the "pmat" shell
112       and use the "identify" command on the given address to find where it
113       went:
114
115          $ pmat ex-2.pmat
116          Perl memory dumpfile from perl 5.28.1 threaded
117          Heap contains 25233 objects
118          pmat> identify 0x55e14c310278
119          HASH(0)=MyBall at 0x55e14c310278 is:
120          ├─(via RV) the lexical $ball at depth 1 of CODE() at 0x55e14c3104a0=main_cv, which is:
121          │ └─the main code
122          └─(via RV) value {self} of HASH(2) at 0x55e14cacb860, which is (*A):
123            └─(via RV) value {cycle} of HASH(2) at 0x55e14cacb860, which is:
124              itself
125
126       (This document isn't intended to be a full tutorial on Devel::MAT and
127       the "pmat" shell; for that see Devel::MAT::UserGuide).
128
129       From this output, we can see that the constructor was well-behaved, but
130       that a reference was leaked by the end of the script - the reference
131       count was 2, when we expected just 1. Reading the trace output, we can
132       see that there were 2 references that could be found - one stored in
133       the $ball lexical in the main program, and one stored in a HASH. Since
134       we expected to find the $ball lexical variable, we know we are now
135       looking for a leak in a hash somewhere in the code. From reading the
136       test script, we can guess this leak is likely to be in the bounce()
137       method. Furthermore, we know that the reference to the object will be
138       stored in a HASH in a member called "self".
139
140       By reading the code which implements the bounce() method, we can see
141       this is indeed the case:
142
143          sub bounce
144          {
145             my $self = shift;
146             my $cycle = { self => $self };
147             $cycle->{cycle} = $cycle;
148          }
149
150       From reading the tracing output, we find that the HASH this object is
151       referenced in also contains a reference to itself, in a member called
152       "cycle". This comes from the last line in this function, a line that
153       purposely created a cycle, to demonstrate the point. While a real
154       program probably wouldn't do anything quite this obvious, the trace
155       would still be useful in finding the likely cause of the leak.
156
157       If "Devel::MAT::Dumper" is not available, then these detailed traces
158       will not be produced. The basic reference count testing will still take
159       place, but a smaller message will be produced:
160
161          1..2
162          ok 1 - One reference after construct
163          not ok 2 - One reference just before EOF
164          #   Failed test 'One reference just before EOF'
165          #   at demo.pl line 16.
166          #   expected 1 references, found 2
167          # Looks like you failed 1 test of 2.
168

BUGS

170       •   Temporaries created on the stack
171
172           Code which creates temporaries on the stack, to be released again
173           when the called function returns does not work correctly on perl
174           5.8 (and probably before). Examples such as
175
176              is_oneref( [] );
177
178           may fail and claim a reference count of 2 instead.
179
180           Passing a variable such as
181
182              my $array = [];
183              is_oneref( $array );
184
185           works fine. Because of the intention of this test module; that is,
186           to assert reference counts on some object stored in a variable
187           during the lifetime of the test script, this is unlikely to cause
188           any problems.
189

ACKNOWLEDGEMENTS

191       Peter Rabbitson <ribasushi@cpan.org> - for suggesting using core's "B"
192       instead of "Devel::Refcount" to obtain refcounts
193

AUTHOR

195       Paul Evans <leonerd@leonerd.org.uk>
196
197
198
199perl v5.38.0                      2023-07-21         Test2::Tools::Refcount(3)
Impressum