/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 | 76 | pw_gid(entry->pw_gid) { |
65 | 76 | } |
66 | | |
67 | 76 | static constexpr ObjHeader obj_header() { |
68 | 76 | return ObjHeader::ClassFixed(field_mask(), sizeof(PasswdEntry)); |
69 | 76 | } |
70 | | |
71 | | BigStr* pw_name; |
72 | | int pw_uid; |
73 | | int pw_gid; |
74 | | |
75 | 76 | static constexpr uint32_t field_mask() { |
76 | 76 | return maskbit(offsetof(PasswdEntry, pw_name)); |
77 | 76 | } |
78 | | }; |
79 | | |
80 | | List<PasswdEntry*>* GetAllUsers(); |
81 | | |
82 | | BigStr* GetUserName(int uid); |
83 | | |
84 | | BigStr* OsType(); |
85 | | |
86 | | Tuple3<double, double, double> Time(); |
87 | | |
88 | | void PrintTimes(); |
89 | | |
90 | | bool InputAvailable(int fd); |
91 | | |
92 | 1 | inline void FlushStdout() { |
93 | | // Flush libc buffers |
94 | 1 | fflush(stdout); |
95 | 1 | } |
96 | | |
97 | | Tuple2<int, void*> PushTermAttrs(int fd, int mask); |
98 | | void PopTermAttrs(int fd, int orig_local_modes, void* term_attrs); |
99 | | |
100 | | // Make the signal queue slab 4096 bytes, including the GC header. See |
101 | | // cpp/core_test.cc. |
102 | | const int kMaxPendingSignals = 1022; |
103 | | |
104 | | class SignalSafe { |
105 | | // State that is shared between the main thread and signal handlers. |
106 | | public: |
107 | | SignalSafe() |
108 | | : pending_signals_(AllocSignalList()), |
109 | | empty_list_(AllocSignalList()), // to avoid repeated allocation |
110 | | last_sig_num_(0), |
111 | | received_sigint_(false), |
112 | | received_sigwinch_(false), |
113 | | sigwinch_code_(UNTRAPPED_SIGWINCH), |
114 | 2 | num_dropped_(0) { |
115 | 2 | } |
116 | | |
117 | | // Called from signal handling context. Do not allocate. |
118 | 1.03k | void UpdateFromSignalHandler(int sig_num) { |
119 | 1.03k | if (pending_signals_->len_ < pending_signals_->capacity_) { |
120 | | // We can append without allocating |
121 | 1.02k | pending_signals_->append(sig_num); |
122 | 1.02k | } else { |
123 | | // Unlikely: we would have to allocate. Just increment a counter, which |
124 | | // we could expose somewhere in the UI. |
125 | 10 | num_dropped_++; |
126 | 10 | } |
127 | | |
128 | 1.03k | if (sig_num == SIGINT) { |
129 | 1.03k | received_sigint_ = true; |
130 | 1.03k | } |
131 | | |
132 | 1.03k | if (sig_num == SIGWINCH) { |
133 | 2 | received_sigwinch_ = true; |
134 | 2 | sig_num = sigwinch_code_; // mutate param |
135 | 2 | } |
136 | | |
137 | | #if LOCK_FREE_ATOMICS |
138 | | last_sig_num_.store(sig_num); |
139 | | #else |
140 | 1.03k | last_sig_num_ = sig_num; |
141 | 1.03k | #endif |
142 | 1.03k | } |
143 | | |
144 | | // Main thread takes signals so it can run traps. |
145 | 5 | List<int>* TakePendingSignals() { |
146 | 5 | List<int>* ret = pending_signals_; |
147 | | |
148 | | // Make sure we have a distinct list to reuse. |
149 | 5 | DCHECK(empty_list_ != pending_signals_); |
150 | 0 | pending_signals_ = empty_list_; |
151 | | |
152 | 5 | return ret; |
153 | 5 | } |
154 | | |
155 | | // Main thread returns the same list as an optimization to avoid allocation. |
156 | 3 | void ReuseEmptyList(List<int>* empty_list) { |
157 | 3 | DCHECK(empty_list != pending_signals_); // must be different |
158 | 3 | DCHECK(len(empty_list) == 0); // main thread clears |
159 | 3 | DCHECK(empty_list->capacity_ == kMaxPendingSignals); |
160 | | |
161 | 0 | empty_list_ = empty_list; |
162 | 3 | } |
163 | | |
164 | | // Main thread wants to get the last signal received. |
165 | 4 | int LastSignal() { |
166 | | #if LOCK_FREE_ATOMICS |
167 | | return last_sig_num_.load(); |
168 | | #else |
169 | 4 | return last_sig_num_; |
170 | 4 | #endif |
171 | 4 | } |
172 | | |
173 | | // Main thread wants to know if SIGINT was received since the last time |
174 | | // PollSigInt was called. |
175 | 0 | bool PollSigInt() { |
176 | 0 | bool result = received_sigint_; |
177 | 0 | received_sigint_ = false; |
178 | 0 | return result; |
179 | 0 | } |
180 | | |
181 | | // Main thread tells us whether SIGWINCH is trapped. |
182 | 1 | void SetSigWinchCode(int code) { |
183 | 1 | sigwinch_code_ = code; |
184 | 1 | } |
185 | | |
186 | | // Main thread wants to know if SIGWINCH was received since the last time |
187 | | // PollSigWinch was called. |
188 | 0 | bool PollSigWinch() { |
189 | 0 | bool result = received_sigwinch_; |
190 | 0 | received_sigwinch_ = false; |
191 | 0 | return result; |
192 | 0 | } |
193 | | |
194 | 1 | static constexpr uint32_t field_mask() { |
195 | 1 | return maskbit(offsetof(SignalSafe, pending_signals_)) | |
196 | 1 | maskbit(offsetof(SignalSafe, empty_list_)); |
197 | 1 | } |
198 | | |
199 | 1 | static constexpr ObjHeader obj_header() { |
200 | 1 | return ObjHeader::ClassFixed(field_mask(), sizeof(SignalSafe)); |
201 | 1 | } |
202 | | |
203 | | List<int>* pending_signals_; // public for testing |
204 | | List<int>* empty_list_; |
205 | | |
206 | | private: |
207 | | // Enforce private state because two different "threads" will use it! |
208 | | |
209 | | // Reserve a fixed number of signals. |
210 | 4 | List<int>* AllocSignalList() { |
211 | 4 | List<int>* ret = NewList<int>(); |
212 | 4 | ret->reserve(kMaxPendingSignals); |
213 | 4 | return ret; |
214 | 4 | } |
215 | | |
216 | | #if LOCK_FREE_ATOMICS |
217 | | std::atomic<int> last_sig_num_; |
218 | | #else |
219 | | int last_sig_num_; |
220 | | #endif |
221 | | // Not sufficient: volatile sig_atomic_t last_sig_num_; |
222 | | |
223 | | int received_sigint_; |
224 | | int received_sigwinch_; |
225 | | int sigwinch_code_; |
226 | | int num_dropped_; |
227 | | }; |
228 | | |
229 | | extern SignalSafe* gSignalSafe; |
230 | | |
231 | | // Allocate global and return it. |
232 | | SignalSafe* InitSignalSafe(); |
233 | | |
234 | | void Sigaction(int sig_num, void (*handler)(int)); |
235 | | |
236 | | void RegisterSignalInterest(int sig_num); |
237 | | |
238 | | Tuple2<BigStr*, int>* MakeDirCacheKey(BigStr* path); |
239 | | |
240 | | } // namespace pyos |
241 | | |
242 | | namespace pyutil { |
243 | | |
244 | | bool IsValidCharEscape(BigStr* c); |
245 | | BigStr* ChArrayToString(List<int>* ch_array); |
246 | | |
247 | | class _ResourceLoader { |
248 | | public: |
249 | 1 | _ResourceLoader() { |
250 | 1 | } |
251 | | |
252 | | virtual BigStr* Get(BigStr* path); |
253 | | |
254 | 1 | static constexpr ObjHeader obj_header() { |
255 | 1 | return ObjHeader::ClassFixed(kZeroMask, sizeof(_ResourceLoader)); |
256 | 1 | } |
257 | | }; |
258 | | |
259 | | _ResourceLoader* GetResourceLoader(); |
260 | | |
261 | | BigStr* GetVersion(_ResourceLoader* loader); |
262 | | |
263 | | void PrintVersionDetails(_ResourceLoader* loader); |
264 | | |
265 | | BigStr* strerror(IOError_OSError* e); |
266 | | |
267 | | BigStr* BackslashEscape(BigStr* s, BigStr* meta_chars); |
268 | | |
269 | | grammar::Grammar* LoadYshGrammar(_ResourceLoader*); |
270 | | |
271 | | } // namespace pyutil |
272 | | |
273 | | #endif // CORE_H |