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 | explicit ReadError(int err_num_) : err_num(err_num_) {
|
50 | }
|
51 |
|
52 | static constexpr ObjHeader obj_header() {
|
53 | return ObjHeader::ClassFixed(kZeroMask, sizeof(ReadError));
|
54 | }
|
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 | pw_gid(entry->pw_gid) {
|
65 | }
|
66 |
|
67 | static constexpr ObjHeader obj_header() {
|
68 | return ObjHeader::ClassFixed(field_mask(), sizeof(PasswdEntry));
|
69 | }
|
70 |
|
71 | BigStr* pw_name;
|
72 | int pw_uid;
|
73 | int pw_gid;
|
74 |
|
75 | static constexpr uint32_t field_mask() {
|
76 | return maskbit(offsetof(PasswdEntry, pw_name));
|
77 | }
|
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 | inline void FlushStdout() {
|
93 | // Flush libc buffers
|
94 | fflush(stdout);
|
95 | }
|
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 | num_dropped_(0) {
|
115 | }
|
116 |
|
117 | // Called from signal handling context. Do not allocate.
|
118 | void UpdateFromSignalHandler(int sig_num) {
|
119 | if (pending_signals_->len_ < pending_signals_->capacity_) {
|
120 | // We can append without allocating
|
121 | pending_signals_->append(sig_num);
|
122 | } else {
|
123 | // Unlikely: we would have to allocate. Just increment a counter, which
|
124 | // we could expose somewhere in the UI.
|
125 | num_dropped_++;
|
126 | }
|
127 |
|
128 | if (sig_num == SIGINT) {
|
129 | received_sigint_ = true;
|
130 | }
|
131 |
|
132 | if (sig_num == SIGWINCH) {
|
133 | received_sigwinch_ = true;
|
134 | sig_num = sigwinch_code_; // mutate param
|
135 | }
|
136 |
|
137 | #if LOCK_FREE_ATOMICS
|
138 | last_sig_num_.store(sig_num);
|
139 | #else
|
140 | last_sig_num_ = sig_num;
|
141 | #endif
|
142 | }
|
143 |
|
144 | // Main thread takes signals so it can run traps.
|
145 | List<int>* TakePendingSignals() {
|
146 | List<int>* ret = pending_signals_;
|
147 |
|
148 | // Make sure we have a distinct list to reuse.
|
149 | DCHECK(empty_list_ != pending_signals_);
|
150 | pending_signals_ = empty_list_;
|
151 |
|
152 | return ret;
|
153 | }
|
154 |
|
155 | // Main thread returns the same list as an optimization to avoid allocation.
|
156 | void ReuseEmptyList(List<int>* empty_list) {
|
157 | DCHECK(empty_list != pending_signals_); // must be different
|
158 | DCHECK(len(empty_list) == 0); // main thread clears
|
159 | DCHECK(empty_list->capacity_ == kMaxPendingSignals);
|
160 |
|
161 | empty_list_ = empty_list;
|
162 | }
|
163 |
|
164 | // Main thread wants to get the last signal received.
|
165 | int LastSignal() {
|
166 | #if LOCK_FREE_ATOMICS
|
167 | return last_sig_num_.load();
|
168 | #else
|
169 | return last_sig_num_;
|
170 | #endif
|
171 | }
|
172 |
|
173 | // Main thread wants to know if SIGINT was received since the last time
|
174 | // PollSigInt was called.
|
175 | bool PollSigInt() {
|
176 | bool result = received_sigint_;
|
177 | received_sigint_ = false;
|
178 | return result;
|
179 | }
|
180 |
|
181 | // Main thread tells us whether SIGWINCH is trapped.
|
182 | void SetSigWinchCode(int code) {
|
183 | sigwinch_code_ = code;
|
184 | }
|
185 |
|
186 | // Main thread wants to know if SIGWINCH was received since the last time
|
187 | // PollSigWinch was called.
|
188 | bool PollSigWinch() {
|
189 | bool result = received_sigwinch_;
|
190 | received_sigwinch_ = false;
|
191 | return result;
|
192 | }
|
193 |
|
194 | static constexpr uint32_t field_mask() {
|
195 | return maskbit(offsetof(SignalSafe, pending_signals_)) |
|
196 | maskbit(offsetof(SignalSafe, empty_list_));
|
197 | }
|
198 |
|
199 | static constexpr ObjHeader obj_header() {
|
200 | return ObjHeader::ClassFixed(field_mask(), sizeof(SignalSafe));
|
201 | }
|
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 | List<int>* AllocSignalList() {
|
211 | List<int>* ret = NewList<int>();
|
212 | ret->reserve(kMaxPendingSignals);
|
213 | return ret;
|
214 | }
|
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 | _ResourceLoader() {
|
250 | }
|
251 |
|
252 | virtual BigStr* Get(BigStr* path);
|
253 |
|
254 | static constexpr ObjHeader obj_header() {
|
255 | return ObjHeader::ClassFixed(kZeroMask, sizeof(_ResourceLoader));
|
256 | }
|
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
|