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

166 lines, 68 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
10
11from typing import Any, List
12"""
13Helpers
14"""
15
16
17def print_list(l):
18 # type: (List[str]) -> None
19 for s in l:
20 print(s)
21
22
23def calls_collect():
24 # type: () -> None
25 mylib.MaybeCollect()
26
27
28def ignore_and_collect(l):
29 # type: (List[str]) -> None
30 mylib.MaybeCollect()
31
32
33def collect_and_return(l):
34 # type: (List[str]) -> List[str]
35 mylib.MaybeCollect()
36 return l
37
38
39class 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"""
55Test cases
56"""
57
58
59def 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
69def 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
83def 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
93def 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
110def 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
122def 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
135def 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
146def 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
157def run_benchmarks():
158 # type: () -> None
159 pass
160
161
162if __name__ == '__main__':
163 if os.getenv('BENCHMARK'):
164 pass
165 else:
166 run_tests()