ARTS/C++(ID:7882/)


Realtime C++ dialect for the running and implementation of the ARTS system


Related languages
C++ => ARTS/C++   Extension of
ARTS/C++ => Real-Time C++   Replacement for

References:
  • Tokuda, H., Kotera, M., ?A Real-Time Tool Set for the ARTS Kernel,? Proceedings of the 9th IEEE Real-Time Systems Symposium, December, 1988 view details
  • Tokuda, H., Mercer, C.W. "ARTS: A Distributed Real-Time Kernel," Operating Systems Review, Vo1.23, No.3, July, 1989, pp.29-53 view details
  • Ishikawa, Yutaka and Tokuda, Hideyuki "Object-Oriented Real-Time Language Design: Constructs for Timing Constraints" pp289-298 view details Abstract: We propose a new object-oriented programming language called RTC++ for programming real-time applications. RTC++ is an extension of C++ and its features are to specify i) a real-time object which is an active entity, ii) timing constraints in an operation as well as in statements, and iii) a periodic task with rigid timing constraints. In this paper, we first discuss real-time programming issues and what language support should be provided for building real-time applications. Then, the key features of RTC++ are described. Some programming examples are shown to demonstrate RTC++'s expressive power. A comparison to other programming languages are also discussed. DOI Extract: Introduction
    Introduction
    Real-time computer systems play a very important role in our society. They are used in multimedia systems, robotics, factory automation, telecommunication systems, and in air traffic control systems. The objectoriented concept and programming languages make it easier to design and develop such complex real-time application programs. However, unlike non real-time programs, real-time programs must satisfy the liming correctness as well as logical correctness. Satisfying the timing correctness of a real-time program is difficult because of the lack of explicit specification of the timing constraints in a program and the lack of schedulability analysis techniques.
    For example, a real-time task must start at a specified time and complete its activity by its deadline. However, in a conventional real-time program, such timing constraints are not explicitly described in its program, rather described in a separate timing chart or document. Thus, it is very difficult to enforce timing constraints or detect timing errors during compile time or/and runtime. Although the data encapsulation in object-oriented programming languages will help us to confine logical errors in a program, timing errors often penetrate the module boundary.
    The schedulability analysis of a real-time program is also a difficult problem. By the schedulability analysis, we mean that a program designer should be able to analyze or predict whether the given real-time tasks having various types of system and task interactions (e.g., memory allocation/deallocation, message communications, I/O interactions, etc) can meet their timing constraints. For instance, if real-time tasks are interacting via shared resources, prediction of the worst case blocking time is difficult due to a possibility of having unbounded blocking delay.
    In order to eliminate such unbounded blocking delay, a real-time system must avoid priority inversion problems. A priority inversion problem occurs when a higher task must wait indefinitely for a lower priority task to execute. For example, priority inversion may occur in task scheduling: when a task attempts to get a shared resource, it is blocked if another task is keeping the resource. If the task keeping the resource has a lower priority and a middle priority task can run under prioritybased preemptive scheduling, then the blocked task has to wait for unbounded time. Thus, the system cannot guarantee to satisfy rigid timing constraints when priority inversion occurs.
    We have designed and implemented a real-time distributed operating system kernel called the ARTS kernel October 21-25, 1990 ECOOP/OOPSLA ?90 Proceedinp 289 and programming languages ARTS/C and ARTS/C++ both of which are running on the kernel[ll]. The ARTS kernel provides primitives of remote object invocation with timing constraints, methods for specifying periodic execution, and mechanisms for avoiding priority inversion.
    ARTS/C and ARTS/C++ are an extension of C and C++ respectively. Those have a capability of defining a special object called a real-lime object. A real-time object has a set of operation with timing constraints and threads each of which is an execution unit. Languages do not have linguistic support for avoiding priority inversion but they can call kernel primitives for that purpose. Our experiment with those languages leads to design more suitable object-oriented programming language which copes with real-time issues easily and efficiently.
    What capabilities we need in a real-time language are: i) expressions for timing constraints for statement level in addition to operation timing constraints, ii) linguistic support for avoiding priority inversion problem, and iii) an inheritance mechanism in a real-time object. In this paper, first we discuss real-time programming issues and describe what language supports are required. Then, a new object-oriented programming language called RTC++ is proposed in section 3. In section 4, some programming examples are shown in order to demonstrate the RTC++ programming power. In section 5, we compare RTC++ with other object-oriented programming languages and real-time programming languages.
    Extract: Timing Specification
    Real-Time Programming Issues
    2.1 Timing Specification
    Both ARTS/C and ARTS/C++ allow us to specify timing constraints of each operation in a real-time object. The objective of this support is as follows[ll]: In traditional real-time systems which use a cyclic executive, a timing error often penetrates the task or module boundary, so that it is very difficult to capture the error at runtime. By using the timing specification, we can bound the timing error at run-time as well as compile-time. Furthermore, we need to specify timing constraints for statement by statement in order to control execution depending on an external behavior. That is, when a part of statements (or transaction) in an operation cannot finish within the specified time, the current execution (or transaction) is aborted and then alternative statements are executed in order to satisfy timing constraint of the operation.
    Another construct we need is the capability of describing a periodic task. Such capability is often realized as the duration of the waiting time by a language or an operating system. However, this leads to the unbounded waiting time. For example, the following program written in Ada is considered:
    1 loop
    2 -- . . . body of cyclic activity . . .
    3 dtime := nexttime - currenttime;
    4 delay dtime;
    5 end loop
    In this example, because the execution of the statement at line 3 is not an atomic action the dtime variable may have wrong value. That is, if the execution of the program is suspended after the currenttime is evaluated and then the execution is resumed later, the dtime is calculated with the wrong value of currenttime. So the program might sleep for wrong time at the delay statement.
    Extract: Priority Inversion Problem
    Priority Inversion Problem
    Using object-oriented paradigm, the client-server model is suitable in a distributed application. This model introduces a priority inversion problem in a real-time application. For example, suppose that there are server's and clients A and B where A's priority is higher than B's one. Suppose that when the client A sends a message to the server's, the server is performing for a request of the client B. In that case the request of client A is postponed until the server's execution for client B is finished. Because A's priority is higher than B's one but processing for B is prior to A, we call this phenomena priority inversion in the server. In order to avoid the priority inversion in a server, three methods can be considered: i) preemption, ii) abort, and iii) priority inheritance.
    In the preemption method, the server's execution is preempted at the request of client A. Then, the server turns to perform for client A. After finishing the service for client A the service for client B is resumed. In the abort method, the server's execution is aborted at the request of client A and then the server turns to perform for client A. At the abort of the execution the server must be responsible for maintaining the consistency. […] If the server's execution cannot be preempted and the cost of the abort procedure is too high compared with waiting for finishing the current server's execution, waiting is the best method.
    However, this is not true when the server is running with other tasks. Suppose that the server's priority depends on the client's priority - and there is another task C whose priority is lower than the client A's priority but higher than the client B's priority. In this assumption, when C is ready to run, C begins to run while the server's performing for B is suspended. Thus, priority inversion occurs.
    This assumption is reasonable since the server has to serve for many clients whose priorities are different. To avoid such priority inversion, we use the notion of priority inheritance to propagate priority. That is, if a task provides a service on behalf of a client, the server'should inherit the priority of the client. Furthermore, the server'should inherit the priority of the highest priority task which is waiting for the service. Also, it is important to use priority queue instead of FIFO queue for a message queue.
    If we apply the priority inheritance mechanism to the above example, the server's?s priority is changed to client A?s priority at the request from A. Thus, C could not run even when it becomes ready to run. Extract: Single vs Multiple Threaded Object Model
    Single vs. Multiple Threaded Object Model
    Most object-oriented concurrent programming languages such Actor, ABCL/1, ConcurrentSmalltalk and Orient84/K provide an object with a single thread model. The idea of an object-oriented concurrent language is that an object is a sequential execution entity and concurrency is expressed by means of various message passing forms such as synchronous and asynchronous communication. The concept of sequential objects and message passing allows us to reduce programming complexity in a parallel application. Let us call this model the single threaded object model. As described in the previous subsection, a highly preemptable server is required in a real-time application. The mechanism we need is that if a request message is coming at a server from a client during the server's execution for a lower priority's client, the server's execution is preempted or aborted and then performs for the higher priority's client. However, if the lower priority's client requests a service to the server, the request should be enqueued.
    In object-oriented concurrent languages such a server can be implemented as follows. A server object consists of a root object and a set of objects each of which is responsible for one of the server's operation with a priority. Objects have to share the server's internal data so that each object can access the same data. For example, if two operations are defined in a server and the number of possible different priorities is three, then three objects each of which has a different priority are responsible for an operation and other three objects are responsible for the other operation. When a client sends a request message to the root object, the root object forwards the message to an object according to the client's request and priority. Another approach to describing a preemptable server is that the server is implemented based on the multiple threaded object model. In the multiple threaded object model, there are some threads of control in an object so 2Actor and ABCL/I support a reentrant object if the object has no internal data.
    That a thread is invoked at the new client request whose priority is higher than the current client's priority. Because the object has multiple threads, the concurrency control has to be employed.
    Conceptually the single threaded model and multiple threaded model have the same capability. So we have to consider the implementation of those models. The implementation of a highly preemptable server in the single threaded model needs more resources than one in the multiple threaded model because there are so many objects required. Moreover, if the internal data is primitive data such as integer or character, the compiler can generate an optimum code in the multiple threaded model while message passing forms are always needed in the single threaded model.
    Based on the above observation, we choose the multiple threaded model to describe a real-time application. In the multiple threaded model, no restrictions on dynamically creating threads in an object may lead to increasing the complexity of concurrency control. Thus, the restriction we choose is that each operation may be executed concurrently but an operation has to be executed by one thread at a time. Moreover, the execution may be preempted to realize a highly preemptable server. Extract: RTC++
    RTC++
    In this section, we propose an object-oriented real-time language called RTC++ which is an extension of C++. RTC++ is designed based on the previous discussion. The syntax and semantics are described with examples.
    Extract: Real-Time Object
    Real-Time Object
    In addition to C++ objects, RTC++ introduces an active object. If an active object is defined with timing constraints, it is called a real-time object. In the following example, the active class Example1 is defined.
    active class Example1 4
    private:
    char buf CBUF-SIZE] ;
    int count ;
    iut background0 ;
    public :
    int read(char+ data, int size) uhen(count > size);
    int urite(char* data, int size);
    int open0 ;
    int close0 ;
    act irity :
    slave [S] read(char*, int);
    slave [53 arite(char*, tit);
    slave openo, close0;
    master background0 cycle(;;Ot3Om;);
    An active object definition is almost the same as an original C++ object definition except for adding the keyword active before the class keyword in RTC++. An active object has a single thread of control in default. A user can specify multiple threads of control which we call member threads in an active object. Member threads are defined in an activity part of a class definition. There are two types of member threads, slave and master.
    A slave thread is an execution unit related to a member function or a group of member functions 3 . In the following definition, one slave thread is only responsible for the open and close requests.
    slave open0, close 0 ;
    A slave thread inherits the priority from a sender. If there are some waiting messages, the priority of the slave thread is set to the highest priority of those messages. When a new message for those functions is comming and the sender?s priority is higher than the current thread?s priority, the thread?s priority is changed to the higher priority. This mechanism is called the priotily inheritance mechanism in an object.
    In the following definition, the processing of the member function read can be preempted by up to five clients whose priorities are higher than the current execution of the read function. We call ?slave [S] ) J a slave thread group.
    slave [51 read(char+, int 1;
    A slave thread group does not realize just an interrupt mechanism. In order to illustrate the concept of the slave thread group, the following example is considered:
    read(char+ b, iat s)
    c



    I
    In this example, the read function consists of the sequence of the non-critical region, critical region, and noncritical region. Suppose that one of the thread group enters the critical region B and at that time a new read request where the sender?s priority is higher than the previous sender?s one is coming. Another new thread begins to execute the read function with the higher priority while the former thread is suspended. Since the critical region B is captured by the former thread, the higher priority?s thread cannot enter the region B and is blocked. So the former thread executes again until the exiting critical region B. After that, the higher priority?s thread is resumed. In this way, aslave thread group does not just realize an interrupt mechanism but supports a preemption mechanism.
    The priority inheritance in a slave thread group is as follows: When all slave threads of a group are employed for clients? requests and at that time a higher priority?s
    A member function is called a method in Smalltalk terminology. request than those threads? priorities is comming, then the highest priority?s thread of the group changes its priority to the new highest priority.
    A master thread is intended to use a background thread within an active object. For example, we want to specify the background thread which saves its internal data into a back up disk every 30 minute:
    master background0 cycle(;;Ot3cnl;);
    In this example, the cycle expression specifies that the background thread is a cyclic task. The following is the syntax of the cycle expression:
    cycle(; ; ; ) ;
    In the example, , , and are omitted so that those constraints are free. In RTC++, a guard expression[2] may be defined in a member function definition in order to control concurrency.
    A guard expression may consist of primitive data types such as integer, primitive operations such as addition, internal variables (or instance variables in Smalltalk terminology), and message variables. For example, the following definition specifies that iff the expression ?count > size? is true, the (?read(. . .> J J member function is invoked by a request, otherwise the invocation for the request is postponed until the expression becomes true:
    int read(char* data, int size) when(count > size);
    In addition to a guard expression, we can specify a function which is invoked when a request is postponed. For example, the definition below specifies that if the guard expression is false then the busy function is invoked. If the function returns 0 the request is rejected, otherwise it is enqueued to the message queue.
    int read(char* data, int size) uhenccount > size)
    onuait (busy()) ;
    An expression for creating an active object is the same as an original C++ new expression except for adding priority. For example, the following expression means that an instance of class Example1 is created with priority 4
    5.
    Example1 l v = new Example1 priority 4;
    When an instance object is created in the above expression,
    threads in the object have priority 4 at the first.
    4An example of the notation of time is that 8 hour 20 minute
    30 second and 10 millisecond 10 microsecond is specified as
    ?Ot8h20m3Os10.10?.
    5Priority is defined as number. Larger number represents higher priority.
    Extract: Inheritance
    Inheritance
    Unlike the previous language ARTS/C++, RTC++ supports the inheritance mechanism in an active object. An example below defines the Example2 active class derived from class Example1 which we call a base class in C++ terminology 6. In class Example2 member functions read and write are redefined and the control member function is defined. The activity parts of among a class and its base classes are merged consistently. That is, an instance of class Example2 has two slave thread groups defined in class Example1 and one slave thread defined in class Example2.
    actiro class Example2 : public Example1 {
    public:
    int read(char* data, int size);
    int arite(char+ data, int aizo);
    int control(...);
    activity:
    slave open0, close0, control(...);
    3
    It should be noted that if there are no activity parts of among an active class and its base classes, an instance of the class has only one thread which is responsible for all member functions.
    Extract: Communication
    Communication
    RTC++ supports synchronous communication. The syntax of communication among active objects is the same as C++ syntax. An example is shown below:
    Example1 l r;
    . . . .
    n = r->read(buf, size);
    . ..;
    RTC++ provides two means of sending a reply message, return and reply statements. The semantics of a return statement is that a reply message is sent to the sender and the execution of the function is finished. The semantics of a reply statement is that a reply message is sent and the subsequent statements are executed instead of finishing the execution of a member function. It should be mentioned that a message has a priority which is the same as the thread's priority of a sender.

          in [SIGPLAN] SIGPLAN Notices 25(10) October 1990 (OOPSLA/ECOOP '90) view details