Scheduling (Fixed Schedule, Prios, #2)

Simpler Solution

Here’s a simpler scheduling solution with process priorities.

Process Priorities

Process priorities are added using one single, NIL-terminated list of processes.

  • The process list is sorted by

    1. process priority, with higher priorities closer to the head in the list
    2. process period, with shorter periods closer to the head in the list
  • It is assumed that the lower timer number means shorter period. As any specific timer can be selected for a period, this is not a relevant restriction, but results is simpler code.

  • Priority and period are not connected. A high priority process can have a long period.

  • If its run-time exceeds the period, a process can lock out all processes of lower priority, as well as processes of the same prio, but with longer periods, but not processes of higher priority.

  • The number of priority levels is not restricted by the concept and implementation. Priorities are positive integers, including zero, with lower priority numbers meaning higher process priority.

Regarding the lockout aspect, the solution described here prevents lockouts of processes of the same prio as the locking process, but at some conceptual and code complexity cost, also restricting the number of priority levels to the number of process rings fixed in code. To detect erroneous permanent or temporary lockouts of lower priorities, other measures have to be implemented anyway. Hence, this solution here is not substantially restricted from a practical point of view, but more lightweight.

Oberon.Loop

The algorithm in Oberon.Loop is basically a simplification of the solution with the rings. All remarks there also apply, mutatis mutandis.

MODULE Oberon;

  VAR
    procs: Process; (* list of processes *)
    cp: Process;    (* current process *)

PROCEDURE Loop*;
    VAR
      res: INTEGER;
      ch: CHAR;
      done: BOOLEAN;
      p: Process;
  BEGIN
    REPEAT
      IF Console.Available() > 0 THEN
        (* command and upload handling *)
      ELSE
        (* load ready bits *)
        ProcTimers.LoadReadyStatus;
        ProcDevSig.LoadReadyStatus;

        (* GC timing ... *)

        (* check the processes *)
        done := FALSE;
        IF procs # NIL THEN
          p := procs;
          REPEAT
            IF ProcDevSig.ProcessReady(p.pcNo) THEN     (* device signals *)
              ProcDevSig.ResetSignals(p.pcNo);
              IF p.state = AwaitDevsigTo THEN
                ProcTimers.CancelDelay(p.pcNo)
              END;
              p.retVal := OK;
              p.state := Active; cp := p; Coroutines.Transfer(loop, p.cor); cp := NIL;
              done := TRUE
            ELSIF ProcTimers.ProcessReady(p.pcNo) THEN    (* timing *)
              ProcTimers.SetPeriod(p.pcNo, p.period);
              p.retVal := OK;
              IF p.state = AwaitDevsigTo THEN
                ProcDevSig.ResetSignals(p.pcNo);
                p.retVal := Timeout
              END;
              IF ~(p.state IN {Suspended, AwaitDevsig}) THEN
                p.state := Active; cp := p; Coroutines.Transfer(loop, p.cor); cp := NIL;
                done := TRUE
              END
            END;
            p := p.next
          UNTIL done OR (p = NIL)
        END
      END
    UNTIL FALSE
  END Loop;

END Oberon.