4 Add selected agent to charge list
5 Manage Timers
6 Update kernel state
7 Context switch to selected agent
Agent context
1 Start of loop 0 Kernel initialisation
Find next agent to run
Find next timer 3 Select next agent to run
2 ♦ Run reason:
♦ Pending scheduler operation?
Select scheduling op ◉ Agent request ◉ External interrupt ◉ Oak timer ◉ No ◉ Yes Figure 26
The Oak Run-Loop control flow. Oak.Core
context switch was voluntary (as part of an agent service request) or because an interrupt was raised (from an external or timer interrupt). Furthermore, if an agent had voluntarily yielded the processor, the kernel agent records the location of the agent’s Oak Message and copies the message into its stack.13 During the context switch to the kernel agent, the context switching routines provide a reason for the kernel agent running:
▶ An agent voluntarily yielded to Oak ▶ An external interrupt requires handling ▶ An Oak Timer requires handling
The latter two situations causes a running agent to involuntarily yield the pro- cessor to the kernel, which Oak notes in the agent’s Agent_Interrupted property to enable correct restoration of the interrupted agent’s context.
Once noted, this stage responds to the reason for run.
An agent voluntarily yields to the kernel agent to request a state change or an Oak provided services. As part of the yield, the agent provides an Oak Message containing the desired request (Listing 50 on page 110).
Here the kernel agent acts on the agent’s request, with the agent ending in the requested state or passing through the requested state to another. For example, an agent requesting the Sleeping state will end up in that state if the supplied wake time lies in the future. Otherwise, the agent will remain in a Running or
Runnable state, since the wake time has passed.
Handling an agent’s request may result in the agent’s scheduler requiring no- tification of changes to the agent’s scheduling properties, or may see an agent added or removed from a scheduler agent. In these cases, the kernel agent pushes the required scheduling operations onto its Scheduler_Ops stack. Later in the run-loop, the kernel agent will dispatch these operations.
Here the kernel agent finds the interrupt agent responsible for handling the ex- ternal interrupt by matching the priority of the interrupt to the corresponding agent located in the kernel agent’s Interrupt Agent Table. It then transfers the
external interrupt details to the interrupt agent’s object and notes the interrupt agent as active within the kernel agent’s Interrupt_States bit-field.
Oak handles three kinds of Oak Timers: scheduler, cycle and timing event tim- ers. The handling of a scheduler timer depends on the state of the timer’s associ- ated scheduler agent. If active and not time for a nested scheduler to deactivate, the kernel agent will push a Selecting_Next_Agent scheduler operation onto its Scheduler_Ops stack: allowing the scheduler agent to manage its queues. If it is time to deactivate a nested scheduler agent (since the agent exhausted its
13. Since agents pass the address of their Oak Message as part of the voluntary context switch.
2
◆ Run Reason
agent request
external interrupt
oak timer
115 ACTON A REAL-TIME EXECUTIVE FOR ADA
budget or passed its deadline), the kernel agent removes the nested scheduler agent from its parent and arranges its next reactivation. Reactivation occurs when the nested scheduler agent’s timer fires while it is inactive, causing the nested scheduler agent added back to its parent.
The handling of a cycle timer, on the other hand, depends on the Timer_Action
property of the timer (which uses Cyclic Task Specification’s Event_Response
type). Since Oak only supports the No_Response and Handler responses, Oak treats the Abort_Cycle and Abort_Task options as No_Response, which the
Run-Loop ignores. When responding with a handler, Oak follows a process
similar to handling external interrupts: activating the corresponding interrupt agent and recording the handler to execute. Timing event timers use the same process. In all cases, the fired timer is deactivated.
Once the run-loop has handled the reason for run, attention turns to selecting
the next agent to dispatch. First, the kernel agent checks for scheduler oper- ations on its Scheduler_Ops stack. If an operation is found, the kernel agent will pop the operation off its stack and select the associated scheduler agent as the next agent to run. The kernel agent copies the scheduling operation into the scheduler agent’s Oak Message store and sets the priority of the system to maximum. The Run-Loop then progresses to the next stage.
If the Scheduler_Ops stack is empty, the kernel searches for the next agent to dispatch. First, the kernel agent consults its active interrupt agent and protected broker lists, choosing the agent or broker with the highest priority: selecting the broker if they have the same priority or the No_Agent if the kernel has no active interrupt agents or protected brokers. The kernel agent then selects the highest priority agent from its top-level schedulers if the agent has a priority greater than the currently selected agent or broker. The top-level scheduler query executes by reading the top-level scheduler agents’ Agent_To_Run component. At this point the kernel agent will have selected an agent or a broker, with the selected item’s priority becoming the system priority. However, the kernel agent does not dispatch selected protected brokers or nested scheduler agents. Instead, if the kernel agent has selected a protected broker, it now elects to dis- patch the task from the protected broker’s Tasks_Within component: allowing
the task inside a protected object to run. Similarly, if a nested scheduler agent has been selected, the kernel agent will select its Agent_To_Run in its place. At this point, the kernel agent will have an agent to dispatch, even if it is the
No_Agent (meaning no other agent was found). Since the No_Agent doubles as the sleep agent, the No_Agent is a valid selection: putting the processor to sleep when dispatched.
A kernel agent maintains a budgets to charge list: a list of agents who will have
their execution budgets consumed by the selected agent. At this step, the kernel agent adds the selected agent to the charge list so it consumes its own budget. This only occurs if the agent’s execution budget is not Time_Span_Last.