--- nono/host/signalthread.cpp 2026/04/29 17:05:11 1.1.1.1 +++ nono/host/signalthread.cpp 2026/04/29 17:05:18 1.1.1.3 @@ -19,20 +19,11 @@ // ブロックするがペンディングにはなる、sigwait(3) はこのペンディングに反応 // する)。 // -// o SIGTERM を受け取ると UIMessage でメインスレッドに通知し、そこから安全に -// プロセスを終了させる。 -// -// o SIGINT(^C)、SIGTSTP(^Z)、SIGQUIT(^\) は HostCOM のうち stdio ドライバが -// 必要とするため動作を切り替える。HostCOM が stdio なら、これらのシグナル -// は処理せず、HostCOM スレッドにパイプで通知する。 -// HostCOM が stdio 以外なら、これらのシグナルは HostCOM に通知せず、 -// こちらで処理する。 +// o SIGTERM、SIGINT(^C) を受け取ると UIMessage でメインスレッドに通知し、 +// そこから安全にプロセスを終了させる。 // // o SIGHUP、SIGPIPE、SIGUSR1、SIGUSR2 はデフォルトがプロセス終了なので // ブロックして無視する。 -// このうち無難そうな SIGPIPE をこのスレッドへのメッセージ通知として使う。 -// SIGPIPE 発生時に特定のメンバ変数がセットされているとそれに従って -// アクションを起こす。 // // Linux (Ubuntu 20.04) ではシグナルの配送が POSIX とは異なっているらしく、 // プロセスに届いたシグナルを sigwait しているスレッドに配送してくれない。 @@ -42,30 +33,27 @@ #include "signalthread.h" #include "uimessage.h" -#include #include -// グローバル参照用 -SignalThread *gSignalThread; +static void signal_handler(int signo); -void signal_handler(int signo); -void +static void signal_handler(int signo) { #if defined(__linux__) // Linux ではプロセスに届いたシグナルを自動でスレッドに配送して // くれないので、自前でやる必要があるようだ。 - if (gSignalThread) { - gSignalThread->Kill(signo); + SignalThread *sigthread = GetSignalThread(); + if (sigthread) { + sigthread->Kill(signo); } #endif } // コンストラクタ SignalThread::SignalThread() - : inherited("SignalThread") + : inherited(OBJ_SIGNAL_THREAD) { - wpipe = -1; sigemptyset(&waitset); } @@ -73,25 +61,18 @@ SignalThread::SignalThread() SignalThread::~SignalThread() { TerminateThread(); - gSignalThread = NULL; } // 常にブロックするシグナル集合 static std::vector siglist_always = { SIGHUP, // ignore + SIGINT, // プロセス終了 SIGPIPE, // ignore SIGTERM, // プロセス終了 SIGUSR1, // ignore SIGUSR2, // ignore }; -// HostCOM が受け取るかどうかによって動作を変えるシグナル集合 -static std::vector siglist_com = { - SIGINT, // HostCOM (^C) - SIGQUIT, // HostCOM (^\) - SIGTSTP, // HostCOM (^Z) -}; - // スレッド開始 (override) // // SIGINT(^C) など緊急性の高いシグナルのブロック期間を極力短くするため、 @@ -106,9 +87,6 @@ SignalThread::StartThread() for (auto signo : siglist_always) { Mask(SIG_BLOCK, signo); } - for (auto signo : siglist_com) { - Mask(SIG_BLOCK, signo); - } // で、スレッドを開始。 return inherited::StartThread(); @@ -119,7 +97,6 @@ void SignalThread::ThreadRun() { request_terminate = false; - request_wpipe = -1; for (;;) { int signo; @@ -128,6 +105,11 @@ SignalThread::ThreadRun() // sigwait() は成功なら 0、失敗ならエラーコードを返す。 putmsg(2, "sigwait..."); r = sigwait(&waitset, &signo); + // 終了要求が立っていたら、どのシグナルでも、エラーが起きてても + // とにかく先にスレッドを終了する。 + if (request_terminate) { + break; + } if (r != 0) { if (r == EINTR) { continue; @@ -138,70 +120,14 @@ SignalThread::ThreadRun() } putmsg(1, "sigwait got %d", signo); - // 終了要求が立っていたら、どのシグナルでもスレッドを終了する - if (request_terminate) { - break; - } - // ここからシグナル別の処理 switch (signo) { - case SIGPIPE: // コマンド - { - // 本来は無視するだけなので、ついでにこのスレッドに対する - // メッセージ通知として使用する。フラグが立ってなければ単に無視。 - // - // SIGPIPE 発生時に request_wpipe が非負なら、HostCOM からの - // パイプ接続もしくは切断の要求。 - int req = request_wpipe.exchange(-1); - if (req >= 0) { - if (loglevel >= 1) { - if (req == INT_MAX) { - putmsg(1, "SIGPIPE: HostCOM del"); - } else { - putmsg(1, "SIGPIPE: HostCOM add %d", req); - } - } - // HostCOM スレッドへのシグナル通知を変更 - ChangeCOM(req); - } - break; - } - - case SIGTERM: // SIGTERM なら即終了 + case SIGTERM: + case SIGINT: // メインスレッドから終了してもらう UIMessage::Post(UIMessage::APPEXIT); break; - case SIGINT: // パイプがあれば配送、なければここで処理 - if (wpipe >= 0) { - WriteSignal(wpipe, signo); - } else { - // メインスレッドから終了してもらう - UIMessage::Post(UIMessage::APPEXIT); - } - break; - - case SIGTSTP: // パイプがあれば配送、なければデフォルト動作 - case SIGQUIT: - if (wpipe >= 0) { - // パイプがあれば書き込んで通知。 - WriteSignal(wpipe, signo); - } else { - // パイプがなければ、 - // ここでデフォルト動作を起こしたい。 - - // 一旦解除して.. - Mask(SIG_UNBLOCK, signo); - - // シグナルを自身に送信。 - Kill(signo); - - // そして再度ブロックしておく。 - // こうしないと、2回目の ^Z が効かないとかになる。 - Mask(SIG_BLOCK, signo); - } - break; - default: // 他は無視する break; } @@ -217,80 +143,7 @@ SignalThread::Terminate() // フラグを立ててシグナルで通知 request_terminate = true; - Kill(SIGPIPE); -} - -// HostCOM スレッドへのパイプを接続する (HostCOM スレッドから呼ばれる) -void -SignalThread::AddPipe(int fd) -{ - putmsg(1, "AddPipe(fd=%d)", fd); - - if ((bool)thread == false) { - // HostCOM から呼ばれるが、起動時の初期設定は Init() フェーズ内の - // ため、その時点ではまだどちらもスレッドはない。 - // この場合は単にここで設定するだけでよい。 - ChangeCOM(fd); - } else { - // スレッド開始後なら、フラグを立ててシグナルで通知 - request_wpipe = fd; - Kill(SIGPIPE); - } -} - -// HostCOM スレッドへのパイプを削除する -void -SignalThread::DeletePipe() -{ - putmsg(1, "DeletePipe(current=%d)", wpipe); - - // フラグを立ててシグナルで通知 - request_wpipe = INT_MAX; - Kill(SIGPIPE); -} - -// シグナルをパイプに再配送する -void -SignalThread::WriteSignal(int wfd, int signo) -{ - char buf[1]; - int r; - - buf[0] = signo; - r = write(wpipe, buf, sizeof(buf)); - if (r < 0) { - // エラーが起きたパイプは閉じればいいか… - ChangeCOM(INT_MAX); - } -} - -// HostCOM スレッドとのパイプを接続/切断する。 -// SignalThread 側処理。 -void -SignalThread::ChangeCOM(int reqfd) -{ - // reqfd が INT_MAX なら現在のディスクリプタを切断。 - // そうでなければ、reqfd を接続。 - // そんな高頻度で投げないので運用上は問題にならないはず。 - - putmsg(1, "ChangeCOM(current=%d request=%d)", wpipe, reqfd); - - if (reqfd == INT_MAX) { - // パイプを切断してからシグナルのブロックを解除 - close(wpipe); - wpipe = -1; - - for (auto signo : siglist_com) { - Mask(SIG_UNBLOCK, signo); - } - } else { - // 必要なシグナルをブロックしてからパイプを接続 - for (auto signo : siglist_com) { - Mask(SIG_BLOCK, signo); - } - - wpipe = reqfd; - } + Kill(SIGTERM); } // SignalThread にシグナルを投げる (他スレッドからも使用可) @@ -355,10 +208,8 @@ SignalThread::Mask(int how, int signo) } while (0) CHECK(HUP); CHECK(INT); - CHECK(QUIT); CHECK(PIPE); CHECK(TERM); - CHECK(TSTP); CHECK(USR1); CHECK(USR2);