Активный Оберон

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск
Активный Оберон
OberonLogo.png
Класс языка:

императивный, модульный, объектно-ориентированный, многопоточный, структурный, типобезопасный

Тип исполнения:

компилируемый

Появился в:

1997 год

Автор:

Юрг Гуткнехт (Jürg Gutknecht), Patrik Reali, B. Meyer, Brinch Hansen, Pieter Muller

Расширение файлов:

Mod

Система типов:

статическая, сильная

Основные реализации:

Active Oberon.Net, FOX, PACO, Ronin

Диалекты:

Zonnon

Испытал влияние:

Паскаль, Модула-2, Оберон, Object Oberon

Повлиял на:

Active C#[1], Go, Zonnon

Сайт:

ocp.inf.ethz.ch/wiki/Documentation/Language

Платформа:

ARM, Cell, x86, x86-64, .NET

ОС:

A2

Активный Оберон (англ. Active Oberon) — компилируемый, типобезопасный, модульный, объектно-ориентированный, многопоточный язык программирования общего назначения, разработанный в 1996 — 1997 гг. группой проф. Гуткнехта[de] в Швейцарской высшей технической школе Цюриха (ETHZ), с целью введения в язык Оберон свойств для выражения параллелизма посредством активных объектов (автономных программных агентов)[2].

Особенности языка[править | править вики-текст]

Название Активный Оберон отражает основную концепцию языка — концепцию Активных Объектов, выраженную в реализации многопоточности и механизмов синхронизации на уровне языка.

Активный Оберон расширяет язык Оберон, вводя в язык понятия Объект (Object) и Активность (Activity), связанная с Объектом. Такая связь называется Активным Объектом (Active Object), и означает способность экземпляра объекта иметь собственный поток выполнения[2].

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

Модули обеспечивают не только раздельную компиляцию, инкапсуляцию, но и возможность реализации динамической загрузки/выгрузки скомпилированных (объектных) модулей, что используется, например, в операционной системе A2, написанной на этом языке. Аналогом модуля является динамически подключаемая библиотека (dll).

В Активном Обероне, как и в других наследниках Модулы-2, при обращении к сущностям подключенного (импортированного) модуля, требуется обязательная квалификация подключенного модуля. Например, если модуль A подключает модуль B и использует переменную v этого модуля, то обращение к переменной должно иметь форму B.v. Иначе говоря, импорт в Активном Обероне не позволяет по умолчанию импортировать из подключенного модуля все экспортируемые им сущности.

Инкапсуляция построена на концепции модуля — все типы, объявленные в модуле, полностью прозрачны друг для друга, а для доступа внешних клиентов требуются спецификаторы доступа. Спецификаторы доступа позволяют экспортировать сущности либо с полным доступом (идентификатор помечается знаком «*»(звёздочка)), либо с доступом «только для чтения» (идентификатор помечается знаком «-» (минус)). Например, конструкция:

TYPE
  Example1* = RECORD x*, y-, z : LONGINT; END;

определяет тип записи (RECORD) Example1, экспортированный за пределы модуля, и имеющий три поля с типом «длинное целое», причем поле «x» объявлено со спецификатором доступа «полный доступ», поле «y» объявлено со спецификатором «доступ только для чтения», и поле «z» является скрытым полем, недоступным внешним клиентам.

Язык поддерживает полиморфизм, перегрузку операций, делегаты, совместимые как с методами, так и с процедурами. Объектным типом является ссылочный тип OBJECT. Концептуально, объект соответствует указателю на запись, но, в отличии от неё, может иметь методы, операции, тело объекта и активность. Все методы являются виртуальными.

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

Синтаксис и семантика[править | править вики-текст]

Синтаксис языка в процессе развития практически не изменяется — разработчики предпочитают уточнять семантику уже имеющихся синтаксических конструкций с помощью вводимых семантических модификаторов, что позволяет исключить значительный объём правок при введении новой функциональности, упростить компилятор, сделав его код более доступным для понимания и модификации, а также обеспечить легкость изучения и использования языка. Модификаторы заключаются в фигурные скобки {} после имени переменной, типа или ключевого слова. Например, конструкция:

Example2 : PROCEDURE {REALTIME, C} ( VAR low, high: LONGINT ): BOOLEAN;

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

Описание объекта, в целом, соответствует описанию модуля, за исключением синтаксиса заголовка и отсутствия секции IMPORT. Методы целиком описываются внутри описания объекта. Объект может иметь произвольное количество инициализаторов и не более одного финализатора. Встроенная процедура NEW, использующаяся для создания переменных ссылочного типа, вызывает инициализатор, выбираемый компилятором по сигнатуре параметров. Инициализатор помечается знаком & перед именем метода. Финализатор — метод без параметров, перед именем которого стоит знак ~, вызывается автоматически при утилизации объекта.

Последовательность операторов, заключенная в операторные скобки BEGIN END, называется блоком операторов. Блок операторов также может иметь список модификаторов и секцию гарантированного завершения (FINALLY).

Типы данных[править | править вики-текст]

Язык предлагает богатый набор встроенных типов:

  • Базовые типы
  • логический: BOOLEAN;
    • символьные: CHAR8, CHAR16, CHAR32 и псевдоним CHAR для символьного типа по умолчанию;
    • целочисленные знаковые: SIGNED8, SIGNED16, SIGNED32, SIGNED64 и псевдонимы SMALLINT, INTEGER, LONGINT, HUGEINT;
    • целочисленные беззнаковые: UNSIGNED8, UNSIGNED16, UNSIGNED32, UNSIGNED64;
    • вещественные: REAL, LONGREAL;
    • комплексные: COMPLEX, LONGCOMPLEX;
    • адаптивные: SIZE, ADDRESS;
    • множество: SET;
    • расширяемые перечисления: ENUM;
  • структурные
    • массивы: ARRAY — статические, динамические, открытые, математические;
    • расширяемые структуры: RECORD;
    • объектные: OBJECT;
  • Специальные
    • процедурные;
    • делегаты: процедуры и методы, помеченные модификатором DELEGATE;
    • типизированные указатели на структурные типы;
    • обобщенные указатели: ANY, OBJECT, ARRAY;
  • системные: SYSTEM.BYTE

Многопоточность[править | править вики-текст]

Модель многопоточности в Активном Обероне основана на работах Бринча Хансена и Тони Хоара[3].

Активные Объекты[править | править вики-текст]

Поток инкапсулирован в объекте и, являясь его неотъемлемой частью, создаётся в момент инстанцирования активного объекта. Для указания активности объекта его тело помечается модификатором ACTIVE.

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

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

Пример описания и использования активного объекта:

MODULE Example3;
 
TYPE
  ActiveObject = OBJECT
  VAR state : SET;
 
    PROCEDURE &New;
    BEGIN
      state := {};
    END New;
 
    PROCEDURE &Init*( state : SET );
    BEGIN
      SELF.state := state;
    END Init;
 
    PROCEDURE ~Finalize;
    BEGIN
    ...
    END Finalize;
 
  BEGIN {ACTIVE}
    ...
  END ActiveObject;
 
VAR
  object : ActiveObject;
 
BEGIN
  NEW( object );
  object.Init( {0..7, 9, 12, 30..31} );
  NEW( object, {} );
END Example3.

Межпроцессное взаимодействие[править | править вики-текст]

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

Защита[править | править вики-текст]

Блок операторов, помеченный модификатором EXCLUSIVE называется монопольной областью (exclusive region). Монопольная область в Активном Обероне, соответствует концепции критической области Хансена[4]. Если монопольная область охватывает всё тело метода, то он называется монопольным методом (exclusive method) и совмещает концепцию Хансена с процедурой монитора Хоара[5][6]. В отличии от процедуры монитора, метод объекта не обязан быть монопольным — в этом случае он может наблюдать несогласованные состояния объекта. В монопольной области может находитьcя не более одной активности одновременно.

Таким образом, модель защиты в Активном Обероне — монитор, размещенный в экземпляре объекта (instance-based monitor). Модуль считается объектным типом с единственным экземпляром (singleton instance) и его процедуры тоже могут быть монопольными, защищая модуль в целом.

Главная идея монитора (и активного объекта) в том, что с монитором связывается некий инвариант — выражение, определяющее непротиворечивое внутреннее состояние объекта и доказывающее правильность его поведения[7]. Инициализаторы и монопольные методы являются инструментами, предоставляемыми языком, для поддержания инвариантов объекта и сокрытия его внутреннего состояния. Инициализатор объекта устанавливает его инвариант, а монопольные методы его поддерживают. Когда понятие монитора объединено с понятием модуля, оно формирует мощный механизм для структурирования операционных систем[8][9][10].

(* Процедуры Set и Reset взаимно исключаемы *)
TYPE
  MyContainer = OBJECT
    VAR x, y: LONGINT; (* Инвариант: y = f(x) *)
 
    PROCEDURE Set(x: LONGINT);
    BEGIN {EXCLUSIVE} (* изменение x и y атомарно *)
      SELF.x := x; y := f(x)
    END Set;
 
    PROCEDURE Reset;
    BEGIN
      ...
      BEGIN {EXCLUSIVE} (* изменение x и y атомарно *)
        x := x0; y := y0;
      END;
      ....
    END Reset;
  END MyContainer;

Синхронизация[править | править вики-текст]

В отличии от большинства реализаций мониторов, использующих для синхронизации условные переменные Хоара[6] (на основе очередей событий Бринча Хансена[4]), синхронизацию активностей обеспечивает оператор AWAIT, принимающий в качестве аргумента логическое выражение — условие продолжения выполнения программы в теле объекта. Чтобы гарантировать правильность синхронизации, AWAIT должен находится в монопольной области. В случае невыполнения условия продолжения, AWAIT приостанавливает (suspend) активность и, если находится в монопольной области, отпускает захваченную область на время приостановки, что позволяет другим активностям изменить состояние объекта и сделать условие продолжения истинным. Приостановленная активность продолжит работу только в том случае, если сможет повторно войти в монопольную область.

Пример синхронизации внутри разделяемого буфера:

TYPE
  Synchronizer = OBJECT
  VAR awake: BOOLEAN
 
  PROCEDURE Wait;
  BEGIN {EXCLUSIVE}
    AWAIT(awake);
	awake := FALSE;
  END Wait;
 
  PROCEDURE WakeUp;
  BEGIN {EXCLUSIVE}
    awake := TRUE;
  END WakeUp;
END Synchronizer;

Исключительные ситуации[править | править вики-текст]

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

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

После обработки исключения управление передаётся в ближайшую по стеку вызовов секцию гарантированного завершения FINALLY, если она есть. Затем, если тело активного объекта помечено модификатором SAFE, будет произведён перезапуск активности, в противном случае работа активности завершается.

Среда времени выполнения[править | править вики-текст]

Информация о типах времени выполнения[править | править вики-текст]

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

Управление памятью[править | править вики-текст]

В Активном Обероне применяется автоматическое управление памятью с использованием прерываемого (вытесняемого) сборщика мусора реального времени[11], основанного на методе пометок (Mark and Sweep). Сборщик мусора выполняется в отдельном потоке, и активности (потоки), имеющие приоритет выше, чем приоритет активности сборщика мусора, могут приостановить его выполнение. При этом дерево объектов замораживается. На текущий момент только сущности реального времени могут прерывать активность сборщика мусора, в них запрещено динамическое выделение памяти, за этим следит компилятор.

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

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

Управление активностью объектов[править | править вики-текст]

Среда времени выполнения отвечает за распределение процессорного времени, гарантирует (совместно с компилятором), что в монопольной области находится не более одной активности, обеспечивает своевременную проверку условий оператора AWAIT и возобновление работы приостановленных активностей. Выражения условий продолжения внутри объекта пересчитываются во всех точках выхода из монопольных областей. Это означает, что изменение состояния объекта вне монопольной области не приводит к пересчёту условий. Когда несколько активностей соревнуются за одну и ту же монопольную область, то активности с выполненными условиями рассматриваются раньше тех, которые только хотят войти в защищенную область[3][12].

Подключение и инициализация модулей[править | править вики-текст]

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

Обработка исключительных ситуаций[править | править вики-текст]

Модуль Traps обеспечивает централизованную обработку исключительных ситуаций. Параметры, принимаемые операторами ASSERT и HALT, могут использоваться для классификации исключительной ситуации.

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

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

Расширения языка[править | править вики-текст]

Математическое расширение[править | править вики-текст]

Примеры программ[править | править вики-текст]

Hello, World![править | править вики-текст]

MODULE HelloWord;
IMPORT KernelLog;
 
BEGIN
  KernelLog.String( "Hello, World!" );
END HelloWorld.

Решение классической задачи поставщика и потребителя[править | править вики-текст]

MODULE BoundedBuffers;
TYPE
  Item* = OBJECT;
 
  Buffer* = OBJECT
    VAR
      h, n: INTEGER;
      B: ARRAY * OF Item;
 
    PROCEDURE Get*(): Item;
    VAR x: Item;
    BEGIN {EXCLUSIVE}
      AWAIT(n # 0); (* буфер не пуст *)
      x := B[h]; h := (h+1) MOD LEN(B); DEC(n);
      RETURN x
    END Get;
 
    PROCEDURE Put*(x: Item);
    BEGIN {EXCLUSIVE}
      AWAIT(n # LEN(B)); (* буфер не полон *)
      B[(h+n) MOD LEN(B)] := x; INC(n)
    END Put;
 
    PROCEDURE &Init(max: INTEGER);
    BEGIN (* инициализатор *)
      NEW(B, max); h := 0; n := 0
    END Init;
  END Buffer;
 
END BoundedBuffers.


Ссылки[править | править вики-текст]


См. также[править | править вики-текст]

Примечания[править | править вики-текст]

  1. Страница проекта Active C#
  2. 1 2 J. Gutknecht. Do the Fish Really Need Remote Control? A Proposal for Self-Active Objects in Oberon., 1997.
  3. 1 2 P.J. Muller. Active Object System. Design and Multiprocessor Implementation. — ETH Zurich, 2002., Diss. ETH № 14755.
  4. 1 2 P. Brinch Hansen. Structured Multiprogramming. Communications of the ACM, 15(7), July 1972
  5. P. Brinch Hansen. Operating System Principles. Prentice-Hall, 1973.
  6. 1 2 C.A.R. Hoare. Monitors: An Operating System Structuring Concept. Communications of the ACM, 17(10):549-557, October 1974.
  7. O. J. Dahl. Monitors Revisited. In A.W. Roscoe, editor, A Classical Mind — Essays in Honour of C.A.R. Hoare. Prentice-Hall,
    1994.
  8. J.L. Keedy. On Structuring Operating Systems With Monitors. ACM Operating Systems Review, 13(1), January 1979.
  9. N. Wirth. Modula: A Language for Modular Multiprogramming. Software — Practice and Experience, 7:3-35, 1977.
  10. N. Wirth. The Use of Modula. Software — Practice and Experience, 7:37-65, 1977.
  11. Ulrike Glavitsch. Real-time Garbage Collection in A2. Institute of Computer Systems, ETH Zurich
  12. P. Reali. Active Oberon Language Report. — ETH Zurich, 2004.