Line | Count | Source (jump to first uncovered line) |
1 | | // core.h: Replacement for core/*.py |
2 | | |
3 | | #ifndef CORE_H |
4 | | #define CORE_H |
5 | | |
6 | | #include <pwd.h> // passwd |
7 | | #include <signal.h> |
8 | | #include <termios.h> |
9 | | |
10 | | // For now, we assume that simple int and pointer operations are atomic, rather |
11 | | // than using std::atomic. Could be a ./configure option later. |
12 | | // |
13 | | // See doc/portability.md. |
14 | | |
15 | | #define LOCK_FREE_ATOMICS 0 |
16 | | |
17 | | #if LOCK_FREE_ATOMICS |
18 | | #include <atomic> |
19 | | #endif |
20 | | |
21 | | #include "_gen/frontend/syntax.asdl.h" |
22 | | #include "cpp/pgen2.h" |
23 | | #include "mycpp/runtime.h" |
24 | | |
25 | | // Hacky forward declaration |
26 | | namespace completion { |
27 | | class RootCompleter; |
28 | | }; |
29 | | |
30 | | namespace pyos { |
31 | | |
32 | | const int TERM_ICANON = ICANON; |
33 | | const int TERM_ECHO = ECHO; |
34 | | const int EOF_SENTINEL = 256; |
35 | | const int NEWLINE_CH = 10; |
36 | | const int UNTRAPPED_SIGWINCH = -1; |
37 | | |
38 | | Tuple2<int, int> WaitPid(int waitpid_options); |
39 | | Tuple2<int, int> Read(int fd, int n, List<BigStr*>* chunks); |
40 | | Tuple2<int, int> ReadByte(int fd); |
41 | | BigStr* ReadLineBuffered(); |
42 | | Dict<BigStr*, BigStr*>* Environ(); |
43 | | int Chdir(BigStr* dest_dir); |
44 | | BigStr* GetMyHomeDir(); |
45 | | BigStr* GetHomeDir(BigStr* user_name); |
46 | | |
47 | | class ReadError { |
48 | | public: |
49 | 1 | explicit ReadError(int err_num_) : err_num(err_num_) { |
50 | 1 | } |
51 | | |
52 | 1 | static constexpr ObjHeader obj_header() { |
53 | 1 | return ObjHeader::ClassFixed(kZeroMask, sizeof(ReadError)); |
54 | 1 | } |
55 | | |
56 | | int err_num; |
57 | | }; |
58 | | |
59 | | class PasswdEntry { |
60 | | public: |
61 | | explicit PasswdEntry(const passwd* entry) |
62 | | : pw_name(StrFromC(entry->pw_name)), |
63 | | pw_uid(entry->pw_uid), |
64 | 20 | pw_gid(entry->pw_gid) { |
65 | 20 | } |
66 | | |
67 | 20 | static constexpr ObjHeader obj_header() { |
68 | 20 | return ObjHeader::ClassFixed(field_mask(), sizeof(PasswdEntry)); |
69 | 20 | } |
70 | | |
71 | | BigStr* pw_name; |
72 | | int pw_uid; |
73 | | int pw_gid; |
74 | | |
75 | 20 | static constexpr uint32_t field_mask() { |
76 | 20 | return maskbit(offsetof(PasswdEntry, pw_name)); |
77 | 20 | } |
78 | | }; |
79 | | |
80 | | List<PasswdEntry*>* GetAllUsers(); |
81 | | |
82 | | BigStr* GetUserName(int uid); |
83 | | |
84 | | BigStr* OsType(); |
85 | | |
86 | | Tuple2<mops::BigInt, mops::BigInt> GetRLimit(int resource); |
87 | | |
88 | | void SetRLimit(int resource, mops::BigInt soft, mops::BigInt hard); |
89 | | |
90 | | Tuple3<double, double, double> Time(); |
91 | | |
92 | | void PrintTimes(); |
93 | | |
94 | | bool InputAvailable(int fd); |
95 | | |
96 | | IOError_OSError* FlushStdout(); |
97 | | |
98 | | Tuple2<int, void*> PushTermAttrs(int fd, int mask); |
99 | | void PopTermAttrs(int fd, int orig_local_modes, void* term_attrs); |
100 | | |
101 | | // Make the signal queue slab 4096 bytes, including the GC header. See |
102 | | // cpp/core_test.cc. |
103 | | const int kMaxPendingSignals = 1022; |
104 | | |
105 | | class SignalSafe { |
106 | | // State that is shared between the main thread and signal handlers. |
107 | | public: |
108 | | SignalSafe() |
109 | | : pending_signals_(AllocSignalList()), |
110 | | empty_list_(AllocSignalList()), // to avoid repeated allocation |
111 | | last_sig_num_(0), |
112 | | received_sigint_(false), |
113 | | received_sigwinch_(false), |
114 | | sigwinch_code_(UNTRAPPED_SIGWINCH), |
115 | 2 | num_dropped_(0) { |
116 | 2 | } |
117 | | |
118 | | // Called from signal handling context. Do not allocate. |
119 | 1.03k | void UpdateFromSignalHandler(int sig_num) { |
120 | 1.03k | if (pending_signals_->len_ < pending_signals_->capacity_) { |
121 | | // We can append without allocating |
122 | 1.02k | pending_signals_->append(sig_num); |
123 | 1.02k | } else { |
124 | | // Unlikely: we would have to allocate. Just increment a counter, which |
125 | | // we could expose somewhere in the UI. |
126 | 10 | num_dropped_++; |
127 | 10 | } |
128 | | |
129 | 1.03k | if (sig_num == SIGINT) { |
130 | 1.03k | received_sigint_ = true; |
131 | 1.03k | } |
132 | | |
133 | 1.03k | if (sig_num == SIGWINCH) { |
134 | 2 | received_sigwinch_ = true; |
135 | 2 | sig_num = sigwinch_code_; // mutate param |
136 | 2 | } |
137 | | |
138 | | #if LOCK_FREE_ATOMICS |
139 | | last_sig_num_.store(sig_num); |
140 | | #else |
141 | 1.03k | last_sig_num_ = sig_num; |
142 | 1.03k | #endif |
143 | 1.03k | } |
144 | | |
145 | | // Main thread takes signals so it can run traps. |
146 | 5 | List<int>* TakePendingSignals() { |
147 | 5 | List<int>* ret = pending_signals_; |
148 | | |
149 | | // Make sure we have a distinct list to reuse. |
150 | 5 | DCHECK(empty_list_ != pending_signals_); |
151 | 0 | pending_signals_ = empty_list_; |
152 | | |
153 | 5 | return ret; |
154 | 5 | } |
155 | | |
156 | | // Main thread returns the same list as an optimization to avoid allocation. |
157 | 3 | void ReuseEmptyList(List<int>* empty_list) { |
158 | 3 | DCHECK(empty_list != pending_signals_); // must be different |
159 | 3 | DCHECK(len(empty_list) == 0); // main thread clears |
160 | 3 | DCHECK(empty_list->capacity_ == kMaxPendingSignals); |
161 | | |
162 | 0 | empty_list_ = empty_list; |
163 | 3 | } |
164 | | |
165 | | // Main thread wants to get the last signal received. |
166 | 4 | int LastSignal() { |
167 | | #if LOCK_FREE_ATOMICS |
168 | | return last_sig_num_.load(); |
169 | | #else |
170 | 4 | return last_sig_num_; |
171 | 4 | #endif |
172 | 4 | } |
173 | | |
174 | | // Main thread wants to know if SIGINT was received since the last time |
175 | | // PollSigInt was called. |
176 | 0 | bool PollSigInt() { |
177 | 0 | bool result = received_sigint_; |
178 | 0 | received_sigint_ = false; |
179 | 0 | return result; |
180 | 0 | } |
181 | | |
182 | | // Main thread tells us whether SIGWINCH is trapped. |
183 | 1 | void SetSigWinchCode(int code) { |
184 | 1 | sigwinch_code_ = code; |
185 | 1 | } |
186 | | |
187 | | // Main thread wants to know if SIGWINCH was received since the last time |
188 | | // PollSigWinch was called. |
189 | 0 | bool PollSigWinch() { |
190 | 0 | bool result = received_sigwinch_; |
191 | 0 | received_sigwinch_ = false; |
192 | 0 | return result; |
193 | 0 | } |
194 | | |
195 | 1 | static constexpr uint32_t field_mask() { |
196 | 1 | return maskbit(offsetof(SignalSafe, pending_signals_)) | |
197 | 1 | maskbit(offsetof(SignalSafe, empty_list_)); |
198 | 1 | } |
199 | | |
200 | 1 | static constexpr ObjHeader obj_header() { |
201 | 1 | return ObjHeader::ClassFixed(field_mask(), sizeof(SignalSafe)); |
202 | 1 | } |
203 | | |
204 | | List<int>* pending_signals_; // public for testing |
205 | | List<int>* empty_list_; |
206 | | |
207 | | private: |
208 | | // Enforce private state because two different "threads" will use it! |
209 | | |
210 | | // Reserve a fixed number of signals. |
211 | 4 | List<int>* AllocSignalList() { |
212 | 4 | List<int>* ret = NewList<int>(); |
213 | 4 | ret->reserve(kMaxPendingSignals); |
214 | 4 | return ret; |
215 | 4 | } |
216 | | |
217 | | #if LOCK_FREE_ATOMICS |
218 | | std::atomic<int> last_sig_num_; |
219 | | #else |
220 | | int last_sig_num_; |
221 | | #endif |
222 | | // Not sufficient: volatile sig_atomic_t last_sig_num_; |
223 | | |
224 | | int received_sigint_; |
225 | | int received_sigwinch_; |
226 | | int sigwinch_code_; |
227 | | int num_dropped_; |
228 | | }; |
229 | | |
230 | | extern SignalSafe* gSignalSafe; |
231 | | |
232 | | // Allocate global and return it. |
233 | | SignalSafe* InitSignalSafe(); |
234 | | |
235 | | void Sigaction(int sig_num, void (*handler)(int)); |
236 | | |
237 | | void RegisterSignalInterest(int sig_num); |
238 | | |
239 | | Tuple2<BigStr*, int>* MakeDirCacheKey(BigStr* path); |
240 | | |
241 | | } // namespace pyos |
242 | | |
243 | | namespace pyutil { |
244 | | |
245 | | double infinity(); |
246 | | double nan(); |
247 | | |
248 | | bool IsValidCharEscape(BigStr* c); |
249 | | BigStr* ChArrayToString(List<int>* ch_array); |
250 | | |
251 | | class _ResourceLoader { |
252 | | public: |
253 | 1 | _ResourceLoader() { |
254 | 1 | } |
255 | | |
256 | | virtual BigStr* Get(BigStr* path); |
257 | | |
258 | 1 | static constexpr ObjHeader obj_header() { |
259 | 1 | return ObjHeader::ClassFixed(kZeroMask, sizeof(_ResourceLoader)); |
260 | 1 | } |
261 | | }; |
262 | | |
263 | | _ResourceLoader* GetResourceLoader(); |
264 | | |
265 | | BigStr* GetVersion(_ResourceLoader* loader); |
266 | | |
267 | | void PrintVersionDetails(_ResourceLoader* loader); |
268 | | |
269 | | BigStr* strerror(IOError_OSError* e); |
270 | | |
271 | | BigStr* BackslashEscape(BigStr* s, BigStr* meta_chars); |
272 | | |
273 | | grammar::Grammar* LoadYshGrammar(_ResourceLoader*); |
274 | | |
275 | | } // namespace pyutil |
276 | | |
277 | | #endif // CORE_H |