/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 LEAKY_CORE_H |
4 | | #define LEAKY_CORE_H |
5 | | |
6 | | #include <pwd.h> // passwd |
7 | | #include <signal.h> // sighandler_t |
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(); |
39 | | Tuple2<int, int> Read(int fd, int n, List<Str*>* chunks); |
40 | | Tuple2<int, int> ReadByte(int fd); |
41 | | Str* ReadLine(); |
42 | | Dict<Str*, Str*>* Environ(); |
43 | | int Chdir(Str* dest_dir); |
44 | | Str* GetMyHomeDir(); |
45 | | Str* GetHomeDir(Str* user_name); |
46 | | |
47 | | class ReadError { |
48 | | public: |
49 | 1 | explicit ReadError(int err_num_) : header_(obj_header()), 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 | | GC_OBJ(header_); |
57 | | int err_num; |
58 | | }; |
59 | | |
60 | | class PasswdEntry { |
61 | | public: |
62 | | explicit PasswdEntry(const passwd* entry) |
63 | | : header_(obj_header()), |
64 | | pw_name(StrFromC(entry->pw_name)), |
65 | | pw_uid(entry->pw_uid), |
66 | 75 | pw_gid(entry->pw_gid) { |
67 | 75 | } |
68 | | |
69 | 75 | static constexpr ObjHeader obj_header() { |
70 | 75 | return ObjHeader::ClassFixed(field_mask(), sizeof(PasswdEntry)); |
71 | 75 | } |
72 | | |
73 | | GC_OBJ(header_); |
74 | | Str* pw_name; |
75 | | int pw_uid; |
76 | | int pw_gid; |
77 | | |
78 | 75 | static constexpr uint16_t field_mask() { |
79 | 75 | return maskbit(offsetof(PasswdEntry, pw_name)); |
80 | 75 | } |
81 | | }; |
82 | | |
83 | | List<PasswdEntry*>* GetAllUsers(); |
84 | | |
85 | | Str* GetUserName(int uid); |
86 | | |
87 | | Str* OsType(); |
88 | | |
89 | | Tuple3<double, double, double> Time(); |
90 | | |
91 | | void PrintTimes(); |
92 | | |
93 | | bool InputAvailable(int fd); |
94 | | |
95 | 1 | inline void FlushStdout() { |
96 | | // Flush libc buffers |
97 | 1 | fflush(stdout); |
98 | 1 | } |
99 | | |
100 | | class TermState { |
101 | | public: |
102 | 0 | TermState(int fd, int mask) { |
103 | 0 | assert(0); |
104 | 0 | } |
105 | 0 | void Restore() { |
106 | 0 | assert(0); |
107 | 0 | } |
108 | | }; |
109 | | |
110 | | // Make the signal queue slab 4096 bytes, including the GC header. See |
111 | | // cpp/core_test.cc. |
112 | | const int kMaxPendingSignals = 1022; |
113 | | |
114 | | class SignalSafe { |
115 | | // State that is shared between the main thread and signal handlers. |
116 | | public: |
117 | | SignalSafe() |
118 | | : header_(obj_header()), |
119 | | pending_signals_(AllocSignalList()), |
120 | | empty_list_(AllocSignalList()), // to avoid repeated allocation |
121 | | last_sig_num_(0), |
122 | | received_sigint_(false), |
123 | | received_sigwinch_(false), |
124 | | sigwinch_code_(UNTRAPPED_SIGWINCH), |
125 | 2 | num_dropped_(0) { |
126 | 2 | } |
127 | | |
128 | | // Called from signal handling context. Do not allocate. |
129 | 1.03k | void UpdateFromSignalHandler(int sig_num) { |
130 | 1.03k | if (pending_signals_->len_ < pending_signals_->capacity_) { |
131 | | // We can append without allocating |
132 | 1.02k | pending_signals_->append(sig_num); |
133 | 1.02k | } else { |
134 | | // Unlikely: we would have to allocate. Just increment a counter, which |
135 | | // we could expose somewhere in the UI. |
136 | 10 | num_dropped_++; |
137 | 10 | } |
138 | | |
139 | 1.03k | if (sig_num == SIGINT) { |
140 | 1.03k | received_sigint_ = true; |
141 | 1.03k | } |
142 | | |
143 | 1.03k | if (sig_num == SIGWINCH) { |
144 | 2 | received_sigwinch_ = true; |
145 | 2 | sig_num = sigwinch_code_; // mutate param |
146 | 2 | } |
147 | | |
148 | | #if LOCK_FREE_ATOMICS |
149 | | last_sig_num_.store(sig_num); |
150 | | #else |
151 | 1.03k | last_sig_num_ = sig_num; |
152 | 1.03k | #endif |
153 | 1.03k | } |
154 | | |
155 | | // Main thread takes signals so it can run traps. |
156 | 5 | List<int>* TakePendingSignals() { |
157 | 5 | List<int>* ret = pending_signals_; |
158 | | |
159 | | // Make sure we have a distinct list to reuse. |
160 | 5 | DCHECK(empty_list_ != pending_signals_); |
161 | 0 | pending_signals_ = empty_list_; |
162 | | |
163 | 5 | return ret; |
164 | 5 | } |
165 | | |
166 | | // Main thread returns the same list as an optimization to avoid allocation. |
167 | 3 | void ReuseEmptyList(List<int>* empty_list) { |
168 | 3 | DCHECK(empty_list != pending_signals_); // must be different |
169 | 3 | DCHECK(len(empty_list) == 0); // main thread clears |
170 | 3 | DCHECK(empty_list->capacity_ == kMaxPendingSignals); |
171 | | |
172 | 0 | empty_list_ = empty_list; |
173 | 3 | } |
174 | | |
175 | | // Main thread wants to get the last signal received. |
176 | 4 | int LastSignal() { |
177 | | #if LOCK_FREE_ATOMICS |
178 | | return last_sig_num_.load(); |
179 | | #else |
180 | 4 | return last_sig_num_; |
181 | 4 | #endif |
182 | 4 | } |
183 | | |
184 | | // Main thread wants to know if SIGINT was received since the last time |
185 | | // PollSigInt was called. |
186 | 0 | bool PollSigInt() { |
187 | 0 | bool result = received_sigint_; |
188 | 0 | received_sigint_ = false; |
189 | 0 | return result; |
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 | 2 | static constexpr uint16_t field_mask() { |
206 | 2 | return maskbit(offsetof(SignalSafe, pending_signals_)) | |
207 | 2 | maskbit(offsetof(SignalSafe, empty_list_)); |
208 | 2 | } |
209 | | |
210 | 2 | static constexpr ObjHeader obj_header() { |
211 | 2 | return ObjHeader::ClassFixed(field_mask(), sizeof(SignalSafe)); |
212 | 2 | } |
213 | | |
214 | | GC_OBJ(header_); |
215 | | List<int>* pending_signals_; // public for testing |
216 | | List<int>* empty_list_; |
217 | | |
218 | | private: |
219 | | // Enforce private state because two different "threads" will use it! |
220 | | |
221 | | // Reserve a fixed number of signals. |
222 | 4 | List<int>* AllocSignalList() { |
223 | 4 | List<int>* ret = NewList<int>(); |
224 | 4 | ret->reserve(kMaxPendingSignals); |
225 | 4 | return ret; |
226 | 4 | } |
227 | | |
228 | | #if LOCK_FREE_ATOMICS |
229 | | std::atomic<int> last_sig_num_; |
230 | | #else |
231 | | int last_sig_num_; |
232 | | #endif |
233 | | // Not sufficient: volatile sig_atomic_t last_sig_num_; |
234 | | |
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, sighandler_t handler); |
247 | | |
248 | | void RegisterSignalInterest(int sig_num); |
249 | | |
250 | | Tuple2<Str*, int>* MakeDirCacheKey(Str* path); |
251 | | |
252 | | } // namespace pyos |
253 | | |
254 | | namespace pyutil { |
255 | | |
256 | | bool IsValidCharEscape(Str* c); |
257 | | Str* ChArrayToString(List<int>* ch_array); |
258 | | |
259 | | class _ResourceLoader { |
260 | | public: |
261 | 1 | _ResourceLoader() : header_(obj_header()) { |
262 | 1 | } |
263 | | |
264 | | virtual Str* Get(Str* path); |
265 | | |
266 | 1 | static constexpr ObjHeader obj_header() { |
267 | 1 | return ObjHeader::ClassFixed(kZeroMask, sizeof(_ResourceLoader)); |
268 | 1 | } |
269 | | |
270 | | GC_OBJ(header_); |
271 | | }; |
272 | | |
273 | | _ResourceLoader* GetResourceLoader(); |
274 | | |
275 | | Str* GetVersion(_ResourceLoader* loader); |
276 | | |
277 | | void ShowAppVersion(_ResourceLoader* loader); |
278 | | |
279 | | Str* strerror(IOError_OSError* e); |
280 | | |
281 | | Str* BackslashEscape(Str* s, Str* meta_chars); |
282 | | |
283 | | grammar::Grammar* LoadOilGrammar(_ResourceLoader*); |
284 | | |
285 | | } // namespace pyutil |
286 | | |
287 | | #endif // LEAKY_CORE_H |