Annotation of nono/host/signalthread.cpp, revision 1.1.1.5

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.