Active Oberon — типобезпечна модульна об'єктно-орієнтована багатопотокова мова програмування загального призначення, яку розробила в 1996—1997 роках група професора у Швейцарській вищій технічній школі Цюриха (ETHZ) з метою введення в мову Оберон властивостей для вираження паралелізму за допомогою активних об'єктівJ. Gutknecht. Do the Fish Really Need Remote Control? A Proposal for Self-Active Objects in Oberon., 1997..
Особливості мови Назва Active Oberon відбиває основну концепцію мови — концепцію активних об'єктів, виражену в реалізації багатопотоковості та механізмів синхронізації на рівні мови.
Active Oberon розширює мову Оберон, вводячи поняття об'єкт (Object) та активність (Activity), пов'язана з об'єктом. Такий зв'язок називають активним об'єктом (Active Object), що означає здатність примірника об'єкта мати активність — власний потік виконання.
Мова належить до модульних типобезпечних мов програмування зі сильною статичною типізацією, допускаючи у виразах неявне зведення скалярних типів без втрати даних (наприклад, використання цілого числа там, де передбачалося використання числа з рухомою комою).
Модулі забезпечують не тільки розділену компіляцію, інкапсуляцію, але й можливість реалізації динамічного завантаження/вивантаження скомпільованих (об'єктних) модулів, що використовується, наприклад, у операційній системі A2, написаній цією мовою. Певною мірою, аналогом модуля можна вважати динамічно приєднувану бібліотеку.
В Active Oberon, як і в інших спадкоємцях Модули-2 при зверненні до сутностей приєднаного (імпортованого) модуля потрібно обов'язково кваліфікувати приєднаний модуль. Наприклад, якщо модуль A приєднує модуль B і використовує змінну цього модуля, то звернення до змінної повинне мати форму B.v. Інакше кажучи, імпорт не дозволяє за замовчанням імпортувати з приєднаного модуля всі сутності, які він експортує.
Інкапсуляція побудована на концепції модуля — всі типи, оголошені в модулі, повністю прозорі один для о́дного, а для доступу зовнішніх клієнтів потрібні специфікатори доступу. Специфікатори доступу дозволяють експортувати сутності або з повним доступом (ідентифікатор позначається знаком «*» (зірочка)), або з доступом «тільки для читання» (ідентифікатор позначається знаком «-» (мінус)). Наприклад, конструкція:VAR Example2 : PROCEDURE {REALTIME, C} ( VAR low, high: LONGINT ): BOOLEAN;визначає тип запису (RECORD) Example1, експортований за межі модуля, що має три поля з типом «довге ціле», причому поле «x» оголошено зі специфікатором доступу «повний доступ», поле «y» — зі специфікатором «доступ тільки для читання» і поле z є прихованим полем, недоступним зовнішнім клієнтам.
Мова підтримує поліморфізм, перевантаження операцій (для структурних типів), делегати, сумісні як із методами, так і з процедурами. Об'єктними типами є RECORD і посилальний тип OBJECT. Вони можуть мати методи та операції. У типу OBJECT можуть бути тіло та активність. Усі методи є віртуальними. Множинне успадкування відсутнє, замість нього використовують концепцію множинного успадкування інтерфейсів (DEFINITION у термінах мови).
Синтаксис та семантика Синтаксис мови в процесі розвитку практично не змінюється — розробники воліють уточнювати семантику вже наявних синтаксичних конструкцій за допомогою введення семантичних модифікаторів, що дозволяє виключити значний обсяг змін при введенні нової функціональності, спростити компілятор, зробивши його код доступнішим для розуміння і модифікації, а також забезпечити легкість вивчення та використання мови. Модифікатори беруть у фігурні дужки {} після імені змінної, типу чи ключового слова. Наприклад, конструкція:VAR Example2 : PROCEDURE {REALTIME, C} ( VAR low, high: LONGINT ): BOOLEAN;оголошує процедурну змінну Example2, що вказує на процедуру реального часу, з домовленістю про виклик CCALL, яка приймає два параметри типу довге ціле і повертає значення логічного типу.
Опис об'єкта, в цілому, відповідає опису модуля, за винятком синтаксису заголовка та відсутності секції IMPORT. Методи повністю описуються всередині опису об'єкта, операції, за рідкісним винятком, можуть описуватися поза тілом об'єкта. Об'єкт може мати довільну кількість ініціалізаторів і не більше одного фіналізатора. Вбудована процедура NEW, що використовується для створення змінних посилального типу, викликає ініціалізатор, який компілятор вибирає за сигнатурою параметрів. Ініціалізатор позначають символом & перед назвою методу. Фіналізатор — метод без параметрів, перед іменем якого стоїть знак ~, — автоматично викликається для утилізації об'єкта.
Послідовність операторів, укладену в операторні дужки BEGIN END, називають блоком операторів. Блок операторів також може містити список модифікаторів та секцію гарантованого завершення (FINALLY).
Змінну, а також поле запису або об'єкта, можна ініціалізувати константним виразом при оголошенні:TYPE Point = RECORD x := 0, y := 0 : LONGINT; END;
VAR i := 0, j := 10, k := 100 : INTEGER; Point : Point; (* поля x, y запису ініціалізовано значенням 0 *)
Типи даних Мова пропонує багатий набір вбудованих типів:
Базові типи логічний: BOOLEAN; символьні: CHAR8, CHAR16, CHAR32 та псевдонім CHAR для типового символьного типу; цілочисельні знакові: SIGNED8, SIGNED16, SIGNED32, SIGNED64 та псевдоніми SMALLINT, INTEGER, LONGINT, HUGEINT; цілочисельні беззнакові: UNSIGNED8, UNSIGNED16, UNSIGNED32, UNSIGNED64; дійсні: REAL, LONGREAL; комплексні: COMPLEX, LONGCOMPLEX; машинозалежні: SIZE, ADDRESS; множини: SET, SET8, SET16, SET32, SET64; розширювані переліки: ENUM; структурні масиви: ARRAY — статичні, динамічні, відкриті, математичні; розширювані структури: RECORD; об'єктні: OBJECT; Спеціальні процедурні; делегати: процедурні змінні, сумісні як із процедурами, і з методами; типізовані вказівники на структурні типи; узагальнені вказівники: ANY, OBJECT, ARRAY; системні: SYSTEM.BYTE
Багатопотоковість В Active Oberon реалізовано дві моделі багатопотоковості, засновані на роботах Брінча Гансена та Тоні ГоараP.J. Muller. Active Object System. Design and Multiprocessor Implementation. — ETH Zurich, 2002., Diss. ETH № 14755.:
Витискальна багатопотоковість, з блокувальною синхронізацією; Кооперативна багатопоточність, з неблокувальною (lock-free) синхронізацієюFlorian Negele. Combining Lock-Free Programming with Cooperative Multitasking for a Portable Multiprocessor Runtime System, ETH Zurich, 2014, Diss. ETH №. 22298. Компілятор у певних місцях автоматично генерує код перемикання потоків. У цьому разі активності (потоки), фактично, є співпрограмами. Середовище виконання надає можливість ручного перемикання на необхідну активність.
Сирцевий код, написаний з використанням синтаксису блокувальних примітивів синхронізації мови Active Oberon, можна використовувати для обох моделей багатопотоковості — компілятор генеруватиме код, потрібний для конкретної моделі. За такого підходу немає потреби у переписуванні програмного забезпечення під різні моделі. Лише невеликі ділянки сирцевого коду вимагають адаптації (наприклад, обробка переривань), щоб придушити автоматичне генерування перемикань у цій ділянці машинного коду. Для цього блок операторів позначають модифікатором {UNCOOPERATIVE}.
Активні об'єкти Потік інкапсульовано в об'єкті і, як його невід'ємна частина, він створюється в момент інстанціювання активного об'єкта. Для вказівки на активність об'єкта його тіло позначають модифікатором 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.
Міжпроцесова взаємодія Виклики методів спільно використовуваних активних та неактивних об'єктів діють як комунікаційний механізм між активними об'єктами. Оскільки активні об'єкти існують у багатопотоковому середовищі, надається механізм для координування паралельного доступу до їхнього стану. Взаємодія через обмін повідомленнями може здійснюватися з використанням спеціальних програмних каркасівFlorian Negele. A2 Concurrency Framework, ETH Zurich, June 3, 2009.
Захист від одночасного виконання Блок операторів, позначений модифікатором EXCLUSIVE, називають монопольною ділянкою (exclusive region). Монопольна ділянка в Active Oberon відповідає концепції критичної ділянки ГансенаP. Brinch Hansen. Structured Multiprogramming. Communications of the ACM, 15(7), July 1972. Якщо монопольна ділянка охоплює все тіло методу, його називають монопольним методом (exclusive method) і він поєднує концепцію Гансена з процедурою монітора ГоараP. Brinch Hansen. Operating System Principles. Prentice-Hall, 1973.C.A.R. Hoare. Monitors: An Operating System Structuring Concept. Communications of the ACM, 17(10):549-557, October 1974.. На відміну від процедури монітора, метод об'єкта може бути монопольним — у такому разі він може спостерігати неузгоджені стани об'єкта. У монопольній ділянці може бути не більше однієї активності одночасно.
Таким чином, модель захисту в Active Oberon — монітор, розміщений у примірнику об'єкта (instance-based monitor). Модуль вважають об'єктним типом з єдиним примірником (singleton instance) і його процедури теж можуть бути монопольними, захищаючи модуль загалом.
Головна ідея монітора (і активного об'єкта) у тому, що з монітором пов'язується якийсь інваріант — вираз, що визначає несуперечливий внутрішній стан об'єкта і доводить правильність його поведінкиO. J. Dahl. Monitors Revisited. In A.W. Roscoe, editor, A Classical Mind — Essays in Honour of C.A.R. Hoare. Prentice-Hall, 1994.. Ініціалізатори та монопольні методи є інструментами, які мова надає для підтримки інваріантів об'єкта та приховування його внутрішнього стану. Ініціалізатор об'єкта встановлює його інваріант, а монопольні методи його підтримують. Коли поняття монітора об'єднано з поняттям модуля, формується потужний механізм для структурування операційних системJ.L. Keedy. On Structuring Operating Systems With Monitors. ACM Operating Systems Review, 13(1), January 1979.N. Wirth. Modula: A Language for Modular Multiprogramming. Software — Practice and Experience, 7:3-35, 1977.N. Wirth. The Use of Modula. Software — Practice and Experience, 7:37-65, 1977..
Приклад використання монопольної секції:(* Процедури 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;
Синхронізація