cpp

Coverage Report

Created: 2024-03-13 14:13

/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
76
        pw_gid(entry->pw_gid) {
65
76
  }
66
67
76
  static constexpr ObjHeader obj_header() {
68
76
    return ObjHeader::ClassFixed(field_mask(), sizeof(PasswdEntry));
69
76
  }
70
71
  BigStr* pw_name;
72
  int pw_uid;
73
  int pw_gid;
74
75
76
  static constexpr uint32_t field_mask() {
76
76
    return maskbit(offsetof(PasswdEntry, pw_name));
77
76
  }
78
};
79
80
List<PasswdEntry*>* GetAllUsers();
81
82
BigStr* GetUserName(int uid);
83
84
BigStr* OsType();
85
86
Tuple3<double, double, double> Time();
87
88
void PrintTimes();
89
90
bool InputAvailable(int fd);
91
92
1
inline void FlushStdout() {
93
  // Flush libc buffers
94
1
  fflush(stdout);
95
1
}
96
97
Tuple2<int, void*> PushTermAttrs(int fd, int mask);
98
void 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.
102
const int kMaxPendingSignals = 1022;
103
104
class 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
2
        num_dropped_(0) {
115
2
  }
116
117
  // Called from signal handling context.  Do not allocate.
118
1.03k
  void UpdateFromSignalHandler(int sig_num) {
119
1.03k
    if (pending_signals_->len_ < pending_signals_->capacity_) {
120
      // We can append without allocating
121
1.02k
      pending_signals_->append(sig_num);
122
1.02k
    } else {
123
      // Unlikely: we would have to allocate.  Just increment a counter, which
124
      // we could expose somewhere in the UI.
125
10
      num_dropped_++;
126
10
    }
127
128
1.03k
    if (sig_num == SIGINT) {
129
1.03k
      received_sigint_ = true;
130
1.03k
    }
131
132
1.03k
    if (sig_num == SIGWINCH) {
133
2
      received_sigwinch_ = true;
134
2
      sig_num = sigwinch_code_;  // mutate param
135
2
    }
136
137
#if LOCK_FREE_ATOMICS
138
    last_sig_num_.store(sig_num);
139
#else
140
1.03k
    last_sig_num_ = sig_num;
141
1.03k
#endif
142
1.03k
  }
143
144
  // Main thread takes signals so it can run traps.
145
5
  List<int>* TakePendingSignals() {
146
5
    List<int>* ret = pending_signals_;
147
148
    // Make sure we have a distinct list to reuse.
149
5
    DCHECK(empty_list_ != pending_signals_);
150
0
    pending_signals_ = empty_list_;
151
152
5
    return ret;
153
5
  }
154
155
  // Main thread returns the same list as an optimization to avoid allocation.
156
3
  void ReuseEmptyList(List<int>* empty_list) {
157
3
    DCHECK(empty_list != pending_signals_);  // must be different
158
3
    DCHECK(len(empty_list) == 0);            // main thread clears
159
3
    DCHECK(empty_list->capacity_ == kMaxPendingSignals);
160
161
0
    empty_list_ = empty_list;
162
3
  }
163
164
  // Main thread wants to get the last signal received.
165
4
  int LastSignal() {
166
#if LOCK_FREE_ATOMICS
167
    return last_sig_num_.load();
168
#else
169
4
    return last_sig_num_;
170
4
#endif
171
4
  }
172
173
  // Main thread wants to know if SIGINT was received since the last time
174
  // PollSigInt was called.
175
0
  bool PollSigInt() {
176
0
    bool result = received_sigint_;
177
0
    received_sigint_ = false;
178
0
    return result;
179
0
  }
180
181
  // Main thread tells us whether SIGWINCH is trapped.
182
1
  void SetSigWinchCode(int code) {
183
1
    sigwinch_code_ = code;
184
1
  }
185
186
  // Main thread wants to know if SIGWINCH was received since the last time
187
  // PollSigWinch was called.
188
0
  bool PollSigWinch() {
189
0
    bool result = received_sigwinch_;
190
0
    received_sigwinch_ = false;
191
0
    return result;
192
0
  }
193
194
1
  static constexpr uint32_t field_mask() {
195
1
    return maskbit(offsetof(SignalSafe, pending_signals_)) |
196
1
           maskbit(offsetof(SignalSafe, empty_list_));
197
1
  }
198
199
1
  static constexpr ObjHeader obj_header() {
200
1
    return ObjHeader::ClassFixed(field_mask(), sizeof(SignalSafe));
201
1
  }
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
4
  List<int>* AllocSignalList() {
211
4
    List<int>* ret = NewList<int>();
212
4
    ret->reserve(kMaxPendingSignals);
213
4
    return ret;
214
4
  }
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
229
extern SignalSafe* gSignalSafe;
230
231
// Allocate global and return it.
232
SignalSafe* InitSignalSafe();
233
234
void Sigaction(int sig_num, void (*handler)(int));
235
236
void RegisterSignalInterest(int sig_num);
237
238
Tuple2<BigStr*, int>* MakeDirCacheKey(BigStr* path);
239
240
}  // namespace pyos
241
242
namespace pyutil {
243
244
bool IsValidCharEscape(BigStr* c);
245
BigStr* ChArrayToString(List<int>* ch_array);
246
247
class _ResourceLoader {
248
 public:
249
1
  _ResourceLoader() {
250
1
  }
251
252
  virtual BigStr* Get(BigStr* path);
253
254
1
  static constexpr ObjHeader obj_header() {
255
1
    return ObjHeader::ClassFixed(kZeroMask, sizeof(_ResourceLoader));
256
1
  }
257
};
258
259
_ResourceLoader* GetResourceLoader();
260
261
BigStr* GetVersion(_ResourceLoader* loader);
262
263
void PrintVersionDetails(_ResourceLoader* loader);
264
265
BigStr* strerror(IOError_OSError* e);
266
267
BigStr* BackslashEscape(BigStr* s, BigStr* meta_chars);
268
269
grammar::Grammar* LoadYshGrammar(_ResourceLoader*);
270
271
}  // namespace pyutil
272
273
#endif  // CORE_H