3.2.2. Scheduling

3.2.2.1. Introduction

EGOS is composed of processes. The goal of the use of some processes is multiple. This page focuses on the advantage of sharing execution time by using processes. There is an automatic time sharing done by the scheduler and it can also be done by the application when it is wished.

3.2.2.2. Scheduler behaviour

The scheduler, provided with the operating system services in the kernel, allows to periodically and automatically share running time between the alive processes. The running processes should be understood like a list of processes.

3.2.2.2.1. Default behaviour

When some processes are ready to run in EGOS, the scheduler is automatically called every millisecond when the systick interrupt of the microcontroler occurs. It goes then in a critical section to unhandle the potential other interrupts which may happen. When a process is running, it means that the scheduler is not called from a start-up sequence of EGOS and that this process ran already for one scheduler window, its running time is incremented. Afterward, if it has not already ran its allocated duration, there is no change: the process stays active, else it has already ran its allocated duration, it signifies that its running turn is over: its execution time is reset and the next process in the list is get.

Depending on the state of the new process, this process is going to run:

  • Run: in all of the case
  • Delay: only if the delay is lower than the current time
  • Msg wait: only if a new message is received
  • Msg wait delay: only if a new message is received or if a the delay is lower than the current time
  • Pause: never

If the condition related to a state is not true, the process is not going to run. In this case, the scheduler goes back in the process list and takes the next one. It checks the state of the new one until it founds one process ready to run in the list.

At the end, in the case of a process is ready to run, the scheduler switches the running process to the found one. It saves the context of the previous process by triggering a PENDSV hardware interrupt. This interrupt allows a full context backup before switching to the next process.

sch_inter_process

3.2.2.2.2. Advanced behaviour

3.2.2.2.2.1. Increase the running time of a process

Some process can need more time to finish a huge load of work without being interrupted. In order to satisfy this specification, the process can be declared with mOSS_EXE__PROC_ADVANCED instead of the standard mOSS_EXE__PROC which allows to customize the process with a priority and a stack' size different from the default configuration. Be aware that the customized priority will not change the order of processes' execution, it will add some additional SysTick periods to run (for example: a priority of 3 allows the process to run during 3ms without being interrupted by the SysTick interrupt each millisecond).

mOSS_EXE__PROC_ADVANCED
( app_main_tst_Proc                                         ///< Process function
, 0                                                                         ///< Size of private chest
, STR_OS("egos.app.Proc")                           ///< Process name
, STR_OS("1.0")                                                     ///< Process version
, 3                                                                         ///< Process priority
, 2048                                                                      ///< Size stack
)
{
    //...
}

image

3.2.2.2.2.2. Decrease the running time of a process

On the other side, some processes may need only few microseconds instead of the full SysTick millisecond. In order to have an application more efficient: when a process has finished its job, it can call mOSS_EXE__SWITCH_PROC(). It is going to do the same loop as the default behavior. It adds an additional running time at the end of the loop before the PENDSV trigger. The following process is going to have the remaining duration of the previous process and its running duration, if it does not call a new process switch.

It will save the context of the current process and then give the hand to another process. The following one will run during the remaining duration of the previous process (which is the switch caller) in addition to its execution duration (depending on its priority). It means that the following SysTick interrupt after the process switch will be not handled. On the following picture, between 0 and 2 ms, P1 is running for 0.5 ms and P2 for 1.5 ms (0.5 of P1 and 1 ms) with the standard priority.

image

mOSS_EXE__SWITCH_PROC() is called without parameter, the following process in the process list is scheduled.

Remove a process for a duration

The sleep feature allows the developer to put a process in a "delayed" state (eOSS_EXE_PROC__STATE__DELAY) during a specific time which is defined in the parameter of the function (in milliseconds or in microseconds, both is feasible). It means that the process is not able to run in the scheduling list. If the i_hProcess is not filled in this macro, the current process is going to sleep.

  • The millisecond alternative:
egos_oss_exe_SleepMs
( .i_hProcess    = l_hHandle               ///< Handle of the targeted process
, .i_nDelay      = 2                       ///< Time in ms to sleep
);
  • The microsecond alternative:
egos_oss_exe_SleepUs
( .i_hProcess    = l_hHandle                  ///< Handle of the targeted process
, .i_nDelay      = 2000                    ///< Time in us to sleep
);
  1. The call of this function indicates how long the process must sleep. It adds the sleep time with the current timer value to know when it will be awoken.
  2. It changes the state of the process for the scheduler loop.
  3. Each time the scheduler will update the running process, it will compare the current time with the end of the wait state of the process. If the current time is lower than the wake-up time, the process will not be running. If it is the opposite (the current timer is greater the wake-up time), the process will change to the running state.

Be aware that if the wake-up time comes when the process is not called by the scheduler, it will be running in the following scheduler time. That means that if the process is sleeping for 5 seconds, it will not necessary be running at the end of those 5 seconds but at the following scheduler loop.

The defined sleep duration is global, which means that the sleeping time elapses also when the process is sleeping. The sleep duration is defined on the global timer of EGOS. Let's take an example: we want to put in sleep a process which is scheduled with 9 other processes. Each process takes 1 millisecond at each scheduler period. So a scheduler period is 10 milliseconds. If we define a 10 millisecond sleep duration for one process, this process will be only squeezed for 1 period, and not for ten periods.

Let's imagine an EGOS architecture with parent-child relationships. If a parent process is sleeping, all of the descending tree (all of its child process) will be put in sleep state.

A process can put itself or another one in the wait state. The only condition to do that is to have the handle of the process which will be put in wait state.

graph TD EGOS-->P1; P1-->P11; P1-->P12;

With this example:

  • if EGOS is sleeping: all process.
  • if P1 is sleeping: P11 & P12 are also sleeping.
  • if P11 is sleeping: no one else is (no child).

This function is useful when the sleep time is known, when an event is expected the freeze function is more relevant.

3.2.2.2.2.3. Remove a process for an undefined duration

The freeze feature enables a process to be in the suspended state (eOSS_EXE_PROC__STATE__SUSPEND) until another process unfreezes it. A process can freeze itself or another one but it cannot unfreeze itself, another one must to it.

egos_oss_exe_Freeze
( .i_hProc = l_hProc           ///< Handle of the freezed entity
);

The freeze mode will freeze only the current process the descending tree will stay active. The frozen time is not known.

Then to unfreeze the process by another one:

egos_oss_exe_Unfreeze
( .i_hProc = l_hProc           ///< Handle of the unfreezed process
);

3.2.2.2.2.4. Add a process only when it received a message

A process may only be used when another process sent it a message to it. In the other case this process is useless so it does not need to have time to be executed. The message loop defined by the macro mOSS_MSG__RECEIVE() allows to have this kind on behavior for a process.

mOSS_MSG__RECEIVE()
{
    // Handle here the wished message
}
mOSS_MSG__LOOP()

In this example, the process caller is going to be executed each time that it receives a message.

3.2.2.2.2.5. Add a process only when it received a message or a duration expired

A process may only be used when another process sent it a message to it but may also need to be executed periodically, to refresh some for example. In the other case this process is useless so it does not need to have time to be executed. The message loop defined by the macro mOSS_MSG__RECEIVE_UNTIL(timeout) allows to have this kind on behavior for a process.

mOSS_MSG__RECEIVE_UNTIL(5000)
{
    // Handle here the wished message
}
mOSS_MSG__LOOP()

In this example, the process caller is going to be executed each time that it receives a message or each 5 seconds.

To have more information on the message behavior, please refer to related message page.

3.2.2.3. Scheduling flow

  • Each time a process is created, it takes the first place in the queue of the scheduler.

  • The parent process runs before the child process.

  • The descending tree of the running process is executed before the right sibling process. Let's imagine the process tree below:

graph TD; EGOS-->P1; EGOS-->P2; P2-->P21;

If the execution order is P2 then P1 on the high level (without regarding bottom processes), then the scheduler will first give the hand to P2 then P21 (child of P2) and finally to P1 (sibling of P2).

3.2.2.4. Scheduler summary

The behavior of the scheduler is showed on the following schematics:

graph TD A(Scheduler begin)-->B[Critical section begin] B-->C[Get the running process] C-->C1{Is there a process running ?
Not a start up sequence} C1-->|True|D[Increment the running time of the process] C1-->|False|C2[Use the root process] C2-->R D-->E{Is the priority higher than running time
OR
is not from automatic scheduling ?
OR
Is there a process running ?
?} E-->|True|F[Has finished its run] F-->G[Reset the running time of the current process] G-->H[Get the next process in the list of processes] H-->I{What is the state of the process ?} I-->K[RUN] K-->L11 I-->L[DELAY] L-->L1{Is still delayed ?} L1-->|True|L11[Shall run] L1-->|False|L12[Stay in its current state] I-->M[MSG_WAIT_DELAY] M1-->|True|L11 M1-->|False|L12 M-->|OR|L1 M-->|OR|M1{Receive a new message ?} I-->N[MSG_WAIT] N-->M1 L11-->O{Is not from automatic scheduling} I-->J[PAUSE] J-->L12 L12-->H O-->|True|P[Add an additional time window] O-->|False|R P-->R[Trigger a process switch] R-->S[Critical section end] E-->|False|Z[Has more ticks to run] S-->T(Scheduler end)

We can consider five ways of management:

  • Sleep mode: This mode is used when the sleep duration of the concerned process is known. By example, when the process needs to run every minute to get the temperature: in that case we know that the process is going to be awoken every minute and then get back to sleep.
  • Freeze mode: On the contrary to the sleep mode, the freeze mode is used when the sleep time is not known, like when the process is waiting for the end of another job. If the current process is waiting for the temperature before giving the command to the conditioner, we don't know, in this process' scope, when the temperature is going to be refreshed. So the current process is unfreeze when the temperature is refreshed, then when the job is done, it is freeze again.
  • Process switch mode: when the process has finished its job, it can give the hand to following process. It is going to give the remaining duration of its allowed execution to the following process. The following process will run for its own duration in addition to the remaining execution of the previous process unless the new process will do a new switch. It deletes process' dead time. For example, a process has only a message loop in execution but has no new message to read, it can switch to a new process.
  • Priority mode: When a process needs more time to execute a non-interruptible job, it is possible to have more than the standard execution window. Its standard priority can be changed to run in a longer execution window. For example, a process received a data buffer which needs more than the execution period to be read, its priority should be increase (the priority number is the number of standard priority windows allowed to run each time).
  • Message mode: when a process needs to run when it receives a message, the message loop will put the process in a message wait state which allows the scheduler to execute it when a new message is received. For example a dispatcher process, which receives messages from its environment does not need to run when nothing happened and only runs when a new message is received.

3.2.2.4.1. Example

image

Priorities:

  • egOS.app.P1 1 (standard)
  • egOS.app.P2 2
  • egOS.app.P3 3

Process switch:

  • P1 does not do a process switch when it finishes its job
  • P2 & P3 do a process switch when they finish their job

Load duration:

  • P2 has a load of 200 s
  • P3 has a load of 2500 s
  • Only P2 and P3 do a mOSS_EXE__SWITCH_PROC at their work end

P3 does its job and gives the hand to the following (P2)

P2 takes the hand but the SysTick occurs, it keeps the hand because it does not have its full running period. During the following time window, it gives the hand to the following process (P1)

P1 takes the hand but as mOSS_EXE__SWITCH_PROC was done by a process (it means it didn't have its full running period) it kept the hand and ran for a full additional ms.