Не сохраняя список текущих потоков, я пытаюсь увидеть, что сигнал в реальном времени доставляется всем потокам в моем процессе. Моя идея такова:
- Первоначально обработчик сигнала установлен, и сигнал разблокирован во всех потоках.
- Когда один поток хочет отправить сигнал «широковещания», он получает мьютекс и устанавливает глобальный флаг о том, что вещание происходит.
- Отправитель блокирует сигнал (используя
pthread_sigmask
) для себя и входит в цикл, неоднократно вызывая raise(sig)
, пока sigpending
не покажет, что сигнал находится в состоянии ожидания (не было потоков, остающихся с заблокированным сигналом).
- Когда потоки получают сигнал, они воздействуют на него, но ожидают в обработчике сигнала очистки флага широковещания, чтобы сигнал оставался маскированным.
- Отправитель завершает цикл, разблокируя сигнал (чтобы получить собственную доставку).
- Когда отправитель обрабатывает собственный сигнал, он сбрасывает глобальный флаг, чтобы все остальные потоки могли продолжить свою работу.
Проблема, с которой я сталкиваюсь, заключается в том, что pthread_sigmask
не соблюдается. Все работает правильно, если я запускаю тестовую программу под strace
(предположительно, из-за разного времени планирования), но как только я запускаю ее в одиночку, отправитель получает свой собственный сигнал (несмотря на то, что заблокировал его ...?) И ни один из другие темы когда-либо назначаются.
Есть идеи, что может быть не так? Я попытался использовать sigqueue
вместо raise
, проверяя маску сигналов, добавляя sleep
повсюду, чтобы убедиться, что потоки терпеливо ждут своих сигналов и т. Д., И теперь я в растерянности.
Редактировать: Благодаря ответу psmears, я думаю, я понимаю проблему. Вот потенциальное решение. Обратная связь была бы отличной:
- В любой момент времени я могу знать количество запущенных потоков и, если нужно, могу запретить создание и завершение всех потоков во время широковещательного сигнала.
- Поток, который хочет выполнить широковещательный сигнал, получает блокировку (так что никакой другой поток не может сделать это одновременно), затем блокирует сигнал для себя и отправляет
num_threads
сигналы процессу, затем разблокирует сигнал для себя.
- Обработчик сигнала атомарно увеличивает счетчик, и каждый экземпляр обработчика сигнала ожидает, пока этот счетчик не станет равным
num_threads
для возврата.
- Поток, который выполнил трансляцию, также ждет, пока счетчик достигнет
num_threads
, затем снимает блокировку.
Одной из возможных проблем является то, что сигналы не будут помещаться в очередь, если ядру не хватает памяти (похоже, в Linux эта проблема существует). Знаете ли вы, если sigqueue
достоверно информирует вызывающего абонента, когда он не может поставить в очередь сигнал (в этом случае я буду зацикливаться до тех пор, пока он не преуспеет), или сигналы могут быть незаметно потеряны?
Редактировать 2: Кажется, сейчас работает. Согласно документации для sigqueue
, он возвращает EAGAIN
, если не удается поставить в очередь сигнал. Но для надежности я решил просто продолжать вызывать sigqueue
до тех пор, пока не будут запущены обработчики сигналов * 1054, чередуя вызовы на sched_yield
после того, как я отправил num_threads-1
сигналов.
Во время создания потока было условие гонки, считая новые потоки, но я решил это странным (ab) использованием блокировок чтения-записи. Создание потока - «чтение», а широковещательный сигнал - «запись», поэтому, если поток не пытается передать, он не создает никакого конфликта при создании потока.