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

188 lines, 77 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)
160 for s in ['foo', 'bar']] # type: List[str]
161 for s in l:
162 print(s)
163
164
165def run_tests():
166 # type: () -> None
167 no_collect()
168 simple_collect()
169 indirect_collect()
170 arg_roots()
171 alias()
172 collect_scoped_resource()
173 # TODO: maybe move these two to invalid examples if we decide to disallow.
174 #collect_in_loop()
175 #collect_in_comprehension()
176
177
178def run_benchmarks():
179 # type: () -> None
180 pass
181
182
183if __name__ == '__main__':
184 if os.getenv('BENCHMARK'):
185 log('Benchmarking...')
186 run_benchmarks()
187 else:
188 run_tests()