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 | 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 | num_dropped_(0) {
|
116 | }
|
117 |
|
118 | // Called from signal handling context. Do not allocate.
|
119 | void UpdateFromSignalHandler(int sig_num) {
|
120 | if (pending_signals_->len_ < pending_signals_->capacity_) {
|
121 | // We can append without allocating
|
122 | pending_signals_->append(sig_num);
|
123 | } else {
|
124 | // Unlikely: we would have to allocate. Just increment a counter, which
|
125 | // we could expose somewhere in the UI.
|
126 | num_dropped_++;
|
127 | }
|
128 |
|
129 | if (sig_num == SIGINT) {
|
130 | received_sigint_ = true;
|
131 | }
|
132 |
|
133 | if (sig_num == SIGWINCH) {
|
134 | received_sigwinch_ = true;
|
135 | sig_num = sigwinch_code_; // mutate param
|
136 | }
|
137 |
|
138 | #if LOCK_FREE_ATOMICS
|
139 | last_sig_num_.store(sig_num);
|
140 | #else
|
141 | last_sig_num_ = sig_num;
|
142 | #endif
|
143 | }
|
144 |
|
145 | // Main thread takes signals so it can run traps.
|
146 | List<int>* TakePendingSignals() {
|
147 | List<int>* ret = pending_signals_;
|
148 |
|
149 | // Make sure we have a distinct list to reuse.
|
150 | DCHECK(empty_list_ != pending_signals_);
|
151 | pending_signals_ = empty_list_;
|
152 |
|
153 | return ret;
|
154 | }
|
155 |
|
156 | // Main thread returns the same list as an optimization to avoid allocation.
|
157 | void ReuseEmptyList(List<int>* empty_list) {
|
158 | DCHECK(empty_list != pending_signals_); // must be different
|
159 | DCHECK(len(empty_list) == 0); // main thread clears
|
160 | DCHECK(empty_list->capacity_ == kMaxPendingSignals);
|
161 |
|
162 | empty_list_ = empty_list;
|
163 | }
|
164 |
|
165 | // Main thread wants to get the last signal received.
|
166 | int LastSignal() {
|
167 | #if LOCK_FREE_ATOMICS
|
168 | return last_sig_num_.load();
|
169 | #else
|
170 | return last_sig_num_;
|
171 | #endif
|
172 | }
|
173 |
|
174 | // Main thread wants to know if SIGINT was received since the last time
|
175 | // PollSigInt was called.
|
176 | bool PollSigInt() {
|
177 | bool result = received_sigint_;
|
178 | received_sigint_ = false;
|
179 | return result;
|
180 | }
|
181 |
|
182 | // Main thread tells us whether SIGWINCH is trapped.
|
183 | void SetSigWinchCode(int code) {
|
184 | sigwinch_code_ = code;
|
185 | }
|
186 |
|
187 | // Main thread wants to know if SIGWINCH was received since the last time
|
188 | // PollSigWinch was called.
|
189 | bool PollSigWinch() {
|
190 | bool result = received_sigwinch_;
|
191 | received_sigwinch_ = false;
|
192 | return result;
|
193 | }
|
194 |
|
195 | static constexpr uint32_t field_mask() {
|
196 | return maskbit(offsetof(SignalSafe, pending_signals_)) |
|
197 | maskbit(offsetof(SignalSafe, empty_list_));
|
198 | }
|
199 |
|
200 | static constexpr ObjHeader obj_header() {
|
201 | return ObjHeader::ClassFixed(field_mask(), sizeof(SignalSafe));
|
202 | }
|
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 | List<int>* AllocSignalList() {
|
212 | List<int>* ret = NewList<int>();
|
213 | ret->reserve(kMaxPendingSignals);
|
214 | return ret;
|
215 | }
|
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 | bool IsValidCharEscape(BigStr* c);
|
246 | BigStr* ChArrayToString(List<int>* ch_array);
|
247 |
|
248 | class _ResourceLoader {
|
249 | public:
|
250 | _ResourceLoader() {
|
251 | }
|
252 |
|
253 | virtual BigStr* Get(BigStr* path);
|
254 |
|
255 | static constexpr ObjHeader obj_header() {
|
256 | return ObjHeader::ClassFixed(kZeroMask, sizeof(_ResourceLoader));
|
257 | }
|
258 | };
|
259 |
|
260 | _ResourceLoader* GetResourceLoader();
|
261 |
|
262 | BigStr* GetVersion(_ResourceLoader* loader);
|
263 |
|
264 | void PrintVersionDetails(_ResourceLoader* loader);
|
265 |
|
266 | BigStr* strerror(IOError_OSError* e);
|
267 |
|
268 | BigStr* BackslashEscape(BigStr* s, BigStr* meta_chars);
|
269 |
|
270 | grammar::Grammar* LoadYshGrammar(_ResourceLoader*);
|
271 |
|
272 | } // namespace pyutil
|
273 |
|
274 | #endif // CORE_H
|