OILS / cpp / core.h View on Github | oilshell.org

288 lines, 165 significant
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
26namespace completion {
27class RootCompleter;
28};
29
30namespace pyos {
31
32const int TERM_ICANON = ICANON;
33const int TERM_ECHO = ECHO;
34const int EOF_SENTINEL = 256;
35const int NEWLINE_CH = 10;
36const int UNTRAPPED_SIGWINCH = -1;
37
38Tuple2<int, int> WaitPid(int waitpid_options);
39Tuple2<int, int> Read(int fd, int n, List<BigStr*>* chunks);
40Tuple2<int, int> ReadByte(int fd);
41BigStr* ReadLineBuffered();
42Dict<BigStr*, BigStr*>* Environ();
43int Chdir(BigStr* dest_dir);
44BigStr* GetMyHomeDir();
45BigStr* GetHomeDir(BigStr* user_name);
46
47class 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
59class 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
80List<PasswdEntry*>* GetAllUsers();
81
82BigStr* GetUserName(int uid);
83
84BigStr* OsType();
85
86Tuple2<mops::BigInt, mops::BigInt> GetRLimit(int resource);
87
88void SetRLimit(int resource, mops::BigInt soft, mops::BigInt hard);
89
90Tuple3<double, double, double> Time();
91
92void PrintTimes();
93
94bool InputAvailable(int fd);
95
96IOError_OSError* FlushStdout();
97
98Tuple2<int, void*> PushTermAttrs(int fd, int mask);
99void 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.
103const int kMaxPendingSignals = 1022;
104
105class 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 void SetSigIntTrapped(bool b) {
175 sigint_trapped_ = b;
176 }
177
178 // Used by pyos.WaitPid, Read, ReadByte.
179 bool PollSigInt() {
180 bool result = received_sigint_;
181 received_sigint_ = false;
182 return result;
183 }
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 bool PollUntrappedSigInt() {
188 bool received = PollSigInt(); // clears a flag
189 return received && !sigint_trapped_;
190 }
191
192 // Main thread tells us whether SIGWINCH is trapped.
193 void SetSigWinchCode(int code) {
194 sigwinch_code_ = code;
195 }
196
197 // Main thread wants to know if SIGWINCH was received since the last time
198 // PollSigWinch was called.
199 bool PollSigWinch() {
200 bool result = received_sigwinch_;
201 received_sigwinch_ = false;
202 return result;
203 }
204
205 static constexpr uint32_t field_mask() {
206 return maskbit(offsetof(SignalSafe, pending_signals_)) |
207 maskbit(offsetof(SignalSafe, empty_list_));
208 }
209
210 static constexpr ObjHeader obj_header() {
211 return ObjHeader::ClassFixed(field_mask(), sizeof(SignalSafe));
212 }
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 List<int>* AllocSignalList() {
222 List<int>* ret = NewList<int>();
223 ret->reserve(kMaxPendingSignals);
224 return ret;
225 }
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
241extern SignalSafe* gSignalSafe;
242
243// Allocate global and return it.
244SignalSafe* InitSignalSafe();
245
246void sigaction(int sig_num, void (*handler)(int));
247
248void RegisterSignalInterest(int sig_num);
249
250Tuple2<BigStr*, int>* MakeDirCacheKey(BigStr* path);
251
252} // namespace pyos
253
254namespace pyutil {
255
256double infinity();
257double nan();
258
259bool IsValidCharEscape(BigStr* c);
260BigStr* ChArrayToString(List<int>* ch_array);
261
262class _ResourceLoader {
263 public:
264 _ResourceLoader() {
265 }
266
267 virtual BigStr* Get(BigStr* path);
268
269 static constexpr ObjHeader obj_header() {
270 return ObjHeader::ClassFixed(kZeroMask, sizeof(_ResourceLoader));
271 }
272};
273
274_ResourceLoader* GetResourceLoader();
275
276BigStr* GetVersion(_ResourceLoader* loader);
277
278void PrintVersionDetails(_ResourceLoader* loader);
279
280BigStr* strerror(IOError_OSError* e);
281
282BigStr* BackslashEscape(BigStr* s, BigStr* meta_chars);
283
284grammar::Grammar* LoadYshGrammar(_ResourceLoader*);
285
286} // namespace pyutil
287
288#endif // CORE_H