**6. Servers**

12 Will-be-set-by-IN-TECH

In order to satisfy our initial requirement for long event interarrival time, we chose for a linked-list implementation of RELTEQ queues. In future work we look into relaxing this

The task concept is an abstraction of a program text. There are roughly three approaches to periodic tasks, depending on the primitives the operating system provides. Figure 5 illustrates the possible implementations of periodic tasks, where function *fi*() represents the body of task

*TaskMakePeriodic*(*τi*, *φi*, *Ti*);

*Registration:*

*RegisterPeriodic*(*fi*(), *φi*, *Ti*);

*TaskWaitPeriod*();

(a) (b) (c)

In Figure 5.a, the periodic behavior is programmed explicitly while in Figure 5.b this periodicity is implicit. The first syntax is typical for a system without support for periodicity, like *μ*C/OS-II. It provides two methods for managing time: *GetTime*() which returns the current time, and *DelayFor*(*t*) which delays the execution of the current task for *t* time units relative to the time when the method was called. As an important downside, the approach in Figure 5.a may give rise to jitter, when the task is preempted between *now* := *GetTime*() and

In order to go from Figure 5.a to 5.c we extract the periodic timer management from the task in two functions: a registration of the task as periodic and a synchronization with the timer system. A straightforward implementation of *TaskWaitPeriod()* is a suspension on a semaphore. Note that we wait at the beginning of the while loop body (rather than at the end) in case *φ<sup>i</sup>* > 0. Going from interface in Figure 5.b to 5.c is now a simple implementation

Note that the task structure described in Figure 5.b guarantees that a job will not start before the previous job has completed, and therefore makes sure that two jobs of the same task will

In order to provide the periodic task interface in 5.b, we need to implement a timer which

To support periodic tasks we introduce a new kind of RELTEQ events: a *period event*. Each period event *ei* points to a task *τi*. The expiration of a period event *ei* indicates the arrival of

expires periodically and triggers the task waiting inside the *TaskWaitPeriod()* call.

not overlap if the first job's response time exceeds the task's period.

*Registration:*

*Task τ<sup>i</sup>* : **while** *true* **do**

*fi*(); **end while**

requirement.

*Task τ<sup>i</sup>* : *k* := 0; **while** *true* **do**

*DelayFor*().

issue.

*now* := *GetTime*();

*k* := *k* + 1; *fi*(); **end while**

*DelayFor*(*φ<sup>i</sup>* + *k* ∗ *Ti* − *now*);

**RELTEQ primitives for periodic tasks**

**5. Periodic tasks**

*τ<sup>i</sup>* (i.e. the actual work done during each job of task *τi*).

Fig. 5. Possible implementations of a periodic task.

A server *σ<sup>i</sup>* is created using *ServerCreate*(Π*i*, Θ*i*, *kind*), where *kind* specifies whether the server is *idling periodic* or *deferrable*. A task *τ<sup>i</sup>* is mapped to server *σ<sup>i</sup>* using *ServerAddTask(σi, τi)*.

In Section 4.3 we have introduced a system queue, which keeps track of pending timed events. For handling periodic tasks assigned to servers we could reuse the system queue. However, this would mean that the tick handler would process the expiration of events local to inactive servers within the budget of the running server.

In order to limit the interference from inactive servers we would like to separate the events belonging to different servers. For this purpose we introduce additional RELTEQ queues for each server. We start this section by introducing additional primitives for manipulating queues, followed by describing how to use these in order to implement fixed-priority servers.

### **6.1 RELTEQ primitives for servers**

We introduce the notion of a pool of queues, and define two pools: *active queues* and *inactive queues*. They are implemented as lists of RELTEQ queues. Conceptually, at every tick of the periodic timer the heads of all active queues are decremented. The inactive queues are left untouched.

To support servers we extend RELTEQ with the following methods:

*ActivateQueue(qi)* Moves queue *qi* from the inactive pool to the active pool.

*DeactivateQueue(qi)* Moves queue *qi* from the active pool to the inactive pool.


**6.2.1 Example of the stopwatch behavior**

A

B

C

Stopwatch

3 B 0 B

0 A

Fig. 6. Example of the stopwatch queue.

is deleted, its time is added to *C*.*se*.

out.

*B*.*se* is added to *C*.*se*.

which *C*.*se* is deleted, and *B*.*se* with time 0 is inserted.

0 C

4 A 0 C

The stopwatch queue is a great example of RELTEQ's strength. It provides an efficient and concise mechanism for keeping track of the inactive time for *all* servers. Figure 6 demonstrates the behavior of the stopwatch queue for an example system consisting of three servers *A*, *B* and *C*. It illustrates the state of the stopwatch queue at different moments during execution, before the currently running server is switched out and after the next server is switched in.

An Efficient Hierarchical Scheduling Framework for the Automotive Domain 81

**Legend**

running server stopwatch event **before** server switch stopwatch event **after** server switch

0 C

0 C

0 5 10 15 20 25

4 A

3 C

> 7 C

> 0 B

0 A 0 C

> 4 A

2 B 4 A

> 6 A

9 C

7 C

0 B

4 A

2 B 0 C

0 B

3 C

9 C 6 A

Initially, when server *σ<sup>i</sup>* is created, a stopwatch event *σi*.*se* with time 0 is inserted into the stopwatch queue. At time 0 server *A* is switched in and its stopwatch event is removed. While server *A* is running, the tick handler increments the head of the stopwatch queue, which happens to be the stopwatch event of server *B*. At time 3, when server *A* is switched out and server *B* is switched in, server *B* synchronizes its absolute queue with the stopwatch queue until and including *B*.*se*, *B*.*se* is deleted, and *A*.*se* with time 0 is inserted. Note that when *B*.*se*

At time 7 server *C* is switched in, its absolute queue is synchronized with time 4 + 3 = 7, after

At time 9, since no server is switched in, no synchronization is taking place and no stopwatch event is deleted. Only stopwatch event *C*.*se* with time 0 is inserted, since server *C* is switched

At time 16, when server *B* is switched in and its stopwatch event *B*.*se* is deleted, the time of

2 B

## **6.2 Limiting interference of inactive servers**

To support servers, we add an additional *server queue* for *each* server *σi*, denoted by *σi*.*sq*, to keep track of the events local to the server, i.e. delays and periodic arrival of tasks *τ<sup>j</sup>* ∈ *γ*(*σi*). At any time at most one server can be active; all other servers are inactive. The additional server queues make sure that the events local to inactive servers do not interfere with the currently active server.

When a server *σ<sup>i</sup>* is switched in its server queue is activated by calling *ActivateQueue(σi.sq)*. In this new configuration the hardware timer drives two event queues:


When the active server is switched out (e.g. a higher priority server is resumed, or the active server gets depleted) then the active server queue is deactivated by calling *DeactivateQueue(σi.sq)*. As a result, the queue of the switched out server will be "paused", and the queue of the switched in server will be "resumed". The system queue is never deactivated.

To keep track of the time which has passed since the last server switch, we introduce a *stopwatch*. The stopwatch is basically a counter, which is incremented with every tick. In order to handle time overflows discussed in Section 4.1, we represent the stopwatch as a RELTEQ queue and use *IncrementQueue(stopwatch)* to increment it.

During the time when a server is inactive, several other servers may be switched in and out. Therefore, next to keeping track of time since the last server switch, for each server we also need to keep track of how long it was inactive, i.e the time since that particular server was switched out. Rather than storing a separate counter for each server, we multiplex the stopwatches for all servers onto the single stopwatch which we have already introduced, exploiting the RELTEQ approach. We do this by inserting a *stopwatch event*, denoted by *σi*.*se*, at the head of the stopwatch queue using *InsertEvent(stopwatch, σi*.*se)* whenever server *σ<sup>i</sup>* is switched out. The event points to the server and its time is initially set to 0. The behavior of the tick handler with respect to the stopwatch remains unchanged: upon every tick the head event in the stopwatch queue is incremented using *IncrementQueue(stopwatch)*.

During runtime the stopwatch queue will contain one stopwatch event for every inactive server (the stopwatch event for the currently active server is removed when the server is switched in). The semantics of the stopwatch queue is defined as follows: the accumulated time from the head of the queue until (and including) a stopwatch event *σi*.*se* represents the time the server *σ<sup>i</sup>* was switched out.

When a server *σ<sup>i</sup>* is switched in, its server queue is synchronized with the stopwatch using *SyncQueuesUntilEvent(σi.sq, stopwatch, σi.se)*, which handles all the events in *σi*.*sq* which might have occurred during the time the server was switched out. It accumulates the time in the stopwatch queue until the stopwatch event *σi*.*se* and handles all the events in *σi*.*sq* which have expired during that time. Then *σi*.*se* is removed from the stopwatch queue. When *σ<sup>i</sup>* is switched out, *σi*.*se* with time 0 is inserted at the head of the stopwatch queue.
