cpp

Coverage Report

Created: 2024-08-25 11:48

/home/andy/git/oilshell/oil/cpp/core.h
Line
Count
Source (jump to first uncovered line)
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
1
  explicit ReadError(int err_num_) : err_num(err_num_) {
50
1
  }
51
52
1
  static constexpr ObjHeader obj_header() {
53
1
    return ObjHeader::ClassFixed(kZeroMask, sizeof(ReadError));
54
1
  }
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
36
        pw_gid(entry->pw_gid) {
65
36
  }
66
67
36
  static constexpr ObjHeader obj_header() {
68
36
    return ObjHeader::ClassFixed(field_mask(), sizeof(PasswdEntry));
69
36
  }
70
71
  BigStr* pw_name;
72
  int pw_uid;
73
  int pw_gid;
74
75
36
  static constexpr uint32_t field_mask() {
76
36
    return maskbit(offsetof(PasswdEntry, pw_name));
77
36
  }
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
2
        num_dropped_(0) {
116
2
  }
117
118
  // Called from signal handling context.  Do not allocate.
119
1.03k
  void UpdateFromSignalHandler(int sig_num) {
120
1.03k
    if (pending_signals_->len_ < pending_signals_->capacity_) {
121
      // We can append without allocating
122
1.02k
      pending_signals_->append(sig_num);
123
1.02k
    } else {
124
      // Unlikely: we would have to allocate.  Just increment a counter, which
125
      // we could expose somewhere in the UI.
126
10
      num_dropped_++;
127
10
    }
128
129
1.03k
    if (sig_num == SIGINT) {
130
1.03k
      received_sigint_ = true;
131
1.03k
    }
132
133
1.03k
    if (sig_num == SIGWINCH) {
134
2
      received_sigwinch_ = true;
135
2
      sig_num = sigwinch_code_;  // mutate param
136
2
    }
137
138
#if LOCK_FREE_ATOMICS
139
    last_sig_num_.store(sig_num);
140
#else
141
1.03k
    last_sig_num_ = sig_num;
142
1.03k
#endif
143
1.03k
  }
144
145
  // Main thread takes signals so it can run traps.
146
5
  List<int>* TakePendingSignals() {
147
5
    List<int>* ret = pending_signals_;
148
149
    // Make sure we have a distinct list to reuse.
150
5
    DCHECK(empty_list_ != pending_signals_);
151
0
    pending_signals_ = empty_list_;
152
153
5
    return ret;
154
5
  }
155
156
  // Main thread returns the same list as an optimization to avoid allocation.
157
3
  void ReuseEmptyList(List<int>* empty_list) {
158
3
    DCHECK(empty_list != pending_signals_);  // must be different
159
3
    DCHECK(len(empty_list) == 0);            // main thread clears
160
3
    DCHECK(empty_list->capacity_ == kMaxPendingSignals);
161
162
0
    empty_list_ = empty_list;
163
3
  }
164
165
  // Main thread wants to get the last signal received.
166
4
  int LastSignal() {
167
#if LOCK_FREE_ATOMICS
168
    return last_sig_num_.load();
169
#else
170
4
    return last_sig_num_;
171
4
#endif
172
4
  }
173
174
0
  void SetSigIntTrapped(bool b) {
175
0
    sigint_trapped_ = b;
176
0
  }
177
178
  // Used by pyos.WaitPid, Read, ReadByte.
179
0
  bool PollSigInt() {
180
0
    bool result = received_sigint_;
181
0
    received_sigint_ = false;
182
0
    return result;
183
0
  }
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
0
  bool PollUntrappedSigInt() {
188
0
    bool received = PollSigInt();  // clears a flag
189
0
    return received && !sigint_trapped_;
190
0
  }
191
192
  // Main thread tells us whether SIGWINCH is trapped.
193
1
  void SetSigWinchCode(int code) {
194
1
    sigwinch_code_ = code;
195
1
  }
196
197
  // Main thread wants to know if SIGWINCH was received since the last time
198
  // PollSigWinch was called.
199
0
  bool PollSigWinch() {
200
0
    bool result = received_sigwinch_;
201
0
    received_sigwinch_ = false;
202
0
    return result;
203
0
  }
204
205
1
  static constexpr uint32_t field_mask() {
206
1
    return maskbit(offsetof(SignalSafe, pending_signals_)) |
207
1
           maskbit(offsetof(SignalSafe, empty_list_));
208
1
  }
209
210
1
  static constexpr ObjHeader obj_header() {
211
1
    return ObjHeader::ClassFixed(field_mask(), sizeof(SignalSafe));
212
1
  }
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
4
  List<int>* AllocSignalList() {
222
4
    List<int>* ret = NewList<int>();
223
4
    ret->reserve(kMaxPendingSignals);
224
4
    return ret;
225
4
  }
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
241
extern SignalSafe* gSignalSafe;
242
243
// Allocate global and return it.
244
SignalSafe* InitSignalSafe();
245
246
void sigaction(int sig_num, void (*handler)(int));
247
248
void RegisterSignalInterest(int sig_num);
249
250
Tuple2<BigStr*, int>* MakeDirCacheKey(BigStr* path);
251
252
}  // namespace pyos
253
254
namespace pyutil {
255
256
double infinity();
257
double nan();
258
259
bool IsValidCharEscape(BigStr* c);
260
BigStr* ChArrayToString(List<int>* ch_array);
261
262
class _ResourceLoader {
263
 public:
264
1
  _ResourceLoader() {
265
1
  }
266
267
  virtual BigStr* Get(BigStr* path);
268
269
1
  static constexpr ObjHeader obj_header() {
270
1
    return ObjHeader::ClassFixed(kZeroMask, sizeof(_ResourceLoader));
271
1
  }
272
};
273
274
_ResourceLoader* GetResourceLoader();
275
276
BigStr* GetVersion(_ResourceLoader* loader);
277
278
void PrintVersionDetails(_ResourceLoader* loader);
279
280
BigStr* strerror(IOError_OSError* e);
281
282
BigStr* BackslashEscape(BigStr* s, BigStr* meta_chars);
283
284
grammar::Grammar* LoadYshGrammar(_ResourceLoader*);
285
286
}  // namespace pyutil
287
288
#endif  // CORE_H