|
|
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: {
1.1.1.4 ! root 99: SetThreadAffinityHint(AffinityClass::Light);
! 100:
1.1 root 101: request_terminate = false;
102:
103: for (;;) {
104: int signo;
105: int r;
106:
107: // sigwait() は成功なら 0、失敗ならエラーコードを返す。
108: putmsg(2, "sigwait...");
109: r = sigwait(&waitset, &signo);
1.1.1.2 root 110: // 終了要求が立っていたら、どのシグナルでも、エラーが起きてても
111: // とにかく先にスレッドを終了する。
112: if (request_terminate) {
113: break;
114: }
1.1 root 115: if (r != 0) {
116: if (r == EINTR) {
117: continue;
118: }
119: putmsg(0, "sigwait: %s", strerror(r));
120: // XXX どうする?
121: continue;
122: }
123: putmsg(1, "sigwait got %d", signo);
124:
125: // ここからシグナル別の処理
126: switch (signo) {
1.1.1.3 root 127: case SIGTERM:
128: case SIGINT:
1.1 root 129: // メインスレッドから終了してもらう
130: UIMessage::Post(UIMessage::APPEXIT);
131: break;
132:
133: default: // 他は無視する
134: break;
135: }
136: }
137:
138: putmsg(1, "ThreadRun terminating");
139: }
140:
141: void
142: SignalThread::Terminate()
143: {
144: putmsg(1, "Terminate");
145:
146: // フラグを立ててシグナルで通知
147: request_terminate = true;
1.1.1.3 root 148: Kill(SIGTERM);
1.1 root 149: }
150:
151: // SignalThread にシグナルを投げる (他スレッドからも使用可)
152: void
153: SignalThread::Kill(int signo)
154: {
155: assert((bool)thread);
156:
157: putmsg(1, "Kill(%d)", signo);
158: pthread_kill(thread->native_handle(), signo);
159: }
160:
161: // SIG_BLOCK ならブロックする集合に追加 (=配送する)。
162: // SIG_UNBLOCK ならブロックする集合から削除 (=こちらで処理する)。
163: void
164: SignalThread::Mask(int how, int signo)
165: {
166: const char *howstr = (how == SIG_BLOCK) ? "BLOCK" : "UNBLOCK";
167:
168: putmsg(1, "Mask(%s, %d)", howstr, signo);
169:
170: // シグナルハンドラを設定 (シグナルハンドラはプロセスグローバル)
171: // signal(3) は挙動が違う可能性があるので使わないこと。
172: struct sigaction act;
173: memset(&act, 0, sizeof(act));
174: if (how == SIG_BLOCK) {
175: act.sa_handler = signal_handler;
176: } else {
177: act.sa_handler = SIG_DFL;
178: }
179: if (sigaction(signo, &act, NULL) < 0) {
180: putmsg(0, "sigaction(%d, %s): %s", signo, howstr, strerror(errno));
181: return;
182: }
183:
184: // このスレッドのマスクを設定
185: sigset_t set;
186: sigemptyset(&set);
187: sigaddset(&set, signo);
188: if (pthread_sigmask(how, &set, NULL) < 0) {
189: putmsg(0, "pthread_sigmask(%s, %d): %s",
190: howstr, signo, strerror(errno));
191: return;
192: }
193:
194: // sigwait() 用のセットを更新
195: if (how == SIG_BLOCK) {
196: sigaddset(&waitset, signo);
197: } else {
198: sigdelset(&waitset, signo);
199: }
200:
201: // 適当デバッグ表示
202: if (loglevel >= 1) {
203: std::string str;
204:
205: #define CHECK(NAM) do { \
206: if (sigismember(&waitset, __CONCAT(SIG,NAM))) { \
207: str += " "; \
208: str += #NAM; \
209: } \
210: } while (0)
211: CHECK(HUP);
212: CHECK(INT);
213: CHECK(PIPE);
214: CHECK(TERM);
215: CHECK(USR1);
216: CHECK(USR2);
217:
218: putmsgn("waitset =%s", str.c_str());
219: }
220: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.