cpp

Coverage Report

Created: 2023-03-07 20:24

/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 LEAKY_CORE_H
4
#define LEAKY_CORE_H
5
6
#include <pwd.h>     // passwd
7
#include <signal.h>  // sighandler_t
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();
39
Tuple2<int, int> Read(int fd, int n, List<Str*>* chunks);
40
Tuple2<int, int> ReadByte(int fd);
41
Str* ReadLine();
42
Dict<Str*, Str*>* Environ();
43
int Chdir(Str* dest_dir);
44
Str* GetMyHomeDir();
45
Str* GetHomeDir(Str* user_name);
46
47
class ReadError {
48
 public:
49
1
  explicit ReadError(int err_num_) : header_(obj_header()), 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
  GC_OBJ(header_);
57
  int err_num;
58
};
59
60
class PasswdEntry {
61
 public:
62
  explicit PasswdEntry(const passwd* entry)
63
      : header_(obj_header()),
64
        pw_name(StrFromC(entry->pw_name)),
65
        pw_uid(entry->pw_uid),
66
75
        pw_gid(entry->pw_gid) {
67
75
  }
68
69
75
  static constexpr ObjHeader obj_header() {
70
75
    return ObjHeader::ClassFixed(field_mask(), sizeof(PasswdEntry));
71
75
  }
72
73
  GC_OBJ(header_);
74
  Str* pw_name;
75
  int pw_uid;
76
  int pw_gid;
77
78
75
  static constexpr uint16_t field_mask() {
79
75
    return maskbit(offsetof(PasswdEntry, pw_name));
80
75
  }
81
};
82
83
List<PasswdEntry*>* GetAllUsers();
84
85
Str* GetUserName(int uid);
86
87
Str* OsType();
88
89
Tuple3<double, double, double> Time();
90
91
void PrintTimes();
92
93
bool InputAvailable(int fd);
94
95
1
inline void FlushStdout() {
96
  // Flush libc buffers
97
1
  fflush(stdout);
98
1
}
99
100
class TermState {
101
 public:
102
0
  TermState(int fd, int mask) {
103
0
    assert(0);
104
0
  }
105
0
  void Restore() {
106
0
    assert(0);
107
0
  }
108
};
109
110
// Make the signal queue slab 4096 bytes, including the GC header.  See
111
// cpp/core_test.cc.
112
const int kMaxPendingSignals = 1022;
113
114
class SignalSafe {
115
  // State that is shared between the main thread and signal handlers.
116
 public:
117
  SignalSafe()
118
      : header_(obj_header()),
119
        pending_signals_(AllocSignalList()),
120
        empty_list_(AllocSignalList()),  // to avoid repeated allocation
121
        last_sig_num_(0),
122
        received_sigint_(false),
123
        received_sigwinch_(false),
124
        sigwinch_code_(UNTRAPPED_SIGWINCH),
125
2
        num_dropped_(0) {
126
2
  }
127
128
  // Called from signal handling context.  Do not allocate.
129
1.03k
  void UpdateFromSignalHandler(int sig_num) {
130
1.03k
    if (pending_signals_->len_ < pending_signals_->capacity_) {
131
      // We can append without allocating
132
1.02k
      pending_signals_->append(sig_num);
133
1.02k
    } else {
134
      // Unlikely: we would have to allocate.  Just increment a counter, which
135
      // we could expose somewhere in the UI.
136
10
      num_dropped_++;
137
10
    }
138
139
1.03k
    if (sig_num == SIGINT) {
140
1.03k
      received_sigint_ = true;
141
1.03k
    }
142
143
1.03k
    if (sig_num == SIGWINCH) {
144
2
      received_sigwinch_ = true;
145
2
      sig_num = sigwinch_code_;  // mutate param
146
2
    }
147
148
#if LOCK_FREE_ATOMICS
149
    last_sig_num_.store(sig_num);
150
#else
151
1.03k
    last_sig_num_ = sig_num;
152
1.03k
#endif
153
1.03k
  }
154
155
  // Main thread takes signals so it can run traps.
156
5
  List<int>* TakePendingSignals() {
157
5
    List<int>* ret = pending_signals_;
158
159
    // Make sure we have a distinct list to reuse.
160
5
    DCHECK(empty_list_ != pending_signals_);
161
0
    pending_signals_ = empty_list_;
162
163
5
    return ret;
164
5
  }
165
166
  // Main thread returns the same list as an optimization to avoid allocation.
167
3
  void ReuseEmptyList(List<int>* empty_list) {
168
3
    DCHECK(empty_list != pending_signals_);  // must be different
169
3
    DCHECK(len(empty_list) == 0);            // main thread clears
170
3
    DCHECK(empty_list->capacity_ == kMaxPendingSignals);
171
172
0
    empty_list_ = empty_list;
173
3
  }
174
175
  // Main thread wants to get the last signal received.
176
4
  int LastSignal() {
177
#if LOCK_FREE_ATOMICS
178
    return last_sig_num_.load();
179
#else
180
4
    return last_sig_num_;
181
4
#endif
182
4
  }
183
184
  // Main thread wants to know if SIGINT was received since the last time
185
  // PollSigInt was called.
186
0
  bool PollSigInt() {
187
0
    bool result = received_sigint_;
188
0
    received_sigint_ = false;
189
0
    return result;
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
2
  static constexpr uint16_t field_mask() {
206
2
    return maskbit(offsetof(SignalSafe, pending_signals_)) |
207
2
           maskbit(offsetof(SignalSafe, empty_list_));
208
2
  }
209
210
2
  static constexpr ObjHeader obj_header() {
211
2
    return ObjHeader::ClassFixed(field_mask(), sizeof(SignalSafe));
212
2
  }
213
214
  GC_OBJ(header_);
215
  List<int>* pending_signals_;  // public for testing
216
  List<int>* empty_list_;
217
218
 private:
219
  // Enforce private state because two different "threads" will use it!
220
221
  // Reserve a fixed number of signals.
222
4
  List<int>* AllocSignalList() {
223
4
    List<int>* ret = NewList<int>();
224
4
    ret->reserve(kMaxPendingSignals);
225
4
    return ret;
226
4
  }
227
228
#if LOCK_FREE_ATOMICS
229
  std::atomic<int> last_sig_num_;
230
#else
231
  int last_sig_num_;
232
#endif
233
  // Not sufficient: volatile sig_atomic_t last_sig_num_;
234
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, sighandler_t handler);
247
248
void RegisterSignalInterest(int sig_num);
249
250
Tuple2<Str*, int>* MakeDirCacheKey(Str* path);
251
252
}  // namespace pyos
253
254
namespace pyutil {
255
256
bool IsValidCharEscape(Str* c);
257
Str* ChArrayToString(List<int>* ch_array);
258
259
class _ResourceLoader {
260
 public:
261
1
  _ResourceLoader() : header_(obj_header()) {
262
1
  }
263
264
  virtual Str* Get(Str* path);
265
266
1
  static constexpr ObjHeader obj_header() {
267
1
    return ObjHeader::ClassFixed(kZeroMask, sizeof(_ResourceLoader));
268
1
  }
269
270
  GC_OBJ(header_);
271
};
272
273
_ResourceLoader* GetResourceLoader();
274
275
Str* GetVersion(_ResourceLoader* loader);
276
277
void ShowAppVersion(_ResourceLoader* loader);
278
279
Str* strerror(IOError_OSError* e);
280
281
Str* BackslashEscape(Str* s, Str* meta_chars);
282
283
grammar::Grammar* LoadOilGrammar(_ResourceLoader*);
284
285
}  // namespace pyutil
286
287
#endif  // LEAKY_CORE_H