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

277 lines, 159 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 // 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
230extern SignalSafe* gSignalSafe;
231
232// Allocate global and return it.
233SignalSafe* InitSignalSafe();
234
235void Sigaction(int sig_num, void (*handler)(int));
236
237void RegisterSignalInterest(int sig_num);
238
239Tuple2<BigStr*, int>* MakeDirCacheKey(BigStr* path);
240
241} // namespace pyos
242
243namespace pyutil {
244
245float infinity();
246float nan();
247
248bool IsValidCharEscape(BigStr* c);
249BigStr* ChArrayToString(List<int>* ch_array);
250
251class _ResourceLoader {
252 public:
253 _ResourceLoader() {
254 }
255
256 virtual BigStr* Get(BigStr* path);
257
258 static constexpr ObjHeader obj_header() {
259 return ObjHeader::ClassFixed(kZeroMask, sizeof(_ResourceLoader));
260 }
261};
262
263_ResourceLoader* GetResourceLoader();
264
265BigStr* GetVersion(_ResourceLoader* loader);
266
267void PrintVersionDetails(_ResourceLoader* loader);
268
269BigStr* strerror(IOError_OSError* e);
270
271BigStr* BackslashEscape(BigStr* s, BigStr* meta_chars);
272
273grammar::Grammar* LoadYshGrammar(_ResourceLoader*);
274
275} // namespace pyutil
276
277#endif // CORE_H