четверг, 20 января 2011 г.

Понимание последствий WAIT_ABANDONED

Это перевод Understanding the consequences of WAIT_ABANDONED. Автор: Реймонд Чен.

Одним важным отличием мьютексов от всех остальных объектов синхронизации является то, что мьютексы имеют владельца. Если поток, который владеет мьютексом, завершается без освобождения мьютекса, то мьютекс автоматически освобождается.

Но если это произошло - то у вас большие проблемы.

Одной из вещей, на которых спотыкаются люди, является возвращаемое значение WAIT_ABANDONED от функций ожидания вроде WaitForSingleObject. Обычно люди рассматривают это значение как успешное, потому что оно означает, что мьютекс был захвачен. Но оно также говорит вам, что предыдущий владелец не освобождал мьютекс и что мьютекс был освобождён системой автоматически.

Почему у вас появляются большие проблемы в этом случае?

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

Например, у вас может быть код, который управляет doubly-linked список в разделяемой памяти:
procedure TMyClass.ReverseList;
var
  i, next: Integer;
begin
  WaitForSingleObject(hMutex, INFINITE);
  i := 0; // индекс элемента
  repeat
    next := m_items[i].m_next;
    m_items[i].m_next := m_items[i].m_prev;
    m_items[i].m_prev := next;
    i := next;
  until (i = 0);
  ReleaseMutex(hMutex);
end;
Тут нет ничего особенно интересного. Обычные вещи, да?

Но что если программа вылетит, пока этот код владеет мьютексом? (даже если вы считаете, что этот код безупречен - рассмотрите сценарий когда программа запущена по сети: вы выдёргиваете сетевой шнур и программа вылетает с исключением EXCEPTION_IN_PAGE_ERROR - потому что ОС не может загрузить следующую страницу кода. Да даже просто пользователь открыл Диспетчер Задач и закрыл вашу программу во время выполнения этой функции).

В этом случае мьютекс автоматически освобождается ОС, но связанный список при этом будет в несогласованном состоянии. Следующий же код, который затребует мьютекс, получит код возврата WAIT_ABANDONED. Если вы проигнорируете этот код возврата, посчитав его успехом, то вы будете работать с испорченным списком. В зависимости от того, как вы используете список, вы можете получить утечку ресурсов, ошибочное создание копии чего-нибудь или даже вылет. Пример как неудачное завершение одной программы привело к проблемам в другой.

Итак, вопрос: "Что вы сделаете, если вы получили WAIT_ABANDONED?" Ответ: "Хороший вопрос".

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

Или вы можете просто игнорировать ошибку и продолжать работу с испорченной структурой, надеясь, что чтобы ни произошло ранее - это не приведёт к куче каскадных ошибок. Это то, что делает абсолютное большинство людей - хотя обычно они даже не осознают это. А отлаживать вылет программы из-за такого дизайна - очень тяжело.

Упражнение: почему мы используем индексы вместо указателей для связывания записей в связанном списке?

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

Отправить комментарий

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

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

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

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

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