Ада-95. Компилятор GNAT

       

Инструкция перенаправления очереди requeue


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

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

Общий вид этой инструкции следующий:

requeue имя_входа [ with abort ] ;

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

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

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

В случае тела инструкции принятия рандеву (accept), задача-сервер управляет своим собственным состоянием, и в данном случае атомарность также обеспечивается, так как она может отвергнуть принятие любого промежуточного вызова.

Инструкция перенаправления requeue предназначена для обработки двух основных ситуаций:

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

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

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

    В обоих случаях, инструкция принятия accept или тело защищенного входа, нуждается в передаче управления.




    Таким образом, могут быть обработаны запросы от других клиентов или выполнена какая-либо другая обработка.

    Инструкция перенаправления requeue

    позволяет разделить обработку оригинального запроса/вызова на два (и более) этапа.

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

    Задачи приостанавливаются для ожидания некоторого события, а после того как оно происходит, все приостановленные задачи продолжают свое выполнение, а событие очищается.

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

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

    Нам позволяет запрограммировать такое предпочтительное управление инструкция перенаправления requeue:

    protected Event is

    entry Wait; entry Signal;

    private

    entry Reset; Occurred: Boolean := False;

    end Event;

    protected body Event is

    entry Wait when Occurred is

    begin

    null; -- пустое тело! end Wait;

    entry Signal when True is -- барьер всегда True begin

    if Wait'Count > 0 then

    Occurred := True; requeue Reset; end if; end Signal;

    entry Reset when Wait'Count = 0 is

    begin

    Occurred := False; end Reset;

    end Event;

    Задачи указывают на то, что они желают ждать событие, выполняя вызов:

    Event.Wait;

    а возникновение события индицируется тем, что какая-либо задача выполняет вызов:

    Event.Signal;

    после чего, все приостановленные в ожидании события задачи продолжат свое выполнение и событие очистится.

    Таким образом, последующие вызовы входа Wait работают корректно.

    Логическая переменная Occurred обычно имеет значение False.

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

    Вход Wait существует, но фактически не имеет тела.

    Таким образом, вызывающие задачи приостанавливают свое выполнение в его очереди, ожидая того, что переменная Occurred



    получит значение True.

    Особый интерес представляет вход Signal.

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

    Если нет задач ожидающих событие (нет задач вызвавших вход Wait), то его вызов просто ничего не делает.

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

    Он выполняет это перенаправляя себя на вход Reset

    (с помощью инструкции перенаправления requeue) после установки флага Occurred в значение True, для индикации появления события.

    Семантика инструкции перенаправления requeue

    подобна тому, что описано при рассмотрении алгоритма работы Signal.

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

    В этом случае, действительно существуют задачи находящиеся в очереди входа Wait, и существует задача в очереди входа Reset

    (та задача которая перед эти вызвала вход Signal).

    Барьер для Wait теперь имеет значение True, а барьер для Reset, естественно, False, поскольку очередь задач на входе Wait не пуста.

    Таким образом, ожидающая задача способна выполнить тело входа Wait

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

    Этот процесс повторяется до тех пор, пока все приостановленные в ожидании задачи не возобновят свое выполнение и значение барьера для Reset не получит значение True.

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

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



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

    Это иллюстрирует два уровня защиты и является смысловой основой отсутствия возможности появления состязания задач за ресурс.

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

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

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

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

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

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

    Без этой проверки, вызвавшая задача будет просто всегда осуществлять перенаправление requeue

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

    Однако это условие делает описание более ясным и чистым.

    Еще более внимательный читатель может заметить, что мы можем запрограммировать этот пример на Ada95 вовсе без использования инструкции перенаправления requeue.

    Как видно из общего вида инструкции перенаправления requeue, она может быть указана с принудительным завершением "with abort".

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

    Такое поведение весьма обосновано.

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



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

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

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

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

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

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

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

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

    Инструкция перенаправления requeue

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

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



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

    В процессе выполнения перенаправления, не происходит никаких переопределений параметров.

    Вместо этого, значения параметров прямо переносятся в новый вызов.

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

    Это может вызвать некоторые трудности, поскольку выполнение инструкции принятия accept или тела защищенного входа будет завершено в результате выполнения инструкции перенаправления requeue

    и локальные переменные будут, таким образом, деаллоцированы (deallocated).

    Необходимо соответствие используемых подтипов между вновь вызываемым целевым входом (если он имеет какие-либо параметры) и текущим входом.

    Это позволяет использовать то же самое представление для нового множества параметров, когда они передаются по значению (by-copy) или по ссылке (by-reference), а также исключить необходимость размещения (allocate) нового пространства для хранения параметров.

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

    В заключение, как общий итог обсуждения инструкции перенаправления requeue

    и логики ее использования, сделаем следующие выводы:

    Инструкция перенаправления requeue

    допустима внутри тела защищенного входа или внутри инструкции принятия рандеву accept.

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

    Возможно использование любых перечисленных комбинаций.


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

    Следовательно, новый вход должен иметь такой же самый профиль параметров или не иметь никаких параметров.


    Содержание раздела