OILS / mycpp / examples / gc_stack_roots.py View on Github | oilshell.org

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