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

273 lines, 156 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
86Tuple3<double, double, double> Time();
87
88void PrintTimes();
89
90bool InputAvailable(int fd);
91
92inline void FlushStdout() {
93 // Flush libc buffers
94 fflush(stdout);
95}
96
97Tuple2<int, void*> PushTermAttrs(int fd, int mask);
98void 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.
102const int kMaxPendingSignals = 1022;
103
104class 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
229extern SignalSafe* gSignalSafe;
230
231// Allocate global and return it.
232SignalSafe* InitSignalSafe();
233
234void Sigaction(int sig_num, void (*handler)(int));
235
236void RegisterSignalInterest(int sig_num);
237
238Tuple2<BigStr*, int>* MakeDirCacheKey(BigStr* path);
239
240} // namespace pyos
241
242namespace pyutil {
243
244bool IsValidCharEscape(BigStr* c);
245BigStr* ChArrayToString(List<int>* ch_array);
246
247class _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
261BigStr* GetVersion(_ResourceLoader* loader);
262
263void PrintVersionDetails(_ResourceLoader* loader);
264
265BigStr* strerror(IOError_OSError* e);
266
267BigStr* BackslashEscape(BigStr* s, BigStr* meta_chars);
268
269grammar::Grammar* LoadYshGrammar(_ResourceLoader*);
270
271} // namespace pyutil
272
273#endif // CORE_H