Руководство по Ada (Ada 2022)
9.5.1 Защищённые подпрограммы и защищённые действия
Защищённая подпрограмма — это подпрограмма, объявленная непосредственно внутри protected_definition. Защищённые процедуры обеспечивают эксклюзивный чтение-запись доступ к данным защищённого объекта; защищённые функции предоставляют одновременный только-чтение доступ к данным.
Статическая семантика
Внутри тела защищённой функции (или функции, объявленной непосредственно внутри protected_body) текущий экземпляр окружающего защищённого блока определяется как константа (то есть его подкомпоненты могут быть считаны, но не обновлены). Внутри тела защищённой процедуры (или процедуры, объявленной непосредственно внутри protected_body) и внутри entry_body текущий экземпляр определяется как переменная (обновление разрешено).
Для типа, объявленного protected_type_declaration или для анонимного типа объекта, объявленного single_protected_declaration, может быть указан следующий определяемый языком аспект, относящийся к типу:
Exclusive_Functions
Тип аспекта Exclusive_Functions — Boolean. Если не указан (включая наследование), аспект равен False.
Значение True для этого аспекта указывает, что защищённые функции ведут себя так же, как защищённые процедуры относительно взаимного исключения и обслуживания очередей (см. ниже).
Защищённая процедура или запрос являются эксклюзивной защищённой операцией. Защищённая функция защищённого типа P является эксклюзивной защищённой операцией, если аспект Exclusive_Functions типа P равен True.
Динамическая семантика
Для выполнения вызова защищённой подпрограммы, вычисление имени или префикса и ассоциации параметров, а также любое присваивание параметров in out или out, происходит так же, как при вызове обычной подпрограммы (см. 6.4). Если вызов является внутренним вызовом (см. 9.5), тело подпрограммы выполняется как при вызове обычной подпрограммы. Если вызов является внешним, тело подпрограммы выполняется как часть нового защищённого действия на целевом защищённом объекте; защищённое действие завершается после выполнения тела подпрограммы. Защищённое действие также может быть запущено вызовом запроса (см. 9.5.3).
Новое защищённое действие не запускается на защищённом объекте, пока другое защищённое действие на том же защищённом объекте не завершено, если оба действия не являются результатом вызова неэксклюзивной защищённой функции. Это правило выражается через ресурс выполнения, связанный с защищённым объектом:
- Запуск защищённого действия на защищённом объекте соответствует получению ресурса выполнения, связанного с защищённым объектом, либо для эксклюзивного доступа чтения-записи, если защищённое действие выполняется для вызова эксклюзивной защищённой операции, либо для одновременного только-чтения доступа в противном случае;
- Завершение защищённого действия соответствует освобождению связанного ресурса выполнения.
После выполнения эксклюзивной защищённой операции на защищённом объекте, но перед завершением связанного защищённого действия, обслуживаются очереди запросов (если таковые имеются) защищённого объекта (см. 9.5.3).
Если параллельная конструкция встречается внутри защищённого действия, новые логические потоки управления не создаются. Вместо этого каждый элемент параллельной конструкции, который должен был бы стать отдельным логическим потоком управления, выполняется в логическом потоке управления, выполняющем защищённое действие. Если таких элементов несколько, инициированных в одной точке, они выполняются в произвольном порядке.
Ограниченные (временные) ошибки
Во время защищённого действия вызов операции, потенциально блокирующей (см. 9.5), является ограниченной ошибкой.
Абзацы с 9 по 16 были перемещены в 9.5.
Если ограниченная ошибка обнаружена, поднимается Program_Error. Если не обнаружена, ограниченная ошибка может привести к тупику или (вложенному) защищённому действию на том же целевом объекте.
Во время защищённого действия вызов подпрограммы, тело которой содержит потенциально блокирующую операцию, является ограниченной ошибкой. Если ограниченная ошибка обнаружена, поднимается Program_Error; в противном случае вызов проходит нормально.
ПРИМЕЧАНИЕ 1 Если две задачи пытаются запустить защищённое действие на защищённом объекте, и, как минимум, одна из них вызывает неэксклюзивную защищённую функцию, то только одна из задач может продолжить выполнение. Хотя другая задача не может продолжить, она не считается заблокированной и может потреблять ресурсы обработки, ожидая своей очереди. Если не существует политики допуска (см. D.4.1), не предусмотрено никакого определяемого языком порядка или очереди для задач, конкурирующих за запуск защищённого действия — на многопроцессорной системе такие задачи могут использовать ожидание с проверкой; для дальнейших рассмотрений для однопроцессорных и многопроцессорных систем см. D.3, «Блокирование приоритетов потолка».
ПРИМЕЧАНИЕ 2 Тело защищённого блока может содержать объявления и тела локальных подпрограмм. Они не видны за пределами защищённого блока.
ПРИМЕЧАНИЕ 3 Тело защищённой функции может содержать внутренние вызовы других защищённых функций, но не защищённых процедур, так как текущий экземпляр является константой. С другой стороны, тело защищённой процедуры может содержать внутренние вызовы как защищённых функций, так и процедур.
ПРИМЕЧАНИЕ 4 Изнутри защищённого действия, внутренний вызов защищённой подпрограммы или внешний вызов защищённой подпрограммы с другим целевым объектом не считаются потенциально блокирующей операцией.
ПРИМЕЧАНИЕ 5 Аспект Nonblocking может быть задан True в определении защищённого блока, чтобы отклонить большинство попыток использовать потенциально блокирующие операции внутри защищённого блока (см. 9.5). Pragma Detect_Blocking может использоваться для обеспечения того, что любые оставшиеся выполнения потенциально блокирующих операций во время защищённого действия поднимают Program_Error. См. H.5.
Примеры
Примеры вызовов защищённых подпрограмм (см. 9.4):
Shared_Array.Set_Component(N, E);
E := Shared_Array.Component(M);
Control.Release;
E := Shared_Array.Component(M);
Control.Release;