1 | #!/usr/bin/env python2
|
2 | """
|
3 | gc_stack_roots.py
|
4 | """
|
5 | from __future__ import print_function
|
6 |
|
7 | import os
|
8 |
|
9 | from mycpp import mylib
|
10 |
|
11 | from typing import Any, List
|
12 | """
|
13 | Helpers
|
14 | """
|
15 |
|
16 |
|
17 | def print_list(l):
|
18 | # type: (List[str]) -> None
|
19 | for s in l:
|
20 | print(s)
|
21 |
|
22 |
|
23 | def calls_collect():
|
24 | # type: () -> None
|
25 | mylib.MaybeCollect()
|
26 |
|
27 |
|
28 | def ignore_and_collect(l):
|
29 | # type: (List[str]) -> None
|
30 | mylib.MaybeCollect()
|
31 |
|
32 |
|
33 | def collect_and_return(l):
|
34 | # type: (List[str]) -> List[str]
|
35 | mylib.MaybeCollect()
|
36 | return l
|
37 |
|
38 |
|
39 | class ctx_Stasher(object):
|
40 |
|
41 | def __init__(self, l):
|
42 | # type: (List[str]) -> None
|
43 | self.l = l
|
44 |
|
45 | def __enter__(self):
|
46 | # type: () -> None
|
47 | pass
|
48 |
|
49 | def __exit__(self, type, value, traceback):
|
50 | # type: (Any, Any, Any) -> None
|
51 | print_list(self.l)
|
52 |
|
53 |
|
54 | """
|
55 | Test cases
|
56 | """
|
57 |
|
58 |
|
59 | def no_collect():
|
60 | # type: () -> None
|
61 | """
|
62 | There's no need to gernate any stack roots in this case. There is no threat
|
63 | of anything being swept.
|
64 | """
|
65 | l = ['no', 'collect'] # type: List[str]
|
66 | print_list(l)
|
67 |
|
68 |
|
69 | def simple_collect():
|
70 | # type: () -> None
|
71 | """
|
72 | Only l1 needs to be rooted here. l2 is not live after the call to collect.
|
73 | """
|
74 | l1 = ['foo', 'bar'] # type: List[str]
|
75 | l2 = ['bing', 'bong'] # type: List[str]
|
76 | print_list(l2)
|
77 | if len(l1):
|
78 | mylib.MaybeCollect()
|
79 |
|
80 | print_list(l1)
|
81 |
|
82 |
|
83 | def indirect_collect():
|
84 | # type: () -> None
|
85 | """
|
86 | l should be rooted since it is live after an indirect call to collect.
|
87 | """
|
88 | l = ['indirect', 'collect']
|
89 | calls_collect()
|
90 | print_list(l)
|
91 |
|
92 |
|
93 | def arg_roots():
|
94 | # type: () -> None
|
95 | """
|
96 | If a function might collect it should unconditionally root its arguments.
|
97 | It should root them even if it doesn't use them directly because we can't
|
98 | gaurantee that the caller will even have been able to root them, e.g. in the
|
99 | case of function composition or an arugment being constructed inline.
|
100 | """
|
101 | l1 = ['OK'] # Should be rooted by ignore_and_collect().
|
102 | ignore_and_collect(l1)
|
103 | print_list(l1)
|
104 |
|
105 | # The temporary list should be rooted by collect_and_return().
|
106 | l2 = collect_and_return(['not', 'swept'])
|
107 | print_list(l2)
|
108 |
|
109 |
|
110 | def alias():
|
111 | # type: () -> None
|
112 | """
|
113 | Only one of l1 and l2 needs to be rooted here. In this case we should choose
|
114 | l2 since it is live after the collector runs.
|
115 | """
|
116 | l1 = ['foo', 'bar'] # type: List[str]
|
117 | l2 = l1
|
118 | mylib.MaybeCollect()
|
119 | print_list(l2)
|
120 |
|
121 |
|
122 | def collect_scoped_resource():
|
123 | # type: () -> None
|
124 | """
|
125 | Similar to function arguments, members of context managers should be rooted
|
126 | by their constructors. However, unlike normal functions these constructors
|
127 | should do so even if they don't cause a collection. The caller might trigger
|
128 | garbage collection while the manager is still in scope and the members will
|
129 | get swept if they weren't already rooted further up in the call stack.
|
130 | """
|
131 | with ctx_Stasher(['context', 'member']) as ctx:
|
132 | mylib.MaybeCollect()
|
133 |
|
134 |
|
135 | def collect_in_loop():
|
136 | # type: () -> None
|
137 | """
|
138 | Temporary variables used in loops should be rooted if a collection might
|
139 | happen within the loop body.
|
140 | """
|
141 | for s in ['watch', 'out']:
|
142 | mylib.MaybeCollect()
|
143 | print(s)
|
144 |
|
145 |
|
146 | def run_tests():
|
147 | # type: () -> None
|
148 | no_collect()
|
149 | simple_collect()
|
150 | indirect_collect()
|
151 | arg_roots()
|
152 | alias()
|
153 | collect_scoped_resource()
|
154 | collect_in_loop()
|
155 |
|
156 |
|
157 | def run_benchmarks():
|
158 | # type: () -> None
|
159 | pass
|
160 |
|
161 |
|
162 | if __name__ == '__main__':
|
163 | if os.getenv('BENCHMARK'):
|
164 | pass
|
165 | else:
|
166 | run_tests()
|