Oberon is dead, long live Oberon! Part 1. Some love more active
With this review article, I open a series of articles on the Active Oberon language and the A2 operating system written in this language.
So, meet - Active Oberon
The first publication on Active Oberon appeared in 1997, but it is clear that the language and its implementation appeared earlier. Over the years, there have been many changes in the language, the runtime environment has been revised, the operating system A2 has been written ...
A feature of the language that distinguishes it from other programming languages is the concept of Active Objects - objects that encapsulate the flow of execution. Active Oberon extends the Oberon language by introducing the object type Object, the concept of Activity associated with an object (stream), as well as synchronization and protection mechanisms from simultaneous access. The implementation of such mechanisms at the language level makes it possible to simplify the development and implementation of multithreaded applications and reduces the number of errors associated with the protection and synchronization problem.
In the import section, plug-ins can have aliases, different modules can have the same alias, forming from some modules a kind of namespace. In such modules, the names of exported entities must not overlap.
For instance:
IMPORT Example := Example1, Example := Example2;Active Oberon implements improved arrays (mathematical and tensor), structural arrays (objects compatible with improved arrays), complex types, operations for objects, improved arrays and complex types, delegates compatible with both module procedures and object methods. Instead of multiple inheritance, the concept of multiple inheritance of interfaces is used.
The syntax of the language in the process of development practically does not change - instead, semantic modifiers are introduced, changing the semantics of the syntactic structures present in the language. The list of modifiers is enclosed in braces {}.
So, to describe the delegate, the DELEGATE procedure modifier is used:
Example1 : PROCEDURE{DELEGATE, REALTIME} ( VAR parameter : LONGINT ) : BOOLEAN;The example describes the procedure variable Example1, pointing to a delegate - a procedure or a real-time method that takes a variable of type a long integer by reference and returns a value of a logical type.
Methods are described inside the object. All methods in Active Oberon are virtual. Methods preceded by the & sign are constructors and are called automatically when the object is created. The compiler selects the required constructor based on the call signature of the NEW built-in procedure, designed to create entities of a reference type. Objects and methods can also have ABSTRACT and FINAL modifiers with clear semantics. Operations (operators) can be described both at the object level and at the module level, with the exception of some operators described only at the object level (at least in the current implementation).
Active Objects and Multithreading Model
An object can have a body - the final block of statements executed after the object is initialized. If the body of the object is marked with the ACTIVE modifier (possibly with priority), then such an object is called active. As already mentioned, the activity (stream) is encapsulated in the object and is created at the time of the instantiation of the active object. Activity exists while the body of the active object is running. An active object continues to exist as long as its activity exists, even if there are no links to it, after which it becomes passive and can be disposed of in the usual way.
Communication between activities is carried out through calls to methods of active and inactive objects.
Protection against simultaneous access to object states is provided by support at the language level of exclusive regions (exclusive regions) - a sequence of statements enclosed in BEGIN ... END statement brackets marked with the EXCLUSIVE modifier - corresponding to the concept of critical Brinch Hansen areas. If a monopoly region covers the entire body of an object, it is called a monopoly region and combines the concept of Hansen's critical regions with the concept of the Hoare monitor procedure. Unlike Hoar’s monitor procedures, object methods are not required to be exclusive and can observe non-synchronized state of the object. A module is considered an object with a single instance. The compiler and runtime environment ensure that no more than one activity can be in a monopoly area at a time.
Thus, the protection model in Active Oberon is a monitor located in an instance of an object (instance-based monitor).
The main idea of the monitor is that an invariant is associated with it - an expression that defines the internal, consistent state and proves the correctness of its behavior. Object initializers establish an invariant, and monopoly methods support it.
For synchronization in Active Oberon, the AWAIT operator is used, while most monitor implementations use Hoare conditional variables based on Hansen queues. The AWAIT operator takes as argument the logical condition for the continuation of execution, in case of non-compliance with which the activity is suspended, and the captured exclusive region is freed until the expression condition becomes true. The activity will continue to work when it can again enter the previously released monopoly section. Continuation conditions are recounted only when any activity leaves the monopoly section.
An example of the description of the active object and the use of the exclusive section:
TYPE
Timer* = OBJECT
VAR
timer: Objects.Timer;
handler: Objects.EventHandler;
timeout, running: BOOLEAN;
PROCEDURE &Init*;
BEGIN
NEW(timer);
timeout := FALSE;
running := TRUE;
END Init;
PROCEDURE SetTimeout*(h: Objects.EventHandler; ms: LONGINT);
BEGIN
handler := h;
Objects.SetTimeout(timer, HandleTimeout, ms)
END SetTimeout;
PROCEDURE CancelTimeout*;
BEGIN
Objects.CancelTimeout(timer);
END CancelTimeout;
PROCEDURE HandleTimeout;
BEGIN {EXCLUSIVE}
timeout := TRUE
END HandleTimeout;
PROCEDURE Finalize*;
BEGIN {EXCLUSIVE}
Objects.CancelTimeout(timer);
running := FALSE
END Finalize;
BEGIN {ACTIVE}
WHILE running DO
LOOP
BEGIN {EXCLUSIVE}
AWAIT(timeout OR ~running);
IF ~running THEN
EXIT
END;
timeout := FALSE
END;
handler()
END
END
END Timer;
Memory management.
Active Oberon applies automatic memory management using a real-time garbage collector. This means that real-time activity can stop the garbage collection process. In areas of real-time code, memory allocation operations are prohibited by the compiler. Real-time procedures, methods, and activities are marked with the REALTIME modifier. Not all compiler and runtime implementations support real-time processes.
Variables marked with the UNTRACED modifier are untracked pointers and are not tracked by automatic memory management mechanisms.
The DISPOSE statement is syntactically supported in the language, but its implementation may be absent.
This concludes the review article of Active Oberon, I recommend starting dating with the classic Oberon.
References:
- Bug tracker, source code for OS A2, including Active Oberon compiler. Also in the repository are options A2 running under Windows and * nix
- Documentation and instructions
- Support Forum
- Criticism of the language of Oberon from the founding fathers. Excerpt from the dissertation by Patrick Reali , creator of the multi-threaded compiler of the Active Oberon language, JAOS JVM, which runs on top of the OS A2 runtime and has made a significant contribution to the development of Oberon Technologies.
Series Content
- Some love more active
- Modules
- Work Environment ETH Oberon