3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

サブスレッドからメインスレッドにだけシグナルを送りたい

Last updated at Posted at 2015-12-09
a.cpp

# include <sys/types.h>
# include <signal.h>
# include <pthread.h>
# include <unistd.h>

# include <cstdio>
# include <cstring>
# include <mutex>
# include <thread>
# include <map>
# include <random>

typedef void(*Handler_func)(int);

void sig_handler(int) {
}

int check_signal() {
    sigset_t ss;
    sigemptyset(&ss);
    sigaddset(&ss, SIGTERM);
    sigaddset(&ss, SIGUSR1);
    
    timespec ts;
    ts.tv_sec = 0;
    ts.tv_nsec = 0;
    int sig = sigtimedwait(&ss, nullptr, &ts);
    if (sig > 0) {
        return sig;
    } else {
        if (errno != EAGAIN) {
            char buf[1024];
            char *bufp = strerror_r(errno, buf, 1024);
            printf("sigtimedwait fail. %s\n", bufp);
            return -1;
        }
    }
    return 0;
}

int set_sigprocmask() {
    sigset_t newss;
    sigemptyset(&newss);
    sigaddset(&newss, SIGCHLD);
    sigaddset(&newss, SIGUSR1);
    sigaddset(&newss, SIGALRM);
    sigaddset(&newss, SIGHUP);
    sigaddset(&newss, SIGINT);
    sigaddset(&newss, SIGQUIT);
    sigaddset(&newss, SIGTERM);
    if (sigprocmask(SIG_BLOCK, &newss, nullptr)) {
        return 1;
    } else {
        return 0;
    }
}

int set_sigaction(int sig, Handler_func func) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(struct sigaction));
    sa.sa_handler = func;
    sigemptyset(&sa.sa_mask);
    if (sigaction(sig, &sa, nullptr)) {
        return 1;
    } else {
        return 0;
    }
}

int set_sigaction_ign(int sig) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(struct sigaction));
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    if (sigaction(sig, &sa, nullptr)) {
        return 1;
    } else {
        return 0;
    }
}

int init_signal() {
    set_sigaction(SIGCHLD, &sig_handler);
    set_sigaction(SIGUSR1, &sig_handler);
    set_sigaction(SIGALRM, &sig_handler);
    set_sigaction(SIGHUP, &sig_handler);
    set_sigaction(SIGINT, &sig_handler);
    set_sigaction(SIGQUIT, &sig_handler);
    set_sigaction(SIGTERM, &sig_handler);
    set_sigaction_ign(SIGPIPE);
    if (set_sigprocmask()) {
        return 1;
    } else {
        return 0;
    }
}

class th_info {
    public:
        std::thread th;
        pthread_t main_pthread_t;
};

void thread_func(std::mutex &mtx, std::map<int, th_info> &ths, int num) {
    // 親の生み出し処理中は止まる
    mtx.lock();
    mtx.unlock();
    
    std::random_device rd;
    std::mt19937 mt(rd());
    std::uniform_int_distribution<int> wait_time(1, 10);
    
    while (!check_signal()) {
        sleep(wait_time(mt));
        printf("[%d]: send SIGUSR1.\n", num);
        mtx.lock();
        pthread_kill(ths[num].main_pthread_t, SIGUSR1);
        mtx.unlock();
    }
}

void main_thread(std::mutex &mtx, std::map<int, th_info> &ths) {
    sigset_t ss;
    sigemptyset(&ss);
    sigaddset(&ss, SIGHUP);
    sigaddset(&ss, SIGTERM);
    sigaddset(&ss, SIGINT);
    sigaddset(&ss, SIGQUIT);
    sigaddset(&ss, SIGUSR1);
    while (1) {
        int sig;
        sigwait(&ss, &sig);
        if (sig == SIGUSR1) {
            printf("recv SIGUSR1.\n");
        } else {
            printf("recv FINISH SIGNAL.\n");
            break;
        }
    }
    // サブスレッドを回収する
    mtx.lock();
    for (auto &it : ths) {
        pthread_kill(it.second.th.native_handle(), SIGUSR1);
    }
    mtx.unlock();
    for (auto &it : ths) {
        it.second.th.join();
    }
}

void gen_thread(std::mutex &mtx, std::map<int, th_info> &ths) {
    pthread_t main_pthread_t = pthread_self();
    mtx.lock();
    for (int i = 0; i < 3; ++i) {
        std::thread th(thread_func, std::ref(mtx), std::ref(ths), i);
        ths[i].th = std::move(th);
        ths[i].main_pthread_t = main_pthread_t;
    }
    mtx.unlock();
}

int main(int, char **) {
    if (init_signal()) {
        printf("init_signal fail.\n");
        return 1;
    }
    
    std::mutex mtx;
    std::map<int, th_info> ths;
    
    gen_thread(mtx, ths);
    main_thread(mtx, ths);
    
    return 0;
}


[root@localhost ~]# g++ --std=c++11 a.cpp -lpthread
[root@localhost ~]#
[root@localhost ~]# ./a.out
[1]: send SIGUSR1.
recv SIGUSR1.
[2]: send SIGUSR1.
recv SIGUSR1.
[0]: send SIGUSR1.
recv SIGUSR1.
[1]: send SIGUSR1.
recv SIGUSR1.
[2]: send SIGUSR1.
recv SIGUSR1.
[2]: send SIGUSR1.
recv SIGUSR1.
[0]: send SIGUSR1.
recv SIGUSR1.
^Crecv FINISH SIGNAL.
[1]: send SIGUSR1.
[2]: send SIGUSR1.
[0]: send SIGUSR1.
[root@localhost ~]#

スレッドを生み出す前に、メインスレッドでpthread_self()を実行して、
それを保存しておけば良い。

サブスレッド側で、メインスレッドに何かを通知したい場合、
pthread_kill(main_pthread_t, SIGUSR1);
という具合に通知できる。

シグナル関連の設定を、あらかじめハンドラをセットした上で
ブロックしておかないと、うまく動かない。

スレッドにした時点で、可能な限りsigwait等を使って、
ハンドラが動作しないようにする。
(なので、プログラム終了時に、サブスレッドが単にsleepで寝ているが、
 これらは割り込み処理で抜けることは無く、渡した秒数待ってから終了する)

ひとまず動作するコードにすると、なんだか長くなる・・・。うーん。

2015/12/10 追記

どうにも動作見ていると、メインスレッドに対してシグナルを送ると、
他のスレッドも受信している・・・。

メインスレッドだけがsigwaitでシグナルを捕捉すればよいけど、
他のスレッドもsigwaitで待っていると、
メインスレッドも他のスレッドも、sigwaitから返ってきてしまう・・・。

うーん、サブスレッド -> メインスレッドに送るシグナルを別途決めて、
サブスレッド側で、そのシグナルを待つようなことをしなければ解決だけど。。

もっとスマートに、決めうちでメインスレッドにだけ、シグナルを送れないものかな・・・。

、、、pthread_killでシグナル送るために、メインスレッドのpthread_tを保存したけど、
結局のところ、kill(メインスレッドのpid, SIGUSR1);という処理と同等な模様。

うーん。

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?