Справочное руководство по языку Ада-83



Справочное руководство по языку Ада-83

         

Атрибуты задач и входов


Для задачного объекта или значения Т определены следующие атрибуты:

T'CALLABLE Вырабатывает значение FALSE, если выполнение указанной задачи Т либо закончено, либо завершено либо задача аварийная. В остальных случаях вырабатывает значение TRUE. Значение этого атрибута имеет предопределенный тип BOOLEAN.
T'TERMINATED Вырабатывает значение TRUE, если указанная Т задача завершена. В остальных случаях вырабатывает значение FALSE. Значение этого атрибута имеет предопределенный тип BOOLEAN.

В дополнение к приведенным для задачного объекта Т или задачного типа Т определены атрибуты представления STORAGE-SIZE, SIZE и ADDRESS (см. 2).

Атрибут COUNT определен для входа Е задачного модуля Т. Вход может быть либо одиночным входом, либо входом семейства (в любом случае имя одиночного входа или семейства входов может быть либо простым, либо расширенным). Этот атрибут допустим только в теле Т, но не во вложенном в тело Т программном модуле.



E'COUNT Вырабатывает число вызовов входа, присутствующих в очереди входа Е в данный момент (если атрибут вычисляется при выполнении оператора принятия входа Е, то в это число не включается вызывающая задача). Значение атрибута имеет тип универсальный-целый.

Примечание. Алгоритмы, соответствующие программы которых используют атрибут E'COUNT, обязаны учитывать возможность увеличения значения атрибута с появлением новых вызовов и уменьшения этого значения, например, при временных вызовах входа.

Ссылки: аварийная задача 9.10, атрибут 4, временной вызов входа 3, вход 9.5, завершенная задача 9.4, задача 9, задачный объект 9.2, задачный тип 9.1, законченная задача 9.4, значение логического типа TRUE 3, значение логического типа FALSE 3, квант памяти 13.7, логический тип 3, оператор принятия 9.5, очередь вызовов входов 9.5, тип универсальный целый 4, указывать



Задачи


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

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

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

Свойства каждой задачи определяются соответствующим задачным модулем, который состоит из спецификации задачи и тела задачи. Задачные модули представляют собой одну из четырех форм программных модулей, из которых может состоять программа. Другие три формы — это подпрограммы, пакеты и настраиваемые модули. В данной главе описываются свойства задачных модулей, задач и входов и операторы, влияющие на взаимодействие задач (т. е. операторы вызова входов, принятия, задержки, отбора и прекращения).

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

Ссылки: вход 9.5, настраиваемый модуль 12, оператор вызова входа 9.5, оператор задержки 9.6, оператор прекращения 9.10, оператор принятия 9.5, оператор отбора 9.7, пакет 7, подпрограмма б, программный модуль 6, рандеву 9.5, спецификация задачи 9.1, тело задачи



Операторы отбора


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

оператор-отбора ::= отбор-с-ожиданием | условный-вызов-входа | временной-вызов-входа

Ссылки: временной вызов входа 3, отбор с ожиданием 1, условный вызов входа 2.

1. ОТБОР С ОЖИДАНИЕМ

Эта форма оператора отбора допускает объединение ожидания и отбор с ожиданием одной или нескольких альтернатив. Отбор может зависеть от условий, связанных с каждой альтернативой отбора с ожиданием.

отбор-с-ожиданием ::= select альтернатива-отбора {or альтернатива-отбора) [else последовательность-операторов] end select;альтернатива-отбора ::= [when условие = >] альтернатива-отбора-с-ожиданиемальтернатива-отбора-с-ожиданием ::= альтернатива-принятия | альтернатива-задержки | альтернатива-завершенияальтернатива-принятия ::= оператор-принятия [последовательность-операторов]альтернатива-задержки ::= оператор-задержки [последовательность - операторов]альтернатива-завершения ::= terminate;

Оператор отбора с ожиданием должен содержать по крайней мере одну альтернативу принятия. В дополнение к этому оператор отбора с ожиданием может содержать либо альтернативу завершения (только одну), либо одну или несколько альтернатив задержки, либо раздел else; эти три возможности являются взаимоисключающими.

Альтернатива отбора называется открытой, если она, не начинается с зарезервированного слова when или если значение условия — TRUE. В противном случае альтернатива называется закрытой.

При выполнении оператора отбора с ожиданием в произвольном, не определенном в языке порядке вычисляются все условия, заданные после зарезервированного слова when; определяются открытые альтернативы. Для открытой альтернативы задержки вычисляется выражение длительности задержки. Для открытой альтернативы принятия входа семейства вычисляется индекс входа. Выполнение отбора с ожиданием заканчивается отбором и вычислением либо одной из открытых альтернатив, либо раздела else; правила такого отбора описываются ниже.

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

Отбор других форм альтернатив или раздела else осуществляется следующим образом.

' Отбирается открытая альтернатива задержки, если никакая другая альтернатива принятия не может быть выбрана до истечения указанной задержки ожидания (немедленно, если длительность отрицательная или нулевая и при отсутствии очереди вызовов входа); затем выполняются возможные последующие операторы этой альтернативы. Если возможен отбор нескольких альтернатив задержки (т.е. если задержки у них одинаковы), то одна из них выбирается произвольно.

• Отбирается раздел else и выполняется последовательность операторов этого раздела, если нельзя немедленно отобрать альтернативу принятия, в частности если все альтернативы закрыты.

•Отбирается открытая альтернатива завершения, если перечисленные в разд. 9.4 условия удовлетворены. Из других правил следует, что нельзя отобрать альтернативу завершения, пока существует очередь вызовов 'любого входа задачи.

Исключение PROGRAM_ERROR возбуждается, если все альтернативы закрыты и раздел else отсутствует.

Пример оператора отбора:

select accept DRIVER_AWAKE_SIGNAL; or delay 30.0*SECONDS; STOP_THE_TRAIN; end select;

Пример тела задачи с оператором отбора:

task body RESOURCE is BUSY : BOOLEAN := FALSE; begin loop select when not BUSY=> accept SEIZE do BUSY := TRUE; end: or accept RELEASE do BUSY := FALSE; end; or terminate; end select; end loop; end RESOURCE;

Примечание. В отборе с ожиданием допускаются несколько открытых альтернатив задержки или несколько открытых альтернатив принятия одного и того же входа.

Ссылки: вход 9.5, вызов входа 9.5, выражение задержки 9.6, длительность 9.6, задача 9, индекс входа 9.5, исключение PROGRAM_ERROR 11.1, оператор задержки 9.6, оператор отбора 9.7, оператор принятия 9.5, описание 3.1, очередь вызовов входа 9.5, последовательность операторов 5.1, рандеву 9.5, условие



Операторы прекращения


Оператор прекращения переводит одну или несколько задач в аварийное состояние, предотвращая любые дальнейшие рандеву с такими задачами.

оператор-прекращения ::= abort имя-задачи {, имя-задачи};

При определении типа имени каждой задачи используется тот факт, что это задачный тип.

При выполнении оператора прекращения заданные имена задач вычисляются в порядке, который в языке не определен. Затем каждая упомянутая задача становится аварийной, если она еще не завершена; аналогично любая зависящая от упомянутой задача становится также аварийной, если она еще не завершена.

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

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

Вызов входа аварийной задачи возбуждает в месте вызова исключение TASKING_ERROR. Аналогично исключение TASKING_ERROR возбуждается в любой задаче, вызвавшей вход аварийной задачи, если вызов входа все еще находится в очереди либо рандеву не окончено (вызовом входа может быть либо оператор вызова входа, либо операторы условного или временного вызова входа); исключение возбуждается не позже окончания аварийной задачи. Для любой аварийной (или законченной) задачи значение атрибута CALLABLE есть FALSE.

Если аварийное окончание задачи произошло во время изменения в задаче некоторой переменной, то значение этой переменной не определено.

Пример:

abort USER, TERMINAL.all, POOL(3);

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

Ссылки: аварийное состояние при рандеву 11.5, активизация 9.3, атрибут 4, атрибут CALLABLE 9.9, временной вызов входа 3, завершенная задача 9.4, зависимая задача 9.4, задача 9, значение логического типа FALSE 3, имя 4.1, исключение TASKING_ERROR 11.1, обработка имени 4.1, обработчик исключения 11.2, оператор 5, оператор вызова входа 9.5, оператор задержки 9.6, оператор отбора 9.7, оператор принятия 9.5, очередь вызовов входов 9.5, рандеву 9.5, условный вызов входа 2.



Операторы задержки, длительность и время


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

оператор-задержки ::= delay простое-выражение;

Простое выражение должно быть предопределенного фиксированного типа DURATION (ДЛИТЕЛЬНОСТЬ); его значение выражается в секундах; оператор задержки с отрицательным значением эквивалентен оператору задержки с нулевым значением.

Все реализации типа DURATION должны допускать представление длительности (положительные и отрицательные) по крайней мере до 86 400 с (одни сутки); минимальная представи-мая длительность, DURATION'SMALL, должна быть не больше 20 мс (по возможности значение не превышает 50 мкс. Заметим, что DURATION'SMALL не обязано соответствовать основному циклу таймера, именованному числу SYSTEM.TICK (см. 13.7).

Определение типа TIME приведено в предопределенном пакете CALENDAR. Функция CLOCK возвращает текущее значение типа TIME. Функции YEAR, MONTH, DAY и SECONDS возвращают соответствующие наименованиям значения для заданного значения аргумента типа TIME, а процедура SPLIT возвращает одновременно все четыре соответствующих значения. Наоборот, функция TIME-OF упаковывает номера года, месяца, числа дня и значение длительности в значение типа TIME. В операции " + " и " -" для сложения и вычитания значений времени и длительности, а также в операции соотношения для значений времени вкладывают традиционный смысл.

Исключение TIME_ERROR возбуждается в функции TIME-OF, если значения фактических параметров не формируют правильную дату. Это исключение возбуждается также операциями "+" и "-", если для заданных операндов они не могут возвратить дату с номером года из диапазона соответствующего подтипа или если операция " -" не может возвратить значение из диапазона типа DURATION.

package CALENDAR is type TIME is private; subtype YEAR_NUMBER is INTEGER range 1901 .. 2099; subtype MONTH_NUMBER is INTEGER range 1 .. 12; subtype DAY_NUMBER is INTEGER range 1 .. 31; subtype DAY_DURATION is DURATION range 0.0 .. 86_400.0; function CLOCK return TIME; function YEAR (DATE : TIME) return YEAR_NUMBER; function MONTH (DATE : TIME) return MONTH_NUMBER; function DAY (DATE : TIME) return DAY_NUMBER; function SECONDS (DATE : TIME) return DAY_DURATION; procedure SPLIT (DATE : in TIME; YEAR : out YEAR-NUMBER; MONTH : out MONTH-NUMBER; DAY : out DAY-NUMBER; SECONDS : out DAY_DURATION); function TIME_OF(YEAR : YEAR_NUMBER; MONTH : MONTH_NUMBER; DAY : DAY_NUMBER; SECONDS : DAY_DURATION := 0.0) return TIME; function "+" (LEFT : TIME; RIGHT : DURATION) return TIME; function "+" (LEFT : DURATION; RIGHT : TIME) return TIME; function "-" (LEFT : TIME; RIGHT : DURATION) return TIME; function "-" (LEFT : TIME; RIGHT : TIME) return DURATION; function "<" (LEFT, RIGHT TIME) return BOOLEAN; function "<=" (LEFT, RIGHT TIME) return BOOLEAN; function ">" (LEFT, RIGHT TIME) return BOOLEAN; function ">=" (LEFT, RIGHT TIME) return BOOLEAN; TIME_ERROR : exception; -— может быть возбуждено функцией и операциями "+" и "-"private -— зависит от реализации end;



также примеры описаний одиночных задач



Примечание

CONTROL : RESOURCE; TELETYPE : KEYBOARDDRIVER; POOL : array(1 .. 10) of KEYBOARDDRIVER; -— см. также примеры описаний одиночных задач в 9.1 Пример ссылочного типа, указывающего задачный объект:
type KEYBOARD is access KEYBOARDDRIVER; TERMINAL : KEYBOARD := new KEYBOARDDRIVER; Примечание. Поскольку задачный тип является лимитируемым, он может появиться как определение лимитируемого личного типа в личном разделе и как фактический параметр настройки, сопоставляемый формальному параметру лимитируемого типа. С другой стороны, тип формального параметра настройки вида in не должен быть лимитируемым и, следовательно, не может быть задачным типом.
Задачные объекты ведут себя как константы (задачный объект всегда указывает одну и ту же задачу), поскольку их значения неявно определены при описании либо при генерации, либо при сопоставлении параметров, и никакие присваивания недопустимы. Однако зарезервированное слово constant недопустимо в описании задачного объекта, так как его наличие требует явной инициализации. Задачный объект, который является формальным параметром вида in, есть константа (как и любой формальный параметр вида in).
Если алгоритм требует запоминания и переименования задачи, то это можно сделать определением ссылочного типа, указывающего на соответствующие задачные объекты, и использованием ссылочных значений для целей идентификации (см. предыдущий пример). Присваивание для такого ссылочного типа возможно, как и для любого другого ссылочного типа.
Для задачных типов допустимы описания подтипов, как и для других типов, но никакие ограничения к задачному типу неприменимы.
Ссылки: вид формального параметра 6.2, вид формального параметра настройки 1, вход 9.5, генератор 4.8, зарезервированное слово 2.9, инициализация 1, константа 1, лимитируемый тип 4, личный раздел 7.2, личный тип 7.4, настраиваемый модуль 12, объект 3.2, ограничение 3.3, операция неравенства 2, операция равенства 2, описание компоненты 3.7, описание константы 1, подкомпонента 3.3, подпрограмма 6, предвыполнение 3.9, присваивание 5.2, сопоставление параметров 6.4, сопоставление параметров настройки 12.3;составной тип 3.3, ссылочный тип 3.8, тело задачи 9.1, тип 3.3, указывать 9, фактический параметр 1, фактический параметр настройки 12.3, формальный параметр 6.2, формальный параметр настройки 1.

Во втором примере цикл повторяется



Примечание

delay 3.0; -— задержка на 3.0 с declare use CALENDAR; -— INTERVAL глобальная константа типа DURATION NEXT_TIME : TIME := CLOCK + INTERVAL; begin loop delay NEXT_TIME - CLOCK; -— некоторые действия NEXT_TIME := NEXT_TIME + INTERVAL; end loop; end: Примечание. Во втором примере цикл повторяется в среднем один раз каждые INTERVAL секунд. Этот интервал между двумя последовательными итерациями только приблизителен. Однако здесь не произойдет ошибки по длительности, поскольку длительность каждой итера-ции (существенно) меньше значения INTERVAL.
Ссылки: библиотечный модуль 10.1, вызов функции 6.4, длительность С, задача 9, личный тип 7.4, оператор 5, операция 4.5, операция отношения 4.5, операция сложения 4.5, пакет 7, простое выражение 4.4, тип 3.3, фиксированный тип 9.

Пример использования задачи


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

loop -— выработка следующего символа CHAR BUFFER.WRITE(CHAR); exit when CHAR = ASCII.EOT; end loop;

потребляющая задача — операторы:

loop BUFFER.READ(CHAR); -— использование символаСНАР exit when CHAR = ASCII.EOT; end loop;

Задача буферизации содержит внутренний пул для символов, обрабатываемых цикличес-ки. Пул имеет два индекса: IN-INDEX, указывающий место следующего вводимого символа, и OUT_INDEX, указывающий место следующего выводимого символа.

task BUFFER is entry READ (С : out CHARACTER); entry WRITE (C : in CHARACTER); end:task body BUFFER is POOL_SIZE : constant INTEGER := 100: POOL : array(1 .. POOL_SIZE) of CHARACTER; COUNT : INTEGER range 0 .. POOL_SIZE := 0; IN_INDEX, OUT_INDEX : INTEGER range 1 .. POOL_SIZE := 1; begin loop select when COUNT < POOL_SIZE => accept WRITE(C : in CHARACTER) do POOL(IN_INDEX) := C; end; IN_INDEX := IN_INDEX mod POOL_SIZE + 1; COUNT := COUNT + 1; or when COUNT > 0 => accept READ(C ; out CHARACTER) do C := POOL(OUT_INDEX); end; OUT_INDEX := OUT_INDEX mod POOL_SIZE + 1; COUNT := COUNT - 1; or terminate; end select: end loop: end BUFFER;

Приоритеты


Каждая задача может (но не обязательно) иметь приоритет со значением подтипа PRIORITY (типа INTEGER), описанного в предопределенном библиотечном пакете SYSTEM (см. 13.7). Меньшее значение приоритета указывает на меньшую степень важности; диапазон приоритетов определяется реализацией. Приоритет связывается с задачей, если в спецификации соответствующей задачи присутствует прагма:

pragma PRIORITY (статическое-выражение);

Приоритет задается значением выражения. Если такая прагма присутствует в самом внешнем разделе описаний главной программы, то приоритет связывается с главной программой. В спецификации данной задачи или при подпрограмме — библиотечном модуле может употребляться не более одной такой прагмы, и это единственные места, допустимые для этой прагмы. Прагма PRIORITY игнорируется при ее появлении в подпрограмме, не являющейся главной программой.

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

Если две задачи с разными приоритетами готовы к выполнению и могут практически выполняться, используя одни и те же физические процессоры и одни и те же ресурсы обработки, то нельзя чтобы выполнялась задача с более низким приоритетом, а не выполнялась задача с более высоким приоритетом.

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

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

Ссылки: главная программа 10.1, задача 9, оператор вызова входа 9.5, пакет SYSTEM 13.7, подтип 3.3, прагма 2.8, раздел описаний 3.9, рандеву 9.5, спецификация задачи 9.1, статическое выражение 4.9, целый тип 4.



Разделяемые переменные


Обычными средствами передачи данных между задачами являются операторы вызова и принятия входов.

Если две задачи считывают или изменяют разделяемую переменную (доступную обеим задачам), то ни одна из них ничего не может знать о порядке выполнения операций над переменной в другой задаче, исключая точки их синхронизации. Две задачи синхронизуются в начале и в конце их рандеву. В начале и в конце своей активизации задача синхронизуется с вызвавшей эту активизацию задачей. Задача, которая закончила свое выполнение, синхрони-зуема с любой другой задачей.

О действиях, выполняемых программой, использующей разделяемые переменные, всегда могут быть сделаны следующие предположения:

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

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

Выполнение программы ошибочно, если какое-либо из этих предположений нарушено.

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

Аналогично если данная задача изменяет значение разделяемой переменной, сделанные предположения допускают, чтобы реализация поддерживала локальные копии значения и откладывала запоминание локальной копии в разделяемую переменную до точки синхронизации, заменяя каждые последующие считывание или изменения значений разделяемой переменной на считывание или изменение локальной копии. С Другой стороны, не допускается, чтобы реализация вводила такую память, которая не будет обрабатываться в каноническом порядке (см. 11.6).

Для задания того, что каждое считывание или изменение значения разделяемой переменной является для этой переменной точкой синхронизации, может быть использована прагма SHARED, т.е. для данной переменной (но не обязательно для остальных) сделанных выше предположения справедливы. Форма этой прагмы следующая:

pragma SHARED (простое-имя-переменной);

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

Реализация должна ограничивать объекты, для которых допустима прагма SHARED, объектами, для которых каждое прямое считывание или прямое изменение реализуется неделимыми операциями.

Ссылки: активизация 9.3, глобальный 8.1, задача 9, изменение значение 6.2 канонический порядок 11.6, оператор вызова входа 9.5, оператор принятия 9.5, ошибочный 1.6, переменная 1, прагма 2.8, присваивание 5.2, простое имя 3.1, 4.1, раздел описаний 3.9, рандеву 9.5, спецификация пакета 7.1, тип 3.3, чтение значения



Спецификации задач и тела задач


Задачный модуль состоит из спецификации задачи и тела задачи. Спецификация задачи, которая начинается зарезервированными словами task type, описывает задачный тип. Значение объекта задачного типа указывает задачу. Если задача имеет входы, то они описываются в спецификации задачи; эти входы также называются входами объекта. Выполнение задачи определяется соответствующим телом задачи.

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

описание-задачи ::== спецификация-задачи; спецификация - задачи :: = task [type] идентификатор [is {описание - входа} {спецификатор - представления} end [простое-имя-задачи]] тело-задачи ::= task body простое-имя-задачи is [раздел - описаний] begin последовательность - операторов [exception обработчик - исключения {обработчик-исключения}] end [простое-имя-задачи];

Простое имя в начале тела задачи должно совпадать с идентификатором задачного моду-ля. Аналогично если в конце спецификации или тела задачи появляется простое имя, то оно должно совпадать с идентификатором задачного модуля. Внутри тела задачи имя соответствующего задачного модуля может быть также использовано для ссылки на объект-задачу (указывать на задачу), тело которой выполняется в данный момент; кроме того, не допускается использование этого имени в качестве обозначения типа внутри собственного задачного модуля. [7]

При предвыполнении спецификации задачи описания входов и спецификаторы представления (если они есть) предвыполняются в том порядке, в котором они даны. Спецификаторы представления применяются только к входам, описанным в спецификации задачи (см. 13.5).

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

Выполнение тела задачи вызывается активизацией задачного объекта соответствующего типа (см. 9.3). Возможные в конце тела задачи обработчики исключений обрабатывают исключения, возбуждаемые в ходе выполнения последовательности операторов тела задачи (см. 11.4).

Примеры спецификации задачных типов:

task type RESOURCE is entry SEIZE; entry RELEASE; end RESOURCE;task type KEYBOARD_DRIVER is entry READ (C : out CHARACTER); entry WRITE (C : In CHARACTER); endKEYBOARD_DRIVER;

Примеры спецификации одной задачи:

task PRODUCER_CONSUMER is entry READ (V : out ITEM); entry WRITE (E : in ITEM); end;task CONTROLLER is entry REQUEST(LEVEL)(D : ITEM); -— семейство ВХОДОВ end CONTROLLER;task USER; -— не имеет входов

Пример спецификации задачи и соответствующего тела:

task PROTECTED_ARRAY is -— INDEX and ITEM — это глобальные типы entry READ (N : in INDEX; V : out ITEM); entry WRITE (N : In INDEX; E : in ITEM); end;task body PROTECTED_ARRAY is TABLE : array(INDEX) of ITEM := (INDEX => NULL_ITEM); begin loop select accept READ (N : in INDEX; V : out ITEM) do V := TABLE(N); end READ; or accept WRITE (N : in INDEX; E : in ITEM) do TABLE(N) := E; end WRITE; end select; end loop; end PROTECTED_ARRAY;

Примечание. Спецификация задачи задает интерфейс задачам данного типа с другими задачами тех же или различных типов, а также с главной программой.

Ссылки: вход 9.5, главная программа 10.1, зарезервированное слово 2.9, идентификатор 2.3, обработчик исключения 11.2, объект 3.2, описание 3.1, описание входа 9.5, описание объекта 1, описание типа 1, последовательность операторов 5.1, предвыполнение 3.9, простое имя 4.1, раздел описаний 3.9, спецификатор представления 13.1, тип


[7] 1) Кроме того, оно не может быть использовано в качестве производного от него типа или подтипа. - Прим. ред.



Условные вызовы входов


Условный вызов входа производит вызов входа, который отменяется, если рандеву нельзя осуществить немедленно.

условный-вызов-входа ::= select оператор-вызова-входа [последовательность-операторов] else последовательность-операторов end select;

При выполнении условного вызова входа вначале вычисляется имя входа. Затем выполняются требуемые вычисления фактических параметров, как при вызове подпрограмы (см. 6.4).

Вызов входа отменяется, если выполнение вызванной задачи не достигло точки, в которой она готова к принятию входа (т.е. не достигнуты оператор принятия соответствующего входа или оператор отбора с открытой альтернативой принятия этого входа), или существует очередь ранее сделанных вызовов этого входа. Если вызванная задача достигла оператора отбора, но альтернатива принятия этого входа не отобрана, то вызов входа отменяется.

Если вызов входа отменен, то выполняются операторы раздела иначе. В противном случае происходит рандеву и выполняется последовательность операторов после вызова входа (если она есть).

Выполнение условного вызова входа возбуждает исключение-TASKING- ERROR, если вызванная задача уже закончила свое выполнение (см. разд. 9.10 для случая, когда вызванная задача становится аварийной).

Пример:

procedure SPIN(R : RESOURCE) is begin loop select R.SEIZE; return; else null; --занято, надо подождать end select: end loop; end;

Ссылки: аварийная задача 9.10, выполнение 4.5, выражение 4.4, задача 9, законченная задача 9.4, индекс входа 9.5, исключение TASKING_ERROR 11.1, оператор вызовов входа 9.5, оператор отбора 9.7, оператор принятия 9.5, открытая альтернатива 1, очередь вызова входа 9.5, последовательность операторов 5.1, раздел фактических параметров 6.4, рандеву 9.5, семейство входов 9.5

3. ВРЕМЕННЫЕ ВЫЗОВЫ ВХОДОВ

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

временной-вызов-входа ::= select оператор-вызова-входа [последовательность - операторов] or альтернатива-задержки end select;

При выполнении временного вызова входа вначале вычисляется имя входа. Затем выполняются требуемые вычисления фактических параметров, как при вызове подпрограммы (см. 6.4). После этого вычисляется выражение, задающее задержку, и, наконец, производится вызов входа.

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

Выполнение временного вызова входа возбуждает исключение TASKING_ERROR, если вызванная задача закончила свое выполнение до принятия вызова (см. также разд. 9.10 для случая, когда вызванная задача становится аварийной).

Пример:

select CONTROLLER. REQUEST(MEDIUM)(SOME_ITEM); or delay 45.0; -- контроллер слишком занят, попробуйте -- что-либо еще end select;

Ссылки: аварийная задача 9.10, выполнение 4.5, выражение 4.4, выражение задержки 9.6, длительность 9.6; задача 9, законченная задача 9.4, индекс входа 9.5, исключение TASKING_ERROR 11.1, оператор вызова входа 9.5, оператор задержки 9.6, оператор принятия 9.5, последовательность операторов 5.1, раздел фактических параметров 6.4, рандеву 9.5, семейство входов 9.5, условный вызов входа 2.



Входы, вызовы входов и операторы принятия


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

описание-входа ::= entry идентификатор [(дискретный-диапазон)] [раздел-формальных-параметров];оператор-вызова-входа ::= имя-входа [раздел-фактических-параметров];оператор-принятия ::= accept простое-имя-входа [(индекс-входа)] [раздел-формальных-параметров] [do последовательность-операторов end [простое-имя-входа]];индекс-входа ::= выражение

Описание входа, включающее дискретный диапазон (см. 1), описывает семейство различных входов с одним и тем же формальным разделом (если он есть), а именно по одному входу для каждого значения дискретного диапазона. Термин одиночный вход используется при определении правил, применимых к любому входу, отличному от члена семейства. Задача, указанная объектом задачного типа, имеет вход (входы), который (которые) описан (описаны) в спецификации этого задачного типа.

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

Одиночный вход совмещается с подпрограммой, литералом перечисления или другим одиночным входом, если у них одинаковые идентификаторы. Совмещение для семейства входов не определено. Одиночный вход или вход семейства могут быть переименованы в процедуру, как поясняется в разд.

Виды параметров, определенные для параметров формального раздела описания входа, такие же, как в описании подпрограммы, и имеют тот же смысл (см. 6.2). Синтаксически оператор вызова входа подобен оператору вызова процедуры; правила сопоставления параметров остаются теми же, что и для вызовов подпрограмм (см. 1 и 2).

Оператор принятия задает действия, которые выполняются при вызове упомянутого в этом операторе входа (им может быть и вход семейства). Раздел формальных параметров оператора принятия должен быть согласован с разделом формальных параметров, заданным в описании одиночного входа или семейства входов, упомянутых в операторе принятия (см. разд. 1 о согласовании). Если в конце оператора принятия используется простое имя, оно должно повторять простое имя, заданное в начале этого оператора.

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

При предвыполнении описания входа вначале вычисляется дискретный диапазон (если он есть), затем так же, как в описании подпрограммы, предвыполняется раздел формальных параметров (если он есть).

Выполнение оператора принятия начинается с вычисления индекса входа (в случае входа семейства). Выполнение оператора вызова входа начинается с вычисления имени входа, затем следуют вычисления, требуемые для фактических параметров, как и при вызове подпрограммы (см. 6.4). Дальнейшее выполнение оператора принятия и соответствующего оператора вызова входа синхронизовано.

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

• Если вызывающая задача перешла к оператору вызова входа раньше, чем имеющая этот вход задача достигла оператора принятия, то выполнение вызывающей задачи приостанавливается.

• Если задача достигла оператора принятия раньше любого вызова этого входа, то выполнение задачи приостанавливается до получения такого вызова.

Если вход был вызван и соответствующий оператор принятия достигнут, то его последовательность операторов (если она есть) выполняется вызванной задачей (вызывающая задача остается приостановленной). Это взаимодействие задач называется рандеву. После рандеву вызывающая задача и задача, содержащая вход, продолжают выполняться параллельно.

Если несколько задач вызывают один и тот же вход до того, как достигнут оператор принятия, то эти вызовы становятся в очередь; с каждым входом связывается одна очередь. Каждое выполнение оператора принятия удаляет из очереди один вызов. Вызовы обрабатываются в порядке поступления.

При попытке вызвать вход задачи, закончившей свое выполнение, в точке вызова вызывающей задачи возбуждается исключение TASKING_ERROR; это же исключение возбуждается в точке вызова, если вызванная задача заканчивает свое выполнение до принятия входа (см. также разд. 9,10 для случая, когда вызванная задача становится аварийной). Исключение CONSTRAINT_ERROR возбуждается, если индекс входа семейства не принадлежит заданному дискретному диапазону.

Примеры описаний входов:

entry READ(V : out ITEM); entry SEIZE; entry REQUEST(LEVEL)(D : ITEM); -- семейство входов

Примеры вызовов входов:

CONTROL.RELEASE; -— см. 9.2 и 9.1 PRODUCER_CONSUMER.WRITE(E); -— CM. 9.1 POOL(5).READ(NEXT_CHAR); -— CM. 9.1 и 9.2 CONTROLLER.REQUEST(LOW)(SOME_ITEM); —- см. 9.1

Примеры операторов принятия:

accept SEIZE;accept READ(V : out ITEM) do V := i.OCAUTEM; end READ;accept REQUEST(LOW)(D : ITEM) do ... end REQUEST;

Примечание. Заданный в операторе принятия раздел формальных параметров не предвыполняется; он используется только для идентификации соответствующего входа.

Оператор принятия может вызывать подпрограммы, производящие вызовы входов. Оператор принятия может не содержать последовательности операторов, даже если соответствующий вход имеет параметры. Точно так же он может содержать последовательность операторов, даже если соответствующий вход не имеет параметров. Последовательность операторов в операторе принятия может включать операторы возврата. Задача может вызывать и свои собственные входы, однако это, конечно, может привести к тупиковой ситуации. Языком разрешены условные и временные вызовы входов (см. 2 и 3). Правилами языка задаче в данной момент времени разрешается находиться только в одной очереди.

Если границы дискретного диапазона семейства входов являются литералами целого типа, то индекс (в имени входа или в операторе принятия) должен быть предопределенного типа INTEGER (см. 1).

Ссылки: аварийная задача 9.10, временнбй вызов входа 3, вызов процедуры 6.4, выполнение 4.5, выражение 4.4, дискретный диапазон 1, задача 9, законченная задача 9.4, идентификатор 2.3, именуемая компонента 3, имя 4.1, индексируемая компонента 1, исключение CONSTRAINT_ERROR 11.1, исключение TASKING_ERROR 11.1, литерал перечисления 1, область 8.2, объект 3.2, оператор возврата 5-8, описание переименования 8.5, описание подпрограммы 6.1, параллельное выполнение 9, подпрограмма 6, последовательность операторов 5.1, постфикс 3, правила согласования 1, предвыполнение 3.1, 3.9, префикс 4.1, простое выражение 4.4, простое имя 4.1, процедура 6, раздел фактических параметров 6.4, совмещение 6.6, 8.7, спецификация задачи 9, тело задачи 9.1, тело подпрограммы 6.3, указывать 9.1, условный вызов входа 2, формальный раздел 6.1, целый тип 4.



Выполнение и активизация задачи


Тело задачи определяет выполнение всякой задачи, которая указывается задачным объектом соответствующего задачного типа. Начальный этап этого выполнения называется активизацией задачного объекта и указанной им задачи; активизация состоит из предвыполнения раздела описаний (если он есть) тела задачи. Выполнение различных задач, в частности, их активизация, производится параллельно.

Если задачный объект описан непосредственно в разделе описаний, то активизация задачного объекта начинается после предвыполнения раздела описаний (т. е. после грамматического разбора зарезервированного слова begin, следующего за разделом описаний); аналогично если такое описание помещено непосредственно в спецификацию пакета, то активизация начинается после предвыполнения раздела описаний тела пакета. То же относится и к активизации задачного объекта, являющегося подкомпонентой объекта, описанной непосредственно в разделе описаний или спецификации пакета. Первый оператор, следующий за разделом описаний, выполняется только после окончания активизации задачных объектов.

Если при активизации одной из таких задач возбуждается исключение, то эта задача становится законченной (см. 9.4); на других задачах это прямо не отражается. Если во время активизации одна из этих задач становится законченной, то после завершения активизации (успешного или нет) всех задач возбуждается исключение TASKING_ERROR; исключение возбуждается непосредственно за разделом описаний перед выполнением первого оператора (непосредственно после зарезервированного слова begin ). Исключение TASKING_ERROR возбуждается лишь однажды, даже если во время активизации сразу несколько задач становятся законченными таким способом.

Если исключение возбуждается при предвыполнении раздела описаний или спецификации пакета, то любая созданная (прямо или косвенно) этим предвыполнением задача, которая еще не активизирована, становится завершенной и, таким образом, она никогда не активизируется (см. разд. 9.4 с определением завершенной задачи).

Приведенные выше правила предполагают, что в теле пакета без операторов присутствует пустой оператор. Для пакета без тела подразумевается тело пакета с единственным пустым оператором. Если пакет без тела описывается непосредственно в некотором программном модуле или в операторе блока, то его тело подразумевается в конце раздела описаний программного модуля или оператора блока; при наличии нескольких пакетов без тела порядок следования подразумеваемых тел пакетов не определен.

Задачный, объект, являющийся объектом или подкомпонентой объекта, созданного выполнением генератора, активизируется этим выполнением. Активизация начинается после инициализации объекта, созданного генератором; если несколько подкомпонент являются задач-ными объектами, они активизируются параллельно. Ссылочное значение, указывающее этот объект, возвращается генератором только после проведения этих активизации.

Если исключение возбуждается при активизации одной из таких задач, то она становится законченной задачей; на другие задачи этот факт не оказывает прямого воздействия. Если во время своей активизации одна из задач становится законченной, то после проведения активизации (успешной или нет) всех этих задач возбуждается исключение TASKING_ERROR;исключение возбуждается в той точке текста программы, где выполняется генератор. Исключение TASKING_ERROR возбуждается лишь однажды, даже если во время активизации сразу несколько задач становятся законченными таким образом.

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

Пример:

procedure P is А, В : RESOURCE; -— предвыполняет заданные объекты А и В С : RESOURCE; -— предвыполняет задачный объект С begin -— задачи А, В, С активизируются параллельно! перед выполнением первого оператора ... end;

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

Ссылки: вход 9.5, генератор 4.8, завершение задачи 9.4, задачный объект 9.2, задачный тип 9.1, законченная задача 9.4, исключение 11, предвыполнение 3.9, 11.1, обработка исключения 11.4, оператор 5, параллельное выполнение 9, подкомпонента 3.3, раздел описаний 3.9, тело задачи 9.1, тело пакета



Заданные типы и задачные объекты


Задачный тип является лимитируемым типом (см. 4). Следовательно, для объектов за-дачного типа не определены ни присваивание, ни предопределенное сравнение на равенство и неравенство; более того, вид out недопустим для формального параметра задачного типа.

Задачный объект — это объект задачного типа. Значение задачного объекта указывает задачу с входами соответствующего задачного типа, а ее выполнение определено соответствующим телом задачи. Если задачный объект является объектом или подкомпонентой объекта, заданными описанием объекта, то его значение определяется предвыполнением описания объекта. Если задачный объект является объектом или подкомпонентой объекта, созданными при выполнении генератора, то его значение определяется выполнением генератора. Для всех видов параметров, если фактический параметр указывает задачу, сопоставляемый формальный параметр указывает ту же задачу; это же относится к подкомпоненте фактического параметра и к соответствующей подкомпбненте сопоставляемого формального параметра; наконец, то же справедливо и для параметров настройки.



Зависимость задач. Завершение задач


Каждая задача зависит по крайней мере от одного родителя. Родитель — это конструкция, являющаяся либо задачей, либо в данный момент выполняемым оператором блока или подпрограммой, либо библиотечным пакетом (но не описанным в другом программном модуле). Зависимость от родителя является непосредственной зависимостью в следующих двух случаях:

а) Задача, указанная задачным объектом, который является объектом или подкомпонен-той объекта, созданными при выполнении генератора, зависит от родителя, предвыполняюще-го соответствующее описание ссылочного типа.

б) Задача, указанная другим задачным объектом, зависит от родителя, выполнение которого создает задачный объект.

Более того, если задача зависит от данного родителя, являющегося оператором блока, выполняемым другим родителем, то задача также косвенно зависит и от этого родителя; тоже справедливо, если данный родитель является подпрограммой, вызванной другим родителем, а также если данный родитель — задача (прямо или косвенно), зависящая от другого родителя. Зависимости существуют и для объектов личного типа, полное описание которого задано в терминах задачного типа.

Говорят, что задача закончила свое выполнение, когда осуществилось выполнение последовательности операторов, помещенных в ее теле за зарезервированным словом begin. Аналогично говорят, что блок или подпрограмма закончили свое выполнение, когда осуществилось выполнение соответствующей последовательности операторов. В случае оператора блока также говорят, что выполнение его закончилось при достижении операторов выхода, возврата или перехода, передающих управление из блока. В случае процедуры также говорят, что ее выполнение закончилось при достижении соответствующего оператора возврата. В случае функции также говорят, что ее выполнение закончилось после вычисления результирующего выражения в операторе возврата. Наконец, выполнение задачи, оператора блока или подпрограммы закончено, если при выполнении содержащихся в них соответствующих последовательностей операторов возбуждено исключение и нет соответствующего ему обработчика, а при его наличии — по окончании выполнения соответствующего обработчика.

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

С другой стороны, завершение задачи имеет место тогда и только тогда, когда ее выполнение достигло открытой альтернативы завершения в операторе отбора (см. 1) и удовлетворены следующие условия:

• Задача зависит от некоторого родителя, выполнение которого закончено (следовательно, не от библиотечного пакета).

• Каждая задача, зависящая от рассмотренного родителя, либо уже завершена, либо также ожидает открытой альтернативы завершения в операторе отбора.

Когда оба условия удовлетворены, задача становится завершенной вместе со всеми задачами, зависящими от этого же родителя.

Пример;

declare type GLOBAL is access RESOURCE; --CM 91 А, В : RESOURCE; G : GLOBAL; begin -— активизация А и В declare type LOCAL is access RESOURCE; X : GLOBAL := new RESOURCE; -— активизация X.all L : LOCAL := new RESOURCE; — активизация L.all С : RESOURCE; begin -— активизация С G := X; -- G и X указывают один и тот же задачный объект end: -— ожидание завершения С и L.all, но не X.all end; -- ожидание завершения А, В и G.all

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

Те же правила справедливы и для главной программы. Следовательно, для завершения главной программы необходимо завершение всех зависимых задач, даже если соответствующий задачный тип описан в библиотечном пакете. С другой стороны, завершение главной программы не зависит от завершения задач, в свою очередь зависящих от библиотечных пакетов;в языке не определено, требуется ли завершение таких задач.

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

Описание переименования вводит новое имя для уже существующего понятия и, следовательно, не порождает дальнейшей зависимости.

Ссылки: альтернатива завершения 1, библиотечный модуль 10.1, вызов подпрограммы 6.4, генератор 4.8, главная программа 10.1, задачный объект 9.2, исключение 11, обработчик исключения 11.2, объект 3.2, оператор 5, оператор блока 5.6, оператор возврата 5.8, оператор выхода 5.7, оператор перехода 5.9, описание 3.1, описание переименования 8.5, отбор с ожиданием 1, открытая альтернатива 1, пакет 7, подкомпонента 3.3, последовательность операторов 5.1, программный модуль 6, ссылочный тип 3.8, тело задачи 9.1, тело подпрограммы 6.3, указывать 9.1, функция