Когда отмена потока POSIX не является немедленной?

POSIX определяет два типа для типа отмены потока: PTHREAD_CANCEL_ASYNCHRONOUS и PTHREAD_CANCEL_DEFERRED (устанавливается pthread_setcanceltype(3)), определяя, когда pthread_cancel(3) должен вступить в силу. Насколько я читал, страницы руководства POSIX мало что говорят об этом, но страница руководства Linux говорит следующее о PTHREAD_CANCEL_ASYNCHRONOUS:

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

Мне любопытно, что означает система не гарантирует этого. Я легко могу представить, как это происходит в многоядерных/многопроцессорных системах (до переключения контекста). А как насчет одноядерных систем:

  1. Можем ли мы, чтобы поток не был отменен немедленно, когда запрашивается отмена, и отмена включена (pthread_setcancelstate(3)), а тип отмены установлен на PTHREAD_CANCEL_ASYNCHRONOUS?
  2. Если да, то при каких условиях это могло произойти?

Меня в основном интересует Linux (LinuxThreads / NPTL), но также и более общий способ просмотра этого бизнеса отмены, совместимый со стандартом POSIX.

Обновление/пояснение: здесь реальной практической проблемой является использование ресурсов, которые уничтожаются сразу после вызова pthread_cancel(), когда для целевого потока включена отмена и установлен тип PTHREAD_CANCEL_ASYNCHRONOUS!!! Итак, дело в следующем: есть ли хоть малейшая возможность для отмененного потока в этом случае продолжать нормально работать после переключения контекста (даже в течение очень короткого времени)?

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

Обновление-2: я ответил на свой собственный вопрос, указав, что это серьезная проблема и что базовая структура программы должна рассматриваться на принципиально другом концептуальном уровне. Я хочу, чтобы этот «неправильный» вопрос был полезен для других, интересующихся тайнами асинхронной отмены.


person FooF    schedule 18.07.2013    source источник


Ответы (2)


Смысл именно в том, что он говорит: это не гарантированно произойдет мгновенно. Причина этого в том, что необходима определенная «свобода» в отношении деталей реализации, которая учитывается в стандарте.

Например, в Linux/NPTL отмена осуществляется путем отправки сигнала nr. 32. Поток отменяется при получении сигнала, что обычно происходит при следующем переключении ядра на пользователя, или при следующем прерывании, или в конце кванта времени (что может случайно быть немедленно, но обычно это не так). Однако сигнал никогда не принимается, пока поток не запущен. Итак, настоящая загвоздка здесь в том, что сигналы не обязательно принимаются немедленно.

Если подумать, то и по-другому сделать тоже невозможно. Поскольку вы можете phtread_cleanup_push некоторые обработчики, которые должна выполнять операционная система (она не может просто уничтожить поток!), поток должен обязательно запуститься для отмены. Нет никакой гарантии, что какой-либо конкретный поток (включая тот, который вы хотите отменить) запущен в точное время, когда вы отменяете поток, поэтому нет гарантии, что он будет отменен немедленно.
За исключением, конечно, гипотетического случая, если ОС была реализована таким образом, чтобы блокировать вызывающий поток и планировать подлежащий отмене поток, чтобы он выполнял свои обработчики и только после этого разблокировал pthread_cancel. Но поскольку pthread_cancel не указано как блокирующее, это было бы совершенно неприятным сюрпризом. Это также было бы несколько неприемлемо из-за вмешательства в ограничения времени выполнения и справедливости планировщика.

Итак, либо ваш тип отмены «отключить», тогда ничего не происходит. Или это «включить», а тип отмены «отложен», тогда поток отменяется при вызове функции, которая указана как точка отмены в pthreads(7).
Или это «асинхронно», тогда, как указано выше, ОС сделает «что-то», чтобы отменить поток, как только сочтет это целесообразным — не в точное, четко определенное время, а «скоро». В случае Linux, отправив сигнал.

person Damon    schedule 18.07.2013
comment
Теперь я также проверяю LinuxThreads внутри uClibc, на который нацелена моя кросс-компиляция, и там используется тот же метод реализации. Спасибо за отличный продуманный ответ. Отмена треда - некрасивое дело. - person FooF; 18.07.2013
comment
На самом деле довольно очевидно, что поток не может быть уничтожен до переключения контекста, и что переключение контекста не может произойти произвольно сразу после вызова pthread_cancel(). Более суженный и близкий к практическому ответ: когда получен сигнал и запущен обработчик сигнала относительно следующего переключения контекста (в unicore-архитектуре, где у нас не может быть реального параллелизма)? - person FooF; 18.07.2013
comment
Это тоже есть в ответе (2-й абзац). Сигналы (если только они не генерируются аппаратно, как, например, «SIGSEGV», они принимаются сразу же по мере их возникновения) принимаются переключателем пользовательского режима ядра, или когда задача запланирована, или когда происходит прерывание. Это объясняется в книге Майкла Керриска где-то под сигналами. Однако сигналы — это только детали реализации, специфичные для Linux, а не общий ответ в отношении потоков. - person Damon; 19.07.2013
comment
Спасибо за разъяснения по поводу доставки сигнала. Давно уже хотел купить эту книгу... Может пора. - person FooF; 19.07.2013
comment
На одноядерных машинах без истинного параллелизма кажется, что отмена на самом деле начнет проявляться сразу после того, как целевой поток снова запустится (когда задача запланирована) — при условии, что режим асинхронной отмены включен. - person FooF; 19.07.2013

Если вам нужно задаться вопросом, когда произойдет асинхронная отмена, вы делаете что-то ужасно неправильное.

  1. Следование стандартам. Вы грызете землю под ногами, преднамеренно создавая или позволяя существовать коду, правильность которого зависит от предположений о платформе (одно ядро, конкретная реализация и т. д.). Почти всегда лучше, если это возможно, следовать стандартам (и четко документировать, когда это невозможно). Само название PTHREAD_CANCEL_ASYNCHROUNOUS предполагает значение асинхронный, которое отличается от немедленного или даже почти немедленного. В оригинальном плакате конкретно указано одно ядро, но почему вы должны допускать существование кода, который будет ломаться недетерминированным образом, когда ваш код запускается на действительно параллельных машинах (многоядерных или ЦП), где практически невозможно иметь гарантию немедленности (для этого потребуется остановить работу других ядер или дождаться переключения контекста или какого-либо другого ужасного взлома, который ваша ОС / ЦП не будет поддерживать для поддержки ваших нетрадиционных желаний). Режим асинхронной отмены потока не предназначен для гарантированной немедленной отмены потока. Следовательно, использовать их таким образом ужасно запутанно, даже если это сработает.

  2. Асинхронная безопасность. Если вас беспокоит механизм асинхронной отмены, возникает подозрение, что рассматриваемые потоки (из-за отсутствия независимости), возможно, не являются чисто вычислительными. или написаны в стиле асинхронно-отмена-безопасно.

    POSIX определяет только три функции как безопасные для асинхронной отмены: pthread_cancel(3), pthread_setcancelstate(3) и pthread_setcancelmode(3) — см. IEEE Std 1003.1, издание 2013 г., 2.9.5. Этот режим отмены подходит только для чисто вычислительных задач, которые не вызывают (кроме чисто вычислительных) библиотечных функций; такой код не предоставил бы точки отмены, если бы потоки были настроены на выполнение в режиме отложенной отмены по умолчанию. Отсюда обоснование определения такого режима.

    Можно написать безопасный для асинхронной отмены код, отключив отмену во время критических секций. Но авторы библиотек (включая разработчиков библиотек POSIX) в целом не должны заботиться об асинхронной безопасности по причинам следования общему соглашению, избежания сложности и даже снижения производительности. Поскольку авторам библиотек все равно, вы никогда не должны ожидать асинхронной безопасности, если явно не указано иное.

    Если ваш код не является асинхронно-безопасным (например, из-за вызова других библиотек, включая POSIX/стандартные библиотеки C, без временного отключения отмены или изменения режима отмены) и происходит асинхронная отмена, вы можете утечь ресурсы (память и т. д.), оставить несогласованные состояния и заблокированные мьютексы, блокирующие другие потоки, и вызывают множество других проблем, которые в настоящее время вообразимы и невообразимы. (Если вы пишете на C++, похоже, у вас будут другие проблемы, связанные с отменой потока POSIX, тесно связанной с обработкой исключений.)

person FooF    schedule 27.07.2013