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