xfail scan-tree-dump-not throw in g++.dg/pr99966.C on hppa*64*-*-*
[official-gcc.git] / gcc / m2 / gm2-libs-iso / Processes.mod
bloba4cd1d3e8b0d598eb59981592aaa3d2fb32f925a
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)
11 any later version.
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 ;
43 CONST
44 defaultSpace = 1024 * 1024 * 8 ;
45 debugging = FALSE ;
48 (* The following procedures create processes and switch control between
49 them. *)
51 TYPE
52 ProcessId = POINTER TO RECORD
53 body : Body ;
54 workSpace : CARDINAL ;
55 stack : ADDRESS ;
56 urgency : Urgency ;
57 context : COROUTINE ;
58 params : Parameter ;
59 state : Status ;
60 right, left: ProcessId ;
61 END ;
63 Status = (ready, waiting, passive, dead) ;
65 VAR
66 process : ExceptionSource ;
67 pQueue : ARRAY Status OF ProcessId ;
68 free,
69 idleId,
70 currentId: ProcessId ;
74 New - assigns, p, to a new ProcessId.
77 PROCEDURE New (VAR p: ProcessId) ;
78 BEGIN
79 IF free=NIL
80 THEN
81 NEW (p)
82 ELSE
83 p := free ;
84 free := free^.right
85 END
86 END New ;
90 Dispose - returns, p, to the free list.
93 PROCEDURE Dispose (VAR p: ProcessId) ;
94 BEGIN
95 p^.right := free ;
96 free := p
97 END Dispose ;
101 add - adds process, p, to queue, head.
104 PROCEDURE add (VAR head: ProcessId; p: ProcessId) ;
105 BEGIN
106 IF head=NIL
107 THEN
108 head := p ;
109 p^.left := p ;
110 p^.right := p
111 ELSE
112 p^.right := head ;
113 p^.left := head^.left ;
114 head^.left^.right := p ;
115 head^.left := p
117 END add ;
121 sub - subtracts process, p, from queue, head.
124 PROCEDURE sub (VAR head: ProcessId; p: ProcessId) ;
125 BEGIN
126 IF (p^.left=head) AND (p=head)
127 THEN
128 head := NIL
129 ELSE
130 IF head=p
131 THEN
132 head := head^.right
133 END ;
134 p^.left^.right := p^.right ;
135 p^.right^.left := p^.left
137 END sub ;
141 Add - adds, p, to the appropriate queue.
144 PROCEDURE Add (p: ProcessId) ;
145 BEGIN
146 add (pQueue[p^.state], p)
147 END Add ;
151 Remove - remove, p, from the appropriate queue.
154 PROCEDURE Remove (p: ProcessId) ;
155 BEGIN
156 sub (pQueue[p^.state], p)
157 END Remove ;
161 OnDeadQueue - removes process, p, from the queue and adds it
162 to the dead queue.
165 PROCEDURE OnDeadQueue (p: ProcessId) ;
166 BEGIN
167 Remove (p) ;
168 p^.state := dead ;
169 Add (p)
170 END OnDeadQueue ;
174 OnReadyQueue - removes process, p, from the queue and adds it
175 to the ready queue.
178 PROCEDURE OnReadyQueue (p: ProcessId) ;
179 BEGIN
180 Remove (p) ;
181 p^.state := ready ;
182 Add (p)
183 END OnReadyQueue ;
187 OnPassiveQueue - removes process, p, from the queue and adds it
188 to the passive queue.
191 PROCEDURE OnPassiveQueue (p: ProcessId) ;
192 BEGIN
193 Remove (p) ;
194 p^.state := passive ;
195 Add (p)
196 END OnPassiveQueue ;
200 OnWaitingQueue - removes process, p, from the queue and adds it
201 to the waiting queue.
204 PROCEDURE OnWaitingQueue (p: ProcessId) ;
205 BEGIN
206 Remove (p) ;
207 p^.state := waiting ;
208 Add (p)
209 END OnWaitingQueue ;
213 checkDead - check to see if any processes are on the dead queue
214 and if they are not the current process deallocate
215 resources.
218 PROCEDURE checkDead ;
220 p: ProcessId ;
221 BEGIN
222 p := pQueue[dead] ;
223 WHILE (p#NIL) AND (p#currentId) DO
224 Remove (p) ;
225 WITH p^ DO
226 IF stack#NIL
227 THEN
228 DEALLOCATE (stack, workSpace)
230 END ;
231 Dispose (p) ;
232 p := pQueue[dead]
234 END checkDead ;
238 RotateReady - rotate the ready queue, as an attempt to introduce some scheduling fairness.
241 PROCEDURE RotateReady ;
242 BEGIN
243 IF pQueue[ready] # NIL
244 THEN
245 pQueue[ready] := pQueue[ready]^.right
247 END RotateReady ;
251 chooseProcess -
254 PROCEDURE chooseProcess () : ProcessId ;
257 best,
258 head: ProcessId ;
259 BEGIN
260 head := pQueue[ready] ;
261 best := NIL ;
262 p := head ;
263 REPEAT
264 IF (best = NIL) OR (p^.urgency >= best^.urgency)
265 THEN
266 best := p
267 END ;
268 p := p^.right
269 UNTIL p=head ;
270 Assert (best # NIL) ;
271 Assert (best^.state = ready) ;
272 RETURN best
273 END chooseProcess ;
277 Reschedule - rotates the ready queue and transfers to the process with the highest
278 run priority.
281 PROCEDURE Reschedule ;
284 best: ProcessId ;
285 BEGIN
286 checkDead ;
287 RotateReady ;
288 best := chooseProcess () ;
289 IF best#currentId
290 THEN
291 IF debugging
292 THEN
293 displayProcesses ("Reschedule")
294 END ;
295 (* the best process to run is different to the current process, so switch. *)
296 p := currentId ;
297 currentId := best ;
298 TRANSFER (p^.context, currentId^.context)
300 END Reschedule ;
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
311 until activated.
314 PROCEDURE Create (procBody: Body; extraSpace: CARDINAL; procUrg: Urgency;
315 procParams: Parameter; VAR procId: ProcessId) ;
316 BEGIN
317 New (procId) ;
318 WITH procId^ DO
319 body := procBody ;
320 workSpace := extraSpace + defaultSpace ;
321 urgency := procUrg ;
322 ALLOCATE (stack, workSpace) ;
323 NEWCOROUTINE (procBody, stack, workSpace, context) ;
324 params := procParams ;
325 state := passive ;
326 right := NIL ;
327 left := NIL
328 END ;
329 Add (procId)
330 END Create ;
334 Creates a new process, with parameters as for Create.
335 The process is created in the ready state; it is eligible to
336 run immediately.
339 PROCEDURE Start (procBody: Body; extraSpace: CARDINAL; procUrg: Urgency;
340 procParams: Parameter; VAR procId: ProcessId) ;
341 BEGIN
342 Create (procBody, extraSpace, procUrg, procParams, procId) ;
343 Activate (procId)
344 END Start ;
348 StopMe - terminates the calling process.
349 The process must not be associated with a source
350 of events.
353 PROCEDURE StopMe ;
354 BEGIN
355 OnDeadQueue (Me ()) ;
356 Reschedule
357 END StopMe ;
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 ;
367 BEGIN
368 IF debugging
369 THEN
370 displayProcesses ("SuspendMe")
371 END ;
372 OnPassiveQueue (Me ()) ;
373 Reschedule
374 END SuspendMe ;
378 doActivate - activate procId and pass, info, in the parameter field.
381 PROCEDURE doActivate (procId: ProcessId; info: Parameter) ;
382 BEGIN
383 procId^.params := info ;
384 OnReadyQueue (procId) ;
385 Reschedule
386 END doActivate ;
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) ;
395 BEGIN
396 doActivate (procId, NIL)
397 END Activate ;
401 SuspendMeAndActivate - executes an atomic sequence of SuspendMe() and
402 Activate(procId).
405 PROCEDURE SuspendMeAndActivate (procId: ProcessId) ;
406 BEGIN
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) ;
424 p: ProcessId ;
425 BEGIN
426 OnPassiveQueue (Me ()) ;
427 doActivate (procId, info) ;
428 p := Me () ;
429 info := p^.params
430 END Switch ;
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.
440 PROCEDURE Wait ;
442 calling,
443 best : ProcessId ;
444 from : COROUTINE ;
445 BEGIN
446 IF debugging
447 THEN
448 displayProcesses ("Wait start")
449 END ;
450 calling := currentId ;
451 OnWaitingQueue (calling) ;
452 best := chooseProcess () ;
453 currentId := best ;
454 from := calling^.context ;
455 IF debugging
456 THEN
457 displayProcesses ("Wait about to perform IOTRANSFER")
458 END ;
459 IOTRANSFER (from, currentId^.context) ;
460 IF debugging
461 THEN
462 displayProcesses ("Wait after IOTRANSFER")
463 END ;
464 currentId^.context := from ;
465 currentId := calling ;
466 OnReadyQueue (calling) ;
467 IF debugging
468 THEN
469 displayProcesses ("Wait end")
471 END Wait ;
475 displayQueue -
478 PROCEDURE displayQueue (name: ARRAY OF CHAR; status: Status) ;
480 p: ProcessId ;
481 BEGIN
482 printf (name) ; printf (" queue\n");
483 p := pQueue[status] ;
484 IF pQueue[status] = NIL
485 THEN
486 printf (" empty queue\n")
487 ELSE
488 printf (" ");
489 REPEAT
490 printf ("[pid %d, urg %d", p^.context^.context, p^.urgency) ;
491 IF p = currentId
492 THEN
493 printf (", currentId")
494 END ;
495 IF p = idleId
496 THEN
497 printf (", idleId")
498 END ;
499 printf ("]") ;
500 p := p^.right ;
501 IF p # pQueue[status]
502 THEN
503 printf (", ")
505 UNTIL p = pQueue[status] ;
506 printf ("\n")
508 END displayQueue ;
512 displayProcesses -
515 PROCEDURE displayProcesses (message: ARRAY OF CHAR) ;
516 BEGIN
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
530 process.
533 PROCEDURE Attach (eventSource: Sources) ;
534 BEGIN
535 ATTACH (eventSource)
536 END Attach ;
540 Detach - dissociates the specified eventSource from the program.
543 PROCEDURE Detach (eventSource: Sources) ;
544 BEGIN
545 DETACH (eventSource)
546 END Detach ;
550 IsAttached - returns TRUE if and only if the specified eventSource is
551 currently associated with one of the processes of the
552 program.
555 PROCEDURE IsAttached (eventSource: Sources) : BOOLEAN ;
556 BEGIN
557 RETURN Handler (eventSource) # NIL
558 END IsAttached ;
562 Handler - returns the identity of the process, if any, that is
563 associated with the specified eventSource.
566 PROCEDURE Handler (eventSource: Sources) : ProcessId ;
568 c: COROUTINE ;
569 p: ProcessId ;
570 s: Status ;
571 BEGIN
572 c := HANDLER (eventSource) ;
573 FOR s := MIN (Status) TO MAX (Status) DO
574 p := pQueue[s] ;
575 IF p#NIL
576 THEN
577 REPEAT
578 IF p^.context=c
579 THEN
580 RETURN p
581 ELSE
582 p := p^.right
584 UNTIL p=pQueue[s]
586 END ;
587 RETURN NIL
588 END Handler ;
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 ;
602 BEGIN
603 RETURN currentId
604 END Me ;
608 MyParam - returns the value specified as procParams when the
609 calling process was created.
612 PROCEDURE MyParam () : Parameter ;
613 BEGIN
614 RETURN currentId^.params
615 END MyParam ;
619 UrgencyOf - returns the urgency established when the process identified
620 by procId was first created.
623 PROCEDURE UrgencyOf (procId: ProcessId) : Urgency ;
624 BEGIN
625 RETURN currentId^.urgency
626 END UrgencyOf ;
629 (* The following procedure provides facilities for exception
630 handlers. *)
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 ;
641 BEGIN
642 IF IsProcessesException ()
643 THEN
644 RETURN VAL (ProcessesExceptions, CurrentNumber (process))
645 ELSE
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
658 FALSE.
661 PROCEDURE IsProcessesException () : BOOLEAN ;
662 BEGIN
663 RETURN IsExceptionalExecution () AND IsCurrentSource (process)
664 END IsProcessesException ;
668 setupCurrentId - sets up the initial process.
671 PROCEDURE setupCurrentId ;
672 BEGIN
673 NEW (currentId) ;
674 WITH currentId^ DO
675 workSpace := 0 ;
676 stack := NIL ;
677 urgency := 0 ;
678 context := CURRENT () ;
679 params := NIL ;
680 state := ready ;
681 right := NIL ;
682 left := NIL
683 END ;
684 Add (currentId)
685 END setupCurrentId ;
689 idleProcess - the idle process which listens for an interrupt.
692 PROCEDURE idleProcess ;
693 BEGIN
694 LOOP
695 ListenLoop
697 END idleProcess ;
701 setupIdleId - sets up the idle process.
704 PROCEDURE setupIdle ;
705 BEGIN
706 Create (idleProcess, 0, MIN (Urgency), NIL, idleId) ;
707 Activate (idleId)
708 END setupIdle ;
712 Init - sets up all the module data structures.
715 PROCEDURE Init ;
716 BEGIN
717 AllocateSource (process) ;
718 free := NIL ;
719 pQueue[ready] := NIL ;
720 pQueue[waiting] := NIL ;
721 pQueue[passive] := NIL ;
722 pQueue[dead] := NIL ;
723 setupCurrentId ;
724 setupIdle
725 END Init ;
728 BEGIN
729 Init
730 END Processes.