/home/andy/git/oilshell/oil/cpp/core.h
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 | 36 | pw_gid(entry->pw_gid) { |
65 | 36 | } |
66 | | |
67 | 36 | static constexpr ObjHeader obj_header() { |
68 | 36 | return ObjHeader::ClassFixed(field_mask(), sizeof(PasswdEntry)); |
69 | 36 | } |
70 | | |
71 | | BigStr* pw_name; |
72 | | int pw_uid; |
73 | | int pw_gid; |
74 | | |
75 | 36 | static constexpr uint32_t field_mask() { |
76 | 36 | return maskbit(offsetof(PasswdEntry, pw_name)); |
77 | 36 | } |
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 | 0 | void SetSigIntTrapped(bool b) { |
175 | 0 | sigint_trapped_ = b; |
176 | 0 | } |
177 | | |
178 | | // Used by pyos.WaitPid, Read, ReadByte. |
179 | 0 | bool PollSigInt() { |
180 | 0 | bool result = received_sigint_; |
181 | 0 | received_sigint_ = false; |
182 | 0 | return result; |
183 | 0 | } |
184 | | |
185 | | // Used by osh/cmd_eval.py. Main loop wants to know if SIGINT was received |
186 | | // since the last time PollSigInt was called. |
187 | 0 | bool PollUntrappedSigInt() { |
188 | 0 | bool received = PollSigInt(); // clears a flag |
189 | 0 | return received && !sigint_trapped_; |
190 | 0 | } |
191 | | |
192 | | // Main thread tells us whether SIGWINCH is trapped. |
193 | 1 | void SetSigWinchCode(int code) { |
194 | 1 | sigwinch_code_ = code; |
195 | 1 | } |
196 | | |
197 | | // Main thread wants to know if SIGWINCH was received since the last time |
198 | | // PollSigWinch was called. |
199 | 0 | bool PollSigWinch() { |
200 | 0 | bool result = received_sigwinch_; |
201 | 0 | received_sigwinch_ = false; |
202 | 0 | return result; |
203 | 0 | } |
204 | | |
205 | 1 | static constexpr uint32_t field_mask() { |
206 | 1 | return maskbit(offsetof(SignalSafe, pending_signals_)) | |
207 | 1 | maskbit(offsetof(SignalSafe, empty_list_)); |
208 | 1 | } |
209 | | |
210 | 1 | static constexpr ObjHeader obj_header() { |
211 | 1 | return ObjHeader::ClassFixed(field_mask(), sizeof(SignalSafe)); |
212 | 1 | } |
213 | | |
214 | | List<int>* pending_signals_; // public for testing |
215 | | List<int>* empty_list_; |
216 | | |
217 | | private: |
218 | | // Enforce private state because two different "threads" will use it! |
219 | | |
220 | | // Reserve a fixed number of signals. |
221 | 4 | List<int>* AllocSignalList() { |
222 | 4 | List<int>* ret = NewList<int>(); |
223 | 4 | ret->reserve(kMaxPendingSignals); |
224 | 4 | return ret; |
225 | 4 | } |
226 | | |
227 | | #if LOCK_FREE_ATOMICS |
228 | | std::atomic<int> last_sig_num_; |
229 | | #else |
230 | | int last_sig_num_; |
231 | | #endif |
232 | | // Not sufficient: volatile sig_atomic_t last_sig_num_; |
233 | | |
234 | | bool sigint_trapped_; |
235 | | int received_sigint_; |
236 | | int received_sigwinch_; |
237 | | int sigwinch_code_; |
238 | | int num_dropped_; |
239 | | }; |
240 | | |
241 | | extern SignalSafe* gSignalSafe; |
242 | | |
243 | | // Allocate global and return it. |
244 | | SignalSafe* InitSignalSafe(); |
245 | | |
246 | | void sigaction(int sig_num, void (*handler)(int)); |
247 | | |
248 | | void RegisterSignalInterest(int sig_num); |
249 | | |
250 | | Tuple2<BigStr*, int>* MakeDirCacheKey(BigStr* path); |
251 | | |
252 | | } // namespace pyos |
253 | | |
254 | | namespace pyutil { |
255 | | |
256 | | double infinity(); |
257 | | double nan(); |
258 | | |
259 | | bool IsValidCharEscape(BigStr* c); |
260 | | BigStr* ChArrayToString(List<int>* ch_array); |
261 | | |
262 | | class _ResourceLoader { |
263 | | public: |
264 | 1 | _ResourceLoader() { |
265 | 1 | } |
266 | | |
267 | | virtual BigStr* Get(BigStr* path); |
268 | | |
269 | 1 | static constexpr ObjHeader obj_header() { |
270 | 1 | return ObjHeader::ClassFixed(kZeroMask, sizeof(_ResourceLoader)); |
271 | 1 | } |
272 | | }; |
273 | | |
274 | | _ResourceLoader* GetResourceLoader(); |
275 | | |
276 | | BigStr* GetVersion(_ResourceLoader* loader); |
277 | | |
278 | | void PrintVersionDetails(_ResourceLoader* loader); |
279 | | |
280 | | BigStr* strerror(IOError_OSError* e); |
281 | | |
282 | | BigStr* BackslashEscape(BigStr* s, BigStr* meta_chars); |
283 | | |
284 | | grammar::Grammar* LoadYshGrammar(_ResourceLoader*); |
285 | | |
286 | | } // namespace pyutil |
287 | | |
288 | | #endif // CORE_H |