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

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

unix.superglobalmegacorp.com

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