Как сделать взаимоисключающие операции чтения или записи для глобальной переменной в обработчике сигналов и нормальной функции - PullRequest
2 голосов
/ 20 февраля 2020

Приложение многопоточное. Внутри main() я регистрирую обработчик сигнала для SIGUSR1:

// Global variable to indicate whether directory's 
// content needs to be reloaded
bool reload_dir = 0;

int main (int argc, char *argv[])
{
    ...

    signal(SIGUSR1, sigusr1_handler);

    ...


    RunServer(arg1, ...);
    return 0;
}

Обработчик сигнала:

static void
sigusr1_handler (int signo __unused)
{
    reload_dir = 1;
    return;
}

Следующая функция (которая вызывается из основного) выполняется только основным thread:

void
RunServer (arg1, ...)
{
    // do some stuffs
    ...

    while (cond != true) {
        sleep(1);
    }

    server_exit();
}

Теперь, когда SIGUSR1 перехватывается (любым потоком, включая основной поток), я устанавливаю переменную reload_dir равной 1. И в RunServer() я перезагружаю каталог на основе этого значения. Однако я также сбрасываю глобальную переменную reload_dir на 0, чтобы избежать повторной загрузки каталога бесконечно. И эта установка reload_dir в 0 приведет к гонке.

Поскольку мы не должны использовать блокировки или мьютекс в обработчике сигналов, как я могу добиться этого без существенного изменения существующего дизайна приложения.

void
RunServer (arg1, ...)
{
    // do some stuffs
    ...

    while (cond != true) {
        if (reload_dir) {
           // reset reload_dir to avoid loading repeatedly indefinitely 
           reload_dir = 0;   // Race condition?
           dir_reinit();
        }
        sleep(1);
    }

    server_exit();
}

1 Ответ

1 голос
/ 20 февраля 2020

Заблокируйте SIGUSR1 с помощью pthread_sigmask до того, как будут созданы все потоки, чтобы все потоки наследовали эту маску. Затем в main() используйте sigtimedwait вместо sleep в вашем основном l oop, чтобы проверить, доставлен ли USR1.

int main(...) {
    ...

    sigset_t ss_usr1;
    sigemptyset(&ss_usr1);
    sigaddset(&ss_usr1, SIGUSR1);

    // block SIGUSR1
    pthread_sigmask(SIG_BLOCK, &ss_usr1, NULL);

    ... spawn threads ...

    // RunServer
    struct timespec patience = { .tv_sec = 1 };

    while (! some_condition) {
        int s = sigtimedwait(&ss_usr1, NULL, &patience);
        if (s == SIGUSR1) dir_reinit();
        // handle errors other than timeout appropriately
    }
    server_exit();

    ...
}

Преимущества: нет сигнала сложность обработчика (и вы не будете справедливо наказаны за , используя signal вместо старшего sigaction; нет необходимости в атомах c flags или sig_atomic_t; проще рассуждать о поведении .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...