Состояние гонки в pthread_kill()?

#linux #pthreads #posix

#linux #pthreads #posix

Вопрос:

Руководство по Linux для pthread_kill() содержит следующий абзац:

POSIX.1-2008 рекомендует, чтобы, если реализация обнаруживает использование идентификатора потока после окончания его жизненного цикла, pthread_kill() должен возвращать ошибку ESRCH. Реализация glibc возвращает эту ошибку в тех случаях, когда может быть обнаружен недопустимый идентификатор потока. Но обратите внимание также, что POSIX говорит, что попытка использовать идентификатор потока, время жизни которого закончилось, приводит к неопределенному поведению, а попытка использовать недопустимый идентификатор потока при вызове pthread_kill() может, например, вызвать ошибку сегментации.

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

Как убедиться, что идентификатор потока будет действительным?

Комментарии:

1. Чтобы быть точным, поток должен быть завершен и либо присоединен, либо отсоединен, чтобы его теперь недействительный идентификатор потока представил эту гонку. В строгом языке posxy завершенный поток все еще находится в пределах своего жизненного цикла и просто «неактивен» (больше не запущен, но еще не отсоединен / присоединен). Тем не менее, хороший вопрос.

2. between checking the thread ID is valid and issuing the pthread_kill(), the thread might have terminated Вы имеете в виду внутренний pthread_kill вызов? Это просто — pthread_kill имеет внутреннюю глобальную блокировку, поэтому этого не происходит, нет?

3. @KamilCuk Не внутри pthread_kill , а между моим if , который решает вызвать pthread_kill , и самим вызовом.

4. Зачем вам это проверять? Просто позвонить pthread_kill ?

5. Обычно что-то срабатывает pthread_kill при идентификаторе потока, который, по моим данным, является действительным. Но между запуском и вызовом мог завершиться отдельный поток.

Ответ №1:

Состояние гонки в pthread_kill()?

Когда поток отсоединяется, всегда. Но если идентификатор потока действителен, нет.

Является ли использование pthread_kill() изначально небезопасным, поскольку всегда существует условие гонки, которое может привести к неопределенному поведению?

Нет, не всегда.

Как убедиться, что идентификатор потока будет действительным?

Из идентификатора потока POSIX:

Время жизни идентификатора потока заканчивается после завершения потока, если он был создан с атрибутом detachstate, установленным в PTHREAD_CREATE_DETACHED, или если для этого потока была вызвана функция pthread_detach() или pthread_join() .

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

Как правило, вы должны прекратить использовать идентификатор потока после pthread_detach или pthread_join . Это как free() в malloc() — вы не можете использовать память, выделенную malloc() after free() . Точно так же, как вы не можете использовать идентификатор потока после отсоединения или присоединения, идентификатор потока просто становится недействительным. Просто с pthread_detach ним становится недействительным «позже», но вы не знаете, когда, поэтому вы все равно не сможете его использовать (ну, если вы не напишете свою собственную синхронизацию). Оно может стать недействительным сразу после вызова pthread_detach . Если вы намереваетесь что-либо сделать с идентификатором потока, не отсоединяйте и не присоединяйте его.

Вызов pthread_kill с «неактивным потоком» (неотделенный не присоединенный поток, который завершился) действителен — идентификатор потока все еще действителен. Мы можем читать из pthread_kill posix:

Существующие реализации зависят от результата выполнения pthread_kill() с идентификатором потока, указывающим на неактивный поток (завершенный поток, который не был отсоединен или присоединен). Некоторые указывают на успех такого вызова, в то время как другие выдают ошибку [ESRCH]. Поскольку определение времени жизни потока в этом томе POSIX.1-2017 охватывает неактивные потоки, ошибка [ESRCH], как описано, в этом случае неуместна. В частности, это означает, что приложение не может иметь проверку одного потока на завершение другого с помощью pthread_kill() .

ДАЛЬНЕЙШИЕ НАПРАВЛЕНИЯ

Будущая версия этого стандарта может потребовать, чтобы pthread_kill() не выполнялся с ошибкой [ESRCH] в случае отправки сигналов в неактивный поток (завершенный поток, который еще не отсоединен или не присоединен), даже если сигнал не будет доставлен, поскольку поток больше не выполняется.

FUTURE DIRECTIONS Похоже, что он предпочитает, чтобы pthread_kill() с неактивным потоком просто выполнялся и возвращал 0. Мне лично нравится ESRCH ошибка в таком случае.

Ответ №2:

Как убедиться, что идентификатор потока будет действительным?

Вы должны перепроектировать так, чтобы ваш код знал это априори.* Все, что не соответствует этому, является гонкой TOCTOU (CWE-367).

К счастью, существует много предшествующего уровня техники в области межпроцессорной kill обработки. Межпроцессная сигнализация не сопряжена с ужасающим риском неопределенного поведения, как pthread_kill это происходит, но осторожные программисты считают риск сигнализации переработанного PID столь же неприемлемым. (И идентификаторы потоков также могут быть переработаны.)

* Хорошо, вы могли бы сделать это, проверив какое-то надуманное состояние. Например, установите для флага, защищенного от мьютексов i_am_still_running , значение false в самом, самом конце вашей процедуры потока. Тогда только pthread_kill этот поток, удерживая мьютекс и подтверждая, что он все еще запущен. Фу.