понедельник, 10 сентября 2012 г.

Как безопасно отправить SIGINT в главный поток?

Это перевод How can SIGINT be safely delivered on the main thread? Автор: Реймонд Чен.

Комментатор AnotherMatt интересуется, почему консольные программы Win32 получают уведомления в фоновом потоке. Почему бы не отправлять их в главный поток?

Вообще-то, позвольте мне задать обратный вопрос: почему Unix отправляет уведомления в главный поток? Ведь это делает почти невозможным сделать в обработчике хоть что-то полезное. Код главного потока в момент вызова уведомления может находится внутри вызова функций менеджера памяти (удерживая блокировку критической секции). И если обработчик попытается выполнить любую операцию, требующую выделения памяти, то он просто заблокирует сам себя (если вам повезло) или испортит данные в куче (если вам не повезло).

К примеру, посмотрите на такой обработчик:
procedure catch_int(sig_num: Integer); cdecl;
begin
  // Переустанавливаем обработчик для следующего уведомления
  signal(SIGINT, catch_int);
  // и выводим сообщение
  WriteLn('Don''t do that');
  Flush(Output);
end;
Что произойдёт, если обработчик будет вызван как раз в тот момент, когда основная программа выполняла вызов Flush, скажем, когда она сбросила половину буфера? Если бы второй вызов был сделан из второго потока, то второй вызов ожидал бы завершения первого (многопоточная синхронизация), но в данном случае оба вызова делает один и тот же поток. Второй вызов не может ожидать завершения работы первого, потому что первый вызов не может завершиться, пока не отработал второй!

(Заметьте, что такой обработчик также может потенциально менять errno (аналог GetLastError), что приведёт к "невозможным" ошибкам в главном коде).

Win32 не использует модель асинхронного прерывания пользовательского кода другим пользовательским кодом, потому что при этом невозможно достоверно судить о состоянии процесса. Доставка консольных уведомлений в фоновый поток означает, что если второй поток попытается получить доступ к куче или иному ресурсу, занятому главным потоком, то он просто будет покорно ожидать, когда главный поток завершит работу с ресурсом, прежде чем изменять его.

Прим.пер.: см. также.

1 комментарий:

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.