1 (* Processes.mod implement the ISO Processes specification.
3 Copyright (C) 2009-2024 Free Software Foundation, Inc.
4 Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
6 This file is part of GNU Modula-2.
8 GNU Modula-2 is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3, or (at your option)
13 GNU Modula-2 is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. *)
27 IMPLEMENTATION MODULE Processes
;
29 FROM Assertion
IMPORT Assert
;
30 FROM SYSTEM
IMPORT ADDRESS
, ADR
;
31 FROM COROUTINES
IMPORT COROUTINE
, NEWCOROUTINE
, TRANSFER
, IOTRANSFER
, CURRENT
, ATTACH
, DETACH
, IsATTACHED
, HANDLER
, LISTEN
, ListenLoop
;
32 FROM Storage
IMPORT ALLOCATE
, DEALLOCATE
;
33 FROM RTExceptions
IMPORT IsInExceptionState
, GetExceptionBlock
, GetNumber
, Raise
;
34 FROM M2EXCEPTION
IMPORT M2Exceptions
;
35 FROM M2RTS
IMPORT NoException
;
37 FROM EXCEPTIONS
IMPORT ExceptionSource
, RAISE
, AllocateSource
, CurrentNumber
,
38 IsCurrentSource
, IsExceptionalExecution
;
40 FROM libc
IMPORT printf
;
44 defaultSpace
= 1024 * 1024 * 8 ;
48 (* The following procedures create processes and switch control between
52 ProcessId
= POINTER TO RECORD
54 workSpace
: CARDINAL ;
60 right
, left
: ProcessId
;
63 Status
= (ready
, waiting
, passive
, dead
) ;
66 process
: ExceptionSource
;
67 pQueue
: ARRAY Status
OF ProcessId
;
70 currentId
: ProcessId
;
74 New - assigns, p, to a new ProcessId.
77 PROCEDURE New (VAR p
: ProcessId
) ;
90 Dispose - returns, p, to the free list.
93 PROCEDURE Dispose (VAR p
: ProcessId
) ;
101 add - adds process, p, to queue, head.
104 PROCEDURE add (VAR head
: ProcessId
; p
: ProcessId
) ;
113 p^.left
:= head^.left
;
114 head^.left^.right
:= p
;
121 sub - subtracts process, p, from queue, head.
124 PROCEDURE sub (VAR head
: ProcessId
; p
: ProcessId
) ;
126 IF (p^.left
=head
) AND (p
=head
)
134 p^.left^.right
:= p^.right
;
135 p^.right^.left
:= p^.left
141 Add - adds, p, to the appropriate queue.
144 PROCEDURE Add (p
: ProcessId
) ;
146 add (pQueue
[p^.state
], p
)
151 Remove - remove, p, from the appropriate queue.
154 PROCEDURE Remove (p
: ProcessId
) ;
156 sub (pQueue
[p^.state
], p
)
161 OnDeadQueue - removes process, p, from the queue and adds it
165 PROCEDURE OnDeadQueue (p
: ProcessId
) ;
174 OnReadyQueue - removes process, p, from the queue and adds it
178 PROCEDURE OnReadyQueue (p
: ProcessId
) ;
187 OnPassiveQueue - removes process, p, from the queue and adds it
188 to the passive queue.
191 PROCEDURE OnPassiveQueue (p
: ProcessId
) ;
194 p^.state
:= passive
;
200 OnWaitingQueue - removes process, p, from the queue and adds it
201 to the waiting queue.
204 PROCEDURE OnWaitingQueue (p
: ProcessId
) ;
207 p^.state
:= waiting
;
213 checkDead - check to see if any processes are on the dead queue
214 and if they are not the current process deallocate
218 PROCEDURE checkDead
;
223 WHILE (p#
NIL) AND (p#currentId
) DO
228 DEALLOCATE (stack
, workSpace
)
238 RotateReady - rotate the ready queue, as an attempt to introduce some scheduling fairness.
241 PROCEDURE RotateReady
;
243 IF pQueue
[ready
] #
NIL
245 pQueue
[ready
] := pQueue
[ready
]^.right
254 PROCEDURE chooseProcess () : ProcessId
;
260 head
:= pQueue
[ready
] ;
264 IF (best
= NIL) OR (p^.urgency
>= best^.urgency
)
270 Assert (best #
NIL) ;
271 Assert (best^.state
= ready
) ;
277 Reschedule - rotates the ready queue and transfers to the process with the highest
281 PROCEDURE Reschedule
;
288 best
:= chooseProcess () ;
293 displayProcesses ("Reschedule")
295 (* the best process to run is different to the current process, so switch. *)
298 TRANSFER (p^.context
, currentId^.context
)
304 Create - creates a new process with procBody as its body,
305 and with urgency and parameters given by procUrg
306 and procParams. At least as much workspace (in
307 units of SYSTEM.LOC) as is specified by extraSpace
308 is allocated to the process. An identity for the
309 new process is returned in procId. The process is
310 created in the passive state; it will not run
314 PROCEDURE Create (procBody
: Body
; extraSpace
: CARDINAL; procUrg
: Urgency
;
315 procParams
: Parameter
; VAR procId
: ProcessId
) ;
320 workSpace
:= extraSpace
+ defaultSpace
;
322 ALLOCATE (stack
, workSpace
) ;
323 NEWCOROUTINE (procBody
, stack
, workSpace
, context
) ;
324 params
:= procParams
;
334 Creates a new process, with parameters as for Create.
335 The process is created in the ready state; it is eligible to
339 PROCEDURE Start (procBody
: Body
; extraSpace
: CARDINAL; procUrg
: Urgency
;
340 procParams
: Parameter
; VAR procId
: ProcessId
) ;
342 Create (procBody
, extraSpace
, procUrg
, procParams
, procId
) ;
348 StopMe - terminates the calling process.
349 The process must not be associated with a source
355 OnDeadQueue (Me ()) ;
361 SuspendMe - causes the calling process to enter the passive state.
362 The procedure only returns when the calling process
363 is again activated by another process.
366 PROCEDURE SuspendMe
;
370 displayProcesses ("SuspendMe")
372 OnPassiveQueue (Me ()) ;
378 doActivate - activate procId and pass, info, in the parameter field.
381 PROCEDURE doActivate (procId
: ProcessId
; info
: Parameter
) ;
383 procId^.params
:= info
;
384 OnReadyQueue (procId
) ;
390 Activate - causes the process identified by procId to enter the ready
391 state, and thus to become eligible to run again.
394 PROCEDURE Activate (procId
: ProcessId
) ;
396 doActivate (procId
, NIL)
401 SuspendMeAndActivate - executes an atomic sequence of SuspendMe() and
405 PROCEDURE SuspendMeAndActivate (procId
: ProcessId
) ;
407 OnPassiveQueue (Me ()) ;
408 doActivate (procId
, NIL)
409 END SuspendMeAndActivate
;
413 Switch - causes the calling process to enter the passive state; the
414 process identified by procId becomes the currently executing
415 process. info is used to pass parameter information from the
416 calling to the activated process. On return, info will
417 contain information from the process that chooses to switch
418 back to this one (or will be NIL if Activate or
419 SuspendMeAndActivate are used instead of Switch).
422 PROCEDURE Switch (procId
: ProcessId
; VAR info
: Parameter
) ;
426 OnPassiveQueue (Me ()) ;
427 doActivate (procId
, info
) ;
434 Wait - causes the calling process to enter the waiting state.
435 The procedure will return when the calling process is
436 activated by another process, or when one of its
437 associated eventSources has generated an event.
448 displayProcesses ("Wait start")
450 calling
:= currentId
;
451 OnWaitingQueue (calling
) ;
452 best
:= chooseProcess () ;
454 from
:= calling^.context
;
457 displayProcesses ("Wait about to perform IOTRANSFER")
459 IOTRANSFER (from
, currentId^.context
) ;
462 displayProcesses ("Wait after IOTRANSFER")
464 currentId^.context
:= from
;
465 currentId
:= calling
;
466 OnReadyQueue (calling
) ;
469 displayProcesses ("Wait end")
478 PROCEDURE displayQueue (name
: ARRAY OF CHAR; status
: Status
) ;
482 printf (name
) ; printf (" queue\n");
483 p
:= pQueue
[status
] ;
484 IF pQueue
[status
] = NIL
486 printf (" empty queue\n")
490 printf ("[pid %d, urg %d", p^.context^.context
, p^.urgency
) ;
493 printf (", currentId")
501 IF p # pQueue
[status
]
505 UNTIL p
= pQueue
[status
] ;
515 PROCEDURE displayProcesses (message
: ARRAY OF CHAR) ;
517 printf ("display processes: %s\n", ADR (message
)) ;
518 displayQueue ("ready", ready
) ;
519 displayQueue ("passive", passive
) ;
520 displayQueue ("waiting", waiting
)
521 END displayProcesses
;
524 (* The following procedures allow the association of processes
525 with sources of external events.
529 Attach - associates the specified eventSource with the calling
533 PROCEDURE Attach (eventSource
: Sources
) ;
540 Detach - dissociates the specified eventSource from the program.
543 PROCEDURE Detach (eventSource
: Sources
) ;
550 IsAttached - returns TRUE if and only if the specified eventSource is
551 currently associated with one of the processes of the
555 PROCEDURE IsAttached (eventSource
: Sources
) : BOOLEAN ;
557 RETURN Handler (eventSource
) #
NIL
562 Handler - returns the identity of the process, if any, that is
563 associated with the specified eventSource.
566 PROCEDURE Handler (eventSource
: Sources
) : ProcessId
;
572 c
:= HANDLER (eventSource
) ;
573 FOR s
:= MIN (Status
) TO MAX (Status
) DO
591 (* The following procedures allow processes to obtain their
592 identity, parameters, and urgency.
597 Me - returns the identity of the calling process (as assigned
598 when the process was first created).
601 PROCEDURE Me () : ProcessId
;
608 MyParam - returns the value specified as procParams when the
609 calling process was created.
612 PROCEDURE MyParam () : Parameter
;
614 RETURN currentId^.params
619 UrgencyOf - returns the urgency established when the process identified
620 by procId was first created.
623 PROCEDURE UrgencyOf (procId
: ProcessId
) : Urgency
;
625 RETURN currentId^.urgency
629 (* The following procedure provides facilities for exception
634 ProcessException - if the current coroutine is in the exceptional
635 execution state because of the raising of a language
636 exception, returns the corresponding enumeration value,
637 and otherwise raises an exception.
640 PROCEDURE ProcessesException () : ProcessesExceptions
;
642 IF IsProcessesException ()
644 RETURN VAL (ProcessesExceptions
, CurrentNumber (process
))
646 NoException (ADR (__FILE__
), __LINE__
,
647 __COLUMN__
, ADR(__FUNCTION__
),
648 ADR ("not in the exceptional execution state"))
650 END ProcessesException
;
654 IsProcessException - returns TRUE if the current coroutine is
655 in the exceptional execution state because
656 of the raising of an exception in
657 a routine from this module; otherwise returns
661 PROCEDURE IsProcessesException () : BOOLEAN ;
663 RETURN IsExceptionalExecution () AND IsCurrentSource (process
)
664 END IsProcessesException
;
668 setupCurrentId - sets up the initial process.
671 PROCEDURE setupCurrentId
;
678 context
:= CURRENT () ;
689 idleProcess - the idle process which listens for an interrupt.
692 PROCEDURE idleProcess
;
701 setupIdleId - sets up the idle process.
704 PROCEDURE setupIdle
;
706 Create (idleProcess
, 0, MIN (Urgency
), NIL, idleId
) ;
712 Init - sets up all the module data structures.
717 AllocateSource (process
) ;
719 pQueue
[ready
] := NIL ;
720 pQueue
[waiting
] := NIL ;
721 pQueue
[passive
] := NIL ;
722 pQueue
[dead
] := NIL ;