|
|
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: //
1.1.1.3 ! root 22: // o SIGTERM、SIGINT(^C) を受け取ると UIMessage でメインスレッドに通知し、
! 23: // そこから安全にプロセスを終了させる。
1.1 root 24: //
25: // o SIGHUP、SIGPIPE、SIGUSR1、SIGUSR2 はデフォルトがプロセス終了なので
26: // ブロックして無視する。
1.1.1.2 root 27: //
1.1 root 28: // Linux (Ubuntu 20.04) ではシグナルの配送が POSIX とは異なっているらしく、
29: // プロセスに届いたシグナルを sigwait しているスレッドに配送してくれない。
30: // glibc のマニュアルには記載があるようだ。
31: // https://linuxjm.osdn.jp/html/glibc-linuxthreads/man3/pthread_kill.3.html
32: // Linux のマニュアルでは確認できなかった。
33:
34: #include "signalthread.h"
35: #include "uimessage.h"
36: #include <csignal>
37:
1.1.1.3 ! root 38: static void signal_handler(int signo);
1.1 root 39:
1.1.1.3 ! root 40: static void
1.1 root 41: signal_handler(int signo)
42: {
43: #if defined(__linux__)
44: // Linux ではプロセスに届いたシグナルを自動でスレッドに配送して
45: // くれないので、自前でやる必要があるようだ。
1.1.1.3 ! root 46: SignalThread *sigthread = GetSignalThread();
! 47: if (sigthread) {
! 48: sigthread->Kill(signo);
1.1 root 49: }
50: #endif
51: }
52:
53: // コンストラクタ
54: SignalThread::SignalThread()
1.1.1.3 ! root 55: : inherited(OBJ_SIGNAL_THREAD)
1.1 root 56: {
57: sigemptyset(&waitset);
58: }
59:
60: // デストラクタ
61: SignalThread::~SignalThread()
62: {
63: TerminateThread();
64: }
65:
66: // 常にブロックするシグナル集合
67: static std::vector<int> siglist_always = {
68: SIGHUP, // ignore
1.1.1.3 ! root 69: SIGINT, // プロセス終了
1.1 root 70: SIGPIPE, // ignore
71: SIGTERM, // プロセス終了
72: SIGUSR1, // ignore
73: SIGUSR2, // ignore
74: };
75:
76: // スレッド開始 (override)
77: //
78: // SIGINT(^C) など緊急性の高いシグナルのブロック期間を極力短くするため、
79: // シグナルのブロック(マスク)は、極力スレッドを開始する直前に行いたい。
80: // SignalThread は Device リストの先頭なので (see ../vm/vm.cpp)、この
81: // StartThread() 時点ではまだ (少なくとも自分が関与できる) 他のスレッドは
82: // 生成されておらず、ここでのこのスレッドに対する Mask() は、この後生成
83: // されるスレッドに引き継がれるベースになる。
84: bool
85: SignalThread::StartThread()
86: {
87: for (auto signo : siglist_always) {
88: Mask(SIG_BLOCK, signo);
89: }
90:
91: // で、スレッドを開始。
92: return inherited::StartThread();
93: }
94:
95: // スレッド実行
96: void
97: SignalThread::ThreadRun()
98: {
99: request_terminate = false;
100:
101: for (;;) {
102: int signo;
103: int r;
104:
105: // sigwait() は成功なら 0、失敗ならエラーコードを返す。
106: putmsg(2, "sigwait...");
107: r = sigwait(&waitset, &signo);
1.1.1.2 root 108: // 終了要求が立っていたら、どのシグナルでも、エラーが起きてても
109: // とにかく先にスレッドを終了する。
110: if (request_terminate) {
111: break;
112: }
1.1 root 113: if (r != 0) {
114: if (r == EINTR) {
115: continue;
116: }
117: putmsg(0, "sigwait: %s", strerror(r));
118: // XXX どうする?
119: continue;
120: }
121: putmsg(1, "sigwait got %d", signo);
122:
123: // ここからシグナル別の処理
124: switch (signo) {
1.1.1.3 ! root 125: case SIGTERM:
! 126: case SIGINT:
1.1 root 127: // メインスレッドから終了してもらう
128: UIMessage::Post(UIMessage::APPEXIT);
129: break;
130:
131: default: // 他は無視する
132: break;
133: }
134: }
135:
136: putmsg(1, "ThreadRun terminating");
137: }
138:
139: void
140: SignalThread::Terminate()
141: {
142: putmsg(1, "Terminate");
143:
144: // フラグを立ててシグナルで通知
145: request_terminate = true;
1.1.1.3 ! root 146: Kill(SIGTERM);
1.1 root 147: }
148:
149: // SignalThread にシグナルを投げる (他スレッドからも使用可)
150: void
151: SignalThread::Kill(int signo)
152: {
153: assert((bool)thread);
154:
155: putmsg(1, "Kill(%d)", signo);
156: pthread_kill(thread->native_handle(), signo);
157: }
158:
159: // SIG_BLOCK ならブロックする集合に追加 (=配送する)。
160: // SIG_UNBLOCK ならブロックする集合から削除 (=こちらで処理する)。
161: void
162: SignalThread::Mask(int how, int signo)
163: {
164: const char *howstr = (how == SIG_BLOCK) ? "BLOCK" : "UNBLOCK";
165:
166: putmsg(1, "Mask(%s, %d)", howstr, signo);
167:
168: // シグナルハンドラを設定 (シグナルハンドラはプロセスグローバル)
169: // signal(3) は挙動が違う可能性があるので使わないこと。
170: struct sigaction act;
171: memset(&act, 0, sizeof(act));
172: if (how == SIG_BLOCK) {
173: act.sa_handler = signal_handler;
174: } else {
175: act.sa_handler = SIG_DFL;
176: }
177: if (sigaction(signo, &act, NULL) < 0) {
178: putmsg(0, "sigaction(%d, %s): %s", signo, howstr, strerror(errno));
179: return;
180: }
181:
182: // このスレッドのマスクを設定
183: sigset_t set;
184: sigemptyset(&set);
185: sigaddset(&set, signo);
186: if (pthread_sigmask(how, &set, NULL) < 0) {
187: putmsg(0, "pthread_sigmask(%s, %d): %s",
188: howstr, signo, strerror(errno));
189: return;
190: }
191:
192: // sigwait() 用のセットを更新
193: if (how == SIG_BLOCK) {
194: sigaddset(&waitset, signo);
195: } else {
196: sigdelset(&waitset, signo);
197: }
198:
199: // 適当デバッグ表示
200: if (loglevel >= 1) {
201: std::string str;
202:
203: #define CHECK(NAM) do { \
204: if (sigismember(&waitset, __CONCAT(SIG,NAM))) { \
205: str += " "; \
206: str += #NAM; \
207: } \
208: } while (0)
209: CHECK(HUP);
210: CHECK(INT);
211: CHECK(PIPE);
212: CHECK(TERM);
213: CHECK(USR1);
214: CHECK(USR2);
215:
216: putmsgn("waitset =%s", str.c_str());
217: }
218: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.