OILS / mycpp / iolib.py View on Github | oilshell.org

165 lines, 63 significant
1"""
2mylib.py: Python stubs/interfaces that are reimplemented in C++, not directly
3translated.
4"""
5from __future__ import print_function
6
7import signal
8
9from typing import List, Any
10
11UNTRAPPED_SIGWINCH = -1
12
13
14class SignalSafe(object):
15 """State that is shared between the main thread and signal handlers.
16
17 See C++ implementation in cpp/core.h
18 """
19
20 def __init__(self):
21 # type: () -> None
22 self.pending_signals = [] # type: List[int]
23 self.last_sig_num = 0 # type: int
24 self.sigint_trapped = False
25 self.received_sigint = False
26 self.received_sigwinch = False
27 self.sigwinch_code = UNTRAPPED_SIGWINCH
28
29 def UpdateFromSignalHandler(self, sig_num, unused_frame):
30 # type: (int, Any) -> None
31 """Receive the given signal, and update shared state.
32
33 This method is registered as a Python signal handler.
34 """
35 self.pending_signals.append(sig_num)
36
37 if sig_num == signal.SIGINT:
38 self.received_sigint = True
39
40 if sig_num == signal.SIGWINCH:
41 self.received_sigwinch = True
42 sig_num = self.sigwinch_code # mutate param
43
44 self.last_sig_num = sig_num
45
46 def LastSignal(self):
47 # type: () -> int
48 """Return the number of the last signal received."""
49 return self.last_sig_num
50
51 def PollSigInt(self):
52 # type: () -> bool
53 """Has SIGINT received since the last time PollSigInt() was called?"""
54 result = self.received_sigint
55 self.received_sigint = False
56 return result
57
58 def PollUntrappedSigInt(self):
59 # type: () -> bool
60 """Has SIGINT received since the last time PollSigInt() was called?"""
61 received = self.PollSigInt()
62 return received and not self.sigint_trapped
63
64 if 0:
65
66 def SigIntTrapped(self):
67 # type: () -> bool
68 return self.sigint_trapped
69
70 def SetSigIntTrapped(self, b):
71 # type: (bool) -> None
72 """Set a flag to tell us whether sigint is trapped by the user."""
73 self.sigint_trapped = b
74
75 def SetSigWinchCode(self, code):
76 # type: (int) -> None
77 """Depending on whether or not SIGWINCH is trapped by a user, it is
78 expected to report a different code to `wait`.
79
80 SetSigWinchCode() lets us set which code is reported.
81 """
82 self.sigwinch_code = code
83
84 def PollSigWinch(self):
85 # type: () -> bool
86 """Has SIGWINCH been received since the last time PollSigWinch() was
87 called?"""
88 result = self.received_sigwinch
89 self.received_sigwinch = False
90 return result
91
92 def TakePendingSignals(self):
93 # type: () -> List[int]
94 """Transfer ownership of queue of pending signals to caller."""
95
96 # A note on signal-safety here. The main loop might be calling this function
97 # at the same time a signal is firing and appending to
98 # `self.pending_signals`. We can forgoe using a lock here
99 # (which would be problematic for the signal handler) because mutual
100 # exclusivity should be maintained by the atomic nature of pointer
101 # assignment (i.e. word-sized writes) on most modern platforms.
102 # The replacement run list is allocated before the swap, so it can be
103 # interrupted at any point without consequence.
104 # This means the signal handler always has exclusive access to
105 # `self.pending_signals`. In the worst case the signal handler might write to
106 # `new_queue` and the corresponding trap handler won't get executed
107 # until the main loop calls this function again.
108 # NOTE: It's important to distinguish between signal-safety an
109 # thread-safety here. Signals run in the same process context as the main
110 # loop, while concurrent threads do not and would have to worry about
111 # cache-coherence and instruction reordering.
112 new_queue = [] # type: List[int]
113 ret = self.pending_signals
114 self.pending_signals = new_queue
115 return ret
116
117 def ReuseEmptyList(self, empty_list):
118 # type: (List[int]) -> None
119 """This optimization only happens in C++."""
120 pass
121
122
123gSignalSafe = None # type: SignalSafe
124
125gOrigSigIntHandler = None # type: Any
126
127
128def InitSignalSafe():
129 # type: () -> SignalSafe
130 """Set global instance so the signal handler can access it."""
131 global gSignalSafe
132 gSignalSafe = SignalSafe()
133
134 # See
135 # - demo/cpython/keyboard_interrupt.py
136 # - pyos::InitSignalSafe()
137
138 # In C++, we do
139 # RegisterSignalInterest(signal.SIGINT)
140
141 global gOrigSigIntHandler
142 gOrigSigIntHandler = signal.signal(signal.SIGINT,
143 gSignalSafe.UpdateFromSignalHandler)
144
145 return gSignalSafe
146
147
148def RegisterSignalInterest(sig_num):
149 # type: (int) -> None
150 """Have the kernel notify the main loop about the given signal."""
151 #log('RegisterSignalInterest %d', sig_num)
152
153 assert gSignalSafe is not None
154 signal.signal(sig_num, gSignalSafe.UpdateFromSignalHandler)
155
156
157def sigaction(sig_num, handler):
158 # type: (int, Any) -> None
159 """
160 Handle a signal with SIG_DFL or SIG_IGN, not our own signal handler.
161 """
162 # SIGINT and SIGWINCH must be registered through SignalSafe
163 assert sig_num != signal.SIGINT
164 assert sig_num != signal.SIGWINCH
165 signal.signal(sig_num, handler)