OILS / mycpp / gc_iolib.h View on Github | oilshell.org

175 lines, 95 significant
1// gc_iolib.h - corresponds to mycpp/iolib.py
2
3#ifndef MYCPP_GC_IOLIB_H
4#define MYCPP_GC_IOLIB_H
5
6// For now, we assume that simple int and pointer operations are atomic, rather
7// than using std::atomic. Could be a ./configure option later.
8//
9// See doc/portability.md.
10
11#define LOCK_FREE_ATOMICS 0
12
13#if LOCK_FREE_ATOMICS
14 #include <atomic>
15#endif
16#include <signal.h>
17
18#include "mycpp/gc_list.h"
19
20namespace iolib {
21
22const int UNTRAPPED_SIGWINCH = -1;
23
24// Make the signal queue slab 4096 bytes, including the GC header. See
25// cpp/core_test.cc.
26const int kMaxPendingSignals = 1022;
27
28class SignalSafe {
29 // State that is shared between the main thread and signal handlers.
30 public:
31 SignalSafe()
32 : pending_signals_(AllocSignalList()),
33 empty_list_(AllocSignalList()), // to avoid repeated allocation
34 last_sig_num_(0),
35 received_sigint_(false),
36 received_sigwinch_(false),
37 sigwinch_code_(UNTRAPPED_SIGWINCH),
38 num_dropped_(0) {
39 }
40
41 // Called from signal handling context. Do not allocate.
42 void UpdateFromSignalHandler(int sig_num) {
43 if (pending_signals_->len_ < pending_signals_->capacity_) {
44 // We can append without allocating
45 pending_signals_->append(sig_num);
46 } else {
47 // Unlikely: we would have to allocate. Just increment a counter, which
48 // we could expose somewhere in the UI.
49 num_dropped_++;
50 }
51
52 if (sig_num == SIGINT) {
53 received_sigint_ = true;
54 }
55
56 if (sig_num == SIGWINCH) {
57 received_sigwinch_ = true;
58 sig_num = sigwinch_code_; // mutate param
59 }
60
61#if LOCK_FREE_ATOMICS
62 last_sig_num_.store(sig_num);
63#else
64 last_sig_num_ = sig_num;
65#endif
66 }
67
68 // Main thread takes signals so it can run traps.
69 List<int>* TakePendingSignals() {
70 List<int>* ret = pending_signals_;
71
72 // Make sure we have a distinct list to reuse.
73 DCHECK(empty_list_ != pending_signals_);
74 pending_signals_ = empty_list_;
75
76 return ret;
77 }
78
79 // Main thread returns the same list as an optimization to avoid allocation.
80 void ReuseEmptyList(List<int>* empty_list) {
81 DCHECK(empty_list != pending_signals_); // must be different
82 DCHECK(len(empty_list) == 0); // main thread clears
83 DCHECK(empty_list->capacity_ == kMaxPendingSignals);
84
85 empty_list_ = empty_list;
86 }
87
88 // Main thread wants to get the last signal received.
89 int LastSignal() {
90#if LOCK_FREE_ATOMICS
91 return last_sig_num_.load();
92#else
93 return last_sig_num_;
94#endif
95 }
96
97 void SetSigIntTrapped(bool b) {
98 sigint_trapped_ = b;
99 }
100
101 // Used by pyos.WaitPid, Read, ReadByte.
102 bool PollSigInt() {
103 bool result = received_sigint_;
104 received_sigint_ = false;
105 return result;
106 }
107
108 // Used by osh/cmd_eval.py. Main loop wants to know if SIGINT was received
109 // since the last time PollSigInt was called.
110 bool PollUntrappedSigInt() {
111 bool received = PollSigInt(); // clears a flag
112 return received && !sigint_trapped_;
113 }
114
115 // Main thread tells us whether SIGWINCH is trapped.
116 void SetSigWinchCode(int code) {
117 sigwinch_code_ = code;
118 }
119
120 // Main thread wants to know if SIGWINCH was received since the last time
121 // PollSigWinch was called.
122 bool PollSigWinch() {
123 bool result = received_sigwinch_;
124 received_sigwinch_ = false;
125 return result;
126 }
127
128 static constexpr uint32_t field_mask() {
129 return maskbit(offsetof(SignalSafe, pending_signals_)) |
130 maskbit(offsetof(SignalSafe, empty_list_));
131 }
132
133 static constexpr ObjHeader obj_header() {
134 return ObjHeader::ClassFixed(field_mask(), sizeof(SignalSafe));
135 }
136
137 List<int>* pending_signals_; // public for testing
138 List<int>* empty_list_;
139
140 private:
141 // Enforce private state because two different "threads" will use it!
142
143 // Reserve a fixed number of signals.
144 List<int>* AllocSignalList() {
145 List<int>* ret = NewList<int>();
146 ret->reserve(kMaxPendingSignals);
147 return ret;
148 }
149
150#if LOCK_FREE_ATOMICS
151 std::atomic<int> last_sig_num_;
152#else
153 int last_sig_num_;
154#endif
155 // Not sufficient: volatile sig_atomic_t last_sig_num_;
156
157 bool sigint_trapped_;
158 int received_sigint_;
159 int received_sigwinch_;
160 int sigwinch_code_;
161 int num_dropped_;
162};
163
164extern SignalSafe* gSignalSafe;
165
166// Allocate global and return it.
167SignalSafe* InitSignalSafe();
168
169void RegisterSignalInterest(int sig_num);
170
171void sigaction(int sig_num, void (*handler)(int));
172
173} // namespace iolib
174
175#endif // MYCPP_GC_IOLIB_H