|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2021 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // シグナルスレッド
9: //
10:
11: // このプロセス内で処理が必要な UNIX シグナルは一旦このスレッドで受け取り、
12: // 必要に応じてスレッドに通知などをする。
13: //
14: // スレッド作成前に StartThread() にてプロセス全体で該当シグナルをブロック
15: // する。他のスレッドはこの後生成するので、全スレッドがこの特性を引き継ぐ。
16: //
17: // シグナルスレッドは、該当シグナルをブロックしたままシグナルが発生するのを
18: // sigwait(3) で待つ (ブロックはデフォルト動作やハンドラ呼び出しが起きるのを
19: // ブロックするがペンディングにはなる、sigwait(3) はこのペンディングに反応
20: // する)。
21: //
22: // o SIGTERM を受け取ると UIMessage でメインスレッドに通知し、そこから安全に
23: // プロセスを終了させる。
24: //
25: // o SIGINT(^C)、SIGTSTP(^Z)、SIGQUIT(^\) は HostCOM のうち stdio ドライバが
26: // 必要とするため動作を切り替える。HostCOM が stdio なら、これらのシグナル
27: // は処理せず、HostCOM スレッドにパイプで通知する。
28: // HostCOM が stdio 以外なら、これらのシグナルは HostCOM に通知せず、
29: // こちらで処理する。
30: //
31: // o SIGHUP、SIGPIPE、SIGUSR1、SIGUSR2 はデフォルトがプロセス終了なので
32: // ブロックして無視する。
1.1.1.2 ! root 33: //
! 34: // o SIGIO は、通常は非同期 I/O アクセスのハンドラとしてアプリケーションが
! 35: // これを使うことが想定されており、つまりライブラリが勝手に差し替えたり
! 36: // しないはずなので、これをこのスレッドへのメッセージ通知として使う。
! 37: // SIGIO 発生時に特定のメンバ変数がセットされているとそれに従って
1.1 root 38: // アクションを起こす。
39: //
40: // Linux (Ubuntu 20.04) ではシグナルの配送が POSIX とは異なっているらしく、
41: // プロセスに届いたシグナルを sigwait しているスレッドに配送してくれない。
42: // glibc のマニュアルには記載があるようだ。
43: // https://linuxjm.osdn.jp/html/glibc-linuxthreads/man3/pthread_kill.3.html
44: // Linux のマニュアルでは確認できなかった。
45:
46: #include "signalthread.h"
47: #include "uimessage.h"
48: #include <climits>
49: #include <csignal>
50:
51: // グローバル参照用
52: SignalThread *gSignalThread;
53:
54: void signal_handler(int signo);
55: void
56: signal_handler(int signo)
57: {
58: #if defined(__linux__)
59: // Linux ではプロセスに届いたシグナルを自動でスレッドに配送して
60: // くれないので、自前でやる必要があるようだ。
61: if (gSignalThread) {
62: gSignalThread->Kill(signo);
63: }
64: #endif
65: }
66:
67: // コンストラクタ
68: SignalThread::SignalThread()
69: : inherited("SignalThread")
70: {
71: wpipe = -1;
72: sigemptyset(&waitset);
73: }
74:
75: // デストラクタ
76: SignalThread::~SignalThread()
77: {
78: TerminateThread();
79: gSignalThread = NULL;
80: }
81:
82: // 常にブロックするシグナル集合
83: static std::vector<int> siglist_always = {
84: SIGHUP, // ignore
85: SIGPIPE, // ignore
86: SIGTERM, // プロセス終了
1.1.1.2 ! root 87: SIGIO, // コマンド通知に使う
1.1 root 88: SIGUSR1, // ignore
89: SIGUSR2, // ignore
90: };
91:
92: // HostCOM が受け取るかどうかによって動作を変えるシグナル集合
93: static std::vector<int> siglist_com = {
94: SIGINT, // HostCOM (^C)
95: SIGQUIT, // HostCOM (^\)
96: SIGTSTP, // HostCOM (^Z)
97: };
98:
99: // スレッド開始 (override)
100: //
101: // SIGINT(^C) など緊急性の高いシグナルのブロック期間を極力短くするため、
102: // シグナルのブロック(マスク)は、極力スレッドを開始する直前に行いたい。
103: // SignalThread は Device リストの先頭なので (see ../vm/vm.cpp)、この
104: // StartThread() 時点ではまだ (少なくとも自分が関与できる) 他のスレッドは
105: // 生成されておらず、ここでのこのスレッドに対する Mask() は、この後生成
106: // されるスレッドに引き継がれるベースになる。
107: bool
108: SignalThread::StartThread()
109: {
110: for (auto signo : siglist_always) {
111: Mask(SIG_BLOCK, signo);
112: }
113: for (auto signo : siglist_com) {
114: Mask(SIG_BLOCK, signo);
115: }
116:
117: // で、スレッドを開始。
118: return inherited::StartThread();
119: }
120:
121: // スレッド実行
122: void
123: SignalThread::ThreadRun()
124: {
125: request_terminate = false;
126: request_wpipe = -1;
127:
128: for (;;) {
129: int signo;
130: int r;
131:
132: // sigwait() は成功なら 0、失敗ならエラーコードを返す。
133: putmsg(2, "sigwait...");
134: r = sigwait(&waitset, &signo);
1.1.1.2 ! root 135: // 終了要求が立っていたら、どのシグナルでも、エラーが起きてても
! 136: // とにかく先にスレッドを終了する。
! 137: if (request_terminate) {
! 138: break;
! 139: }
1.1 root 140: if (r != 0) {
141: if (r == EINTR) {
142: continue;
143: }
144: putmsg(0, "sigwait: %s", strerror(r));
145: // XXX どうする?
146: continue;
147: }
148: putmsg(1, "sigwait got %d", signo);
149:
150: // ここからシグナル別の処理
151: switch (signo) {
1.1.1.2 ! root 152: case SIGIO: // コマンド
1.1 root 153: {
154: // 本来は無視するだけなので、ついでにこのスレッドに対する
155: // メッセージ通知として使用する。フラグが立ってなければ単に無視。
156: //
1.1.1.2 ! root 157: // SIGIO 発生時に request_wpipe が非負なら、HostCOM からの
1.1 root 158: // パイプ接続もしくは切断の要求。
159: int req = request_wpipe.exchange(-1);
160: if (req >= 0) {
161: if (loglevel >= 1) {
162: if (req == INT_MAX) {
1.1.1.2 ! root 163: putmsg(1, "SIGIO: HostCOM del");
1.1 root 164: } else {
1.1.1.2 ! root 165: putmsg(1, "SIGIO: HostCOM add %d", req);
1.1 root 166: }
167: }
168: // HostCOM スレッドへのシグナル通知を変更
169: ChangeCOM(req);
170: }
171: break;
172: }
173:
174: case SIGTERM: // SIGTERM なら即終了
175: // メインスレッドから終了してもらう
176: UIMessage::Post(UIMessage::APPEXIT);
177: break;
178:
179: case SIGINT: // パイプがあれば配送、なければここで処理
180: if (wpipe >= 0) {
181: WriteSignal(wpipe, signo);
182: } else {
183: // メインスレッドから終了してもらう
184: UIMessage::Post(UIMessage::APPEXIT);
185: }
186: break;
187:
188: case SIGTSTP: // パイプがあれば配送、なければデフォルト動作
189: case SIGQUIT:
190: if (wpipe >= 0) {
191: // パイプがあれば書き込んで通知。
192: WriteSignal(wpipe, signo);
193: } else {
194: // パイプがなければ、
195: // ここでデフォルト動作を起こしたい。
196:
197: // 一旦解除して..
198: Mask(SIG_UNBLOCK, signo);
199:
200: // シグナルを自身に送信。
201: Kill(signo);
202:
203: // そして再度ブロックしておく。
204: // こうしないと、2回目の ^Z が効かないとかになる。
205: Mask(SIG_BLOCK, signo);
206: }
207: break;
208:
209: default: // 他は無視する
210: break;
211: }
212: }
213:
214: putmsg(1, "ThreadRun terminating");
215: }
216:
217: void
218: SignalThread::Terminate()
219: {
220: putmsg(1, "Terminate");
221:
222: // フラグを立ててシグナルで通知
223: request_terminate = true;
1.1.1.2 ! root 224: Kill(SIGIO);
1.1 root 225: }
226:
227: // HostCOM スレッドへのパイプを接続する (HostCOM スレッドから呼ばれる)
228: void
229: SignalThread::AddPipe(int fd)
230: {
231: putmsg(1, "AddPipe(fd=%d)", fd);
232:
233: if ((bool)thread == false) {
234: // HostCOM から呼ばれるが、起動時の初期設定は Init() フェーズ内の
235: // ため、その時点ではまだどちらもスレッドはない。
236: // この場合は単にここで設定するだけでよい。
237: ChangeCOM(fd);
238: } else {
239: // スレッド開始後なら、フラグを立ててシグナルで通知
240: request_wpipe = fd;
1.1.1.2 ! root 241: Kill(SIGIO);
1.1 root 242: }
243: }
244:
245: // HostCOM スレッドへのパイプを削除する
246: void
247: SignalThread::DeletePipe()
248: {
249: putmsg(1, "DeletePipe(current=%d)", wpipe);
250:
251: // フラグを立ててシグナルで通知
252: request_wpipe = INT_MAX;
1.1.1.2 ! root 253: Kill(SIGIO);
1.1 root 254: }
255:
256: // シグナルをパイプに再配送する
257: void
258: SignalThread::WriteSignal(int wfd, int signo)
259: {
260: char buf[1];
261: int r;
262:
263: buf[0] = signo;
264: r = write(wpipe, buf, sizeof(buf));
265: if (r < 0) {
266: // エラーが起きたパイプは閉じればいいか…
267: ChangeCOM(INT_MAX);
268: }
269: }
270:
271: // HostCOM スレッドとのパイプを接続/切断する。
272: // SignalThread 側処理。
273: void
274: SignalThread::ChangeCOM(int reqfd)
275: {
276: // reqfd が INT_MAX なら現在のディスクリプタを切断。
277: // そうでなければ、reqfd を接続。
278: // そんな高頻度で投げないので運用上は問題にならないはず。
279:
280: putmsg(1, "ChangeCOM(current=%d request=%d)", wpipe, reqfd);
281:
282: if (reqfd == INT_MAX) {
283: // パイプを切断してからシグナルのブロックを解除
284: close(wpipe);
285: wpipe = -1;
286:
287: for (auto signo : siglist_com) {
288: Mask(SIG_UNBLOCK, signo);
289: }
290: } else {
291: // 必要なシグナルをブロックしてからパイプを接続
292: for (auto signo : siglist_com) {
293: Mask(SIG_BLOCK, signo);
294: }
295:
296: wpipe = reqfd;
297: }
298: }
299:
300: // SignalThread にシグナルを投げる (他スレッドからも使用可)
301: void
302: SignalThread::Kill(int signo)
303: {
304: assert((bool)thread);
305:
306: putmsg(1, "Kill(%d)", signo);
307: pthread_kill(thread->native_handle(), signo);
308: }
309:
310: // SIG_BLOCK ならブロックする集合に追加 (=配送する)。
311: // SIG_UNBLOCK ならブロックする集合から削除 (=こちらで処理する)。
312: void
313: SignalThread::Mask(int how, int signo)
314: {
315: const char *howstr = (how == SIG_BLOCK) ? "BLOCK" : "UNBLOCK";
316:
317: putmsg(1, "Mask(%s, %d)", howstr, signo);
318:
319: // シグナルハンドラを設定 (シグナルハンドラはプロセスグローバル)
320: // signal(3) は挙動が違う可能性があるので使わないこと。
321: struct sigaction act;
322: memset(&act, 0, sizeof(act));
323: if (how == SIG_BLOCK) {
324: act.sa_handler = signal_handler;
325: } else {
326: act.sa_handler = SIG_DFL;
327: }
328: if (sigaction(signo, &act, NULL) < 0) {
329: putmsg(0, "sigaction(%d, %s): %s", signo, howstr, strerror(errno));
330: return;
331: }
332:
333: // このスレッドのマスクを設定
334: sigset_t set;
335: sigemptyset(&set);
336: sigaddset(&set, signo);
337: if (pthread_sigmask(how, &set, NULL) < 0) {
338: putmsg(0, "pthread_sigmask(%s, %d): %s",
339: howstr, signo, strerror(errno));
340: return;
341: }
342:
343: // sigwait() 用のセットを更新
344: if (how == SIG_BLOCK) {
345: sigaddset(&waitset, signo);
346: } else {
347: sigdelset(&waitset, signo);
348: }
349:
350: // 適当デバッグ表示
351: if (loglevel >= 1) {
352: std::string str;
353:
354: #define CHECK(NAM) do { \
355: if (sigismember(&waitset, __CONCAT(SIG,NAM))) { \
356: str += " "; \
357: str += #NAM; \
358: } \
359: } while (0)
360: CHECK(HUP);
361: CHECK(INT);
362: CHECK(QUIT);
363: CHECK(PIPE);
364: CHECK(TERM);
365: CHECK(TSTP);
1.1.1.2 ! root 366: CHECK(IO);
1.1 root 367: CHECK(USR1);
368: CHECK(USR2);
369:
370: putmsgn("waitset =%s", str.c_str());
371: }
372: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.