Введение

Когда поведение системы зависит от времени наступления неконтролируемых событий, возникает состояние гонки или гоночная опасность. Это состояние в электронике, программном обеспечении или другой системе, при котором её фактическое поведение зависит от последовательности или времени неконтролируемых событий, что приводит к непредсказуемым или противоречивым результатам. Состояние гонки становится ошибкой, если одно или несколько из возможных вариантов поведения нежелательны. Термин "состояние гонки" уже использовался в 1954 году, например, в докторской диссертации Дэвида А. Хаффмана "Синтез последовательных коммутационных схем". Состояния гонки особенно часто возникают в логических схемах, многопоточных или распределённых программных системах.

В электронике

Типичный пример состояния гонки может возникнуть, когда логический элемент объединяет сигналы, прошедшие по разным путям от одного источника. Входы элемента могут изменяться в немного разное время в ответ на изменение исходного сигнала. Выход может на короткий промежуток времени перейти в нежелательное состояние, прежде чем стабилизироваться в заданном. Некоторые системы могут допускать такие сбои, но если этот выход используется в качестве тактового сигнала для других систем, содержащих память, например, система может быстро отклониться от заданного поведения (фактически, временный сбой превращается в постоянный). Рассмотрим, например, двухвходовое И-элемент, на входы которого подается следующая логика: логический сигнал на одном входе и его отрицание (¬ – логическое отрицание) на другом входе теоретически никогда не выдает истинное значение. Однако, если изменения значения задерживаются при распространении на второй вход по сравнению с первым, когда значение меняется с ложного на истинное, то наступит короткий период, в течение которого оба входа будут истинными, и, следовательно, выход элемента также будет истинным. Практический пример состояния гонки может возникнуть при использовании логической схемы для обнаружения определенных выходов счетчика. Если все биты счетчика не изменяются одновременно, то могут возникнуть промежуточные состояния, которые могут привести к ложным срабатываниям.

Критические и некритические формы

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

Статические, динамические и существенные формы

Статическое условие гонки возникает при объединении сигнала и его инверсии. Динамическое условие гонки возникает, когда это приводит к множественным переключениям вместо одного запланированного. Они вызваны взаимодействием между логическими элементами. Оно может быть устранено путем использования не более двух каскадов логических элементов. Существенное условие гонки возникает, когда входной сигнал имеет два фронта за время, меньшее общей задержки обратной связи. Иногда для их устранения используются линии задержки на индуктивностях, эффективно увеличивающие длительность входного сигнала.

Обходные пути

Методы проектирования, такие как карты Карно, стимулируют разработчиков выявлять и устранять гонки (race conditions) до того, как они приведут к сбоям. Часто для устранения некоторых видов гонок можно добавить логическое резервирование. Помимо этих проблем, некоторые логические элементы могут переходить в метастабильные состояния, что создает дополнительные трудности для разработчиков схем.

В программном обеспечении

Состояние гонки может возникнуть в программном обеспечении, когда компьютерная программа имеет несколько путей выполнения, которые выполняются одновременно. Если эти пути выполнения занимают не то время, которое ожидалось, они могут завершиться в другом порядке, чем предполагалось, что может привести к ошибкам в программном обеспечении из-за непредсказуемого поведения. Гонка также может возникать между двумя программами, что приводит к проблемам безопасности (см. ниже). Критические состояния гонки приводят к некорректному выполнению и ошибкам программного обеспечения. Критические состояния гонки часто возникают, когда процессы или потоки зависят от общего состояния. Операции с общим состоянием выполняются в критических секциях, которые должны быть взаимоисключающими. Нарушение этого правила может привести к повреждению общего состояния. Гонка данных — это один из видов состояния гонки. Гонки данных являются важной частью различных формальных моделей памяти. Модель памяти, определенная в стандартах C11 и C++11, указывает, что программа на C или C++ с гонкой данных имеет неопределенное поведение. Состояние гонки может быть трудно воспроизвести и отладить, поскольку конечный результат недетерминирован и зависит от относительного времени выполнения конфликтующих потоков. Проблемы такого рода могут исчезать при запуске в режиме отладки, добавлении дополнительной журнализации или подключении отладчика. Ошибка, которая исчезает таким образом при попытках отладки, часто называется "Heisenbug". Поэтому лучше избегать состояний гонки путем тщательного проектирования программного обеспечения.

Гонка данных

Не все рассматривают гонки данных как подмножество условий гонки. Точное определение гонки данных специфично для используемой формальной модели параллелизма, но обычно оно относится к ситуации, когда операция памяти в одной нити может потенциально пытаться получить доступ к ячейке памяти одновременно с операцией памяти в другой нити, которая записывает в эту ячейку, в контексте, когда это небезопасно. Это означает, что гонка данных отличается от условия гонки, поскольку недетерминизм из-за времени выполнения возможен даже в программе, свободной от гонок данных, например, в программе, где все обращения к памяти используют только атомарные операции. Это может быть опасно, поскольку на многих платформах, если две нити одновременно записывают в одну и ту же ячейку памяти, содержимое этой ячейки может оказаться произвольной и бессмысленной комбинацией битов, представляющих значения, которые каждая нить пыталась записать; это может привести к повреждению памяти, если результирующее значение не соответствует ни одному из значений, которые нити пытались записать (иногда это называют «разорванной записью»). Аналогично, если одна нить читает из ячейки, пока другая нить записывает в неё, чтение может вернуть произвольную и бессмысленную комбинацию битов, представляющих значение, которое ячейка содержала до записи, и битов, представляющих записываемое значение. На многих платформах предусмотрены специальные операции памяти для одновременного доступа; в таких случаях одновременный доступ с использованием этих специальных операций обычно безопасен, а одновременный доступ с использованием других операций памяти – опасен. Иногда такие специальные операции (безопасные для одновременного доступа) называют атомарными или операциями синхронизации, а обычные операции (небезопасные для одновременного доступа) – операциями с данными. Вероятно, именно поэтому термин и называется «гонка данных»; на многих платформах, где существует условие гонки, включающее только операции синхронизации, такая гонка может быть недетерминированной, но в остальном безопасной; однако гонка данных может привести к повреждению памяти или неопределенному поведению.

Компьютерная безопасность

Многие условия гонки в программном обеспечении имеют последствия для компьютерной безопасности. Условие гонки позволяет злоумышленнику, имеющему доступ к общему ресурсу, вызвать сбой в работе других процессов, использующих этот ресурс, что может привести к отказу в обслуживании или повышению привилегий. Особый вид условия гонки заключается в проверке условия (например, для аутентификации) и последующем действии на основе этого условия, при этом состояние может измениться между моментом проверки и моментом использования. Если подобная ошибка присутствует в коде, критичном к безопасности, возникает уязвимость безопасности, называемая уязвимостью типа "time-of-check to time-of-use" (TOCTTOU). Условия гонки также намеренно используются для создания аппаратных генераторов случайных чисел и физически неклонируемых функций. Физически неклонируемые функции (PUF) можно создавать, проектируя топологии схем с идентичными путями к узлу и полагаясь на производственные отклонения для случайного определения того, какой путь завершится первым. Измеряя специфический набор результатов условий гонки для каждой изготовленной схемы, можно собрать профиль для каждой схемы и сохранить его в секрете для последующей проверки подлинности схемы.

Файловые системы

Две или более программ могут конфликтовать при попытках изменить или получить доступ к файловой системе, что может привести к повреждению данных или повышению привилегий. Блокировка файлов является распространенным решением. Более сложный подход заключается в организации системы таким образом, чтобы один уникальный процесс (например, запускающий демон) имел исключительный доступ к файлу, а все остальные процессы, которым требуется доступ к данным в этом файле, осуществляли его только посредством межпроцессного взаимодействия с этим процессом. Это требует синхронизации на уровне процессов. Другой вид гонки данных возникает в файловых системах, где не связанные между собой программы могут влиять друг на друга, внезапно исчерпывая доступные ресурсы, такие как дисковое пространство, память или процессорное время. Программное обеспечение, разработанное без учета и обработки этой ситуации, может стать непредсказуемым. Такой риск может оставаться незамеченным в течение длительного времени в системе, кажущейся надежной. Однако со временем может накопиться достаточно данных или быть добавлено достаточное количество другого программного обеспечения, чтобы критически дестабилизировать многие части системы. Примером этого является почти потеря марсохода "Spirit" вскоре после посадки. Решением является предварительное запрошение и резервирование программным обеспечением всех необходимых ресурсов перед началом задачи; если запрос не удается, задача откладывается, что позволяет избежать множества потенциальных точек отказа. Альтернативно, каждая из этих точек может быть оснащена обработкой ошибок, или успех всей задачи может быть проверен после завершения, перед продолжением работы. Более распространенный подход – просто убедиться в наличии достаточных системных ресурсов перед началом задачи; однако этого может быть недостаточно, поскольку в сложных системах действия других запущенных программ могут быть непредсказуемыми.

Сетевые связи

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

Системы жизненно важные

Недостатки программного обеспечения в системах, критичных для жизни, могут иметь катастрофические последствия. Состояние гонки было одним из недостатков аппарата для лучевой терапии Therac 25, что привело к смерти как минимум трех пациентов и травмам еще нескольких. Другой пример – система управления энергоснабжением, поставляемая компанией GE Energy и используемая компанией FirstEnergy Corp. (среди других энергетических объектов) в штате Огайо. В подсистеме сигнализации наблюдалось состояние гонки; когда одновременно произошло отключение трех провисших линий электропередач, это помешало отправке предупреждений операторам, что задержало обнаружение проблемы. Этот программный сбой в конечном итоге привел к масштабному отключению электроэнергии в Северной Америке в 2003 году. Позднее компания GE Energy разработала программную заплату для устранения ранее не выявленной ошибки.

В других областях

Нейробиология показывает, что подобные ситуации гонки могут возникать и в мозге млекопитающих. В железнодорожной сигнализации Великобритании ситуация гонки могла возникнуть при выполнении правила 55. Согласно этому правилу, если поезд останавливался на действующем пути по сигналу, помощник машиниста должен был идти к постам управления, чтобы напомнить диспетчеру о присутствии поезда. По крайней мере, в одном случае, в Уинвике в 1934 году, произошла авария, потому что диспетчер принял другой поезд до того, как помощник машиниста прибыл. Современные методы сигнализации устраняют ситуацию гонки, позволяя машинисту мгновенно связаться с постом управления по радио.