1 (* COROUTINES.mod implement the ISO COROUTINES specification.
3 Copyright (C) 2002-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 COROUTINES
;
29 FROM RTco
IMPORT init
, initThread
, transfer
, initSemaphore
,
30 wait
, signal
, currentThread
, turnInterrupts
,
31 currentInterruptLevel
;
33 FROM RTExceptions
IMPORT EHBlock
, InitExceptionBlock
,
34 SetExceptionBlock
, GetExceptionBlock
,
35 SetExceptionState
, IsInExceptionState
,
36 SetExceptionSource
, GetExceptionSource
;
38 FROM SYSTEM
IMPORT ADDRESS
, ADR
;
39 FROM EXCEPTIONS
IMPORT ExceptionSource
;
40 FROM RTint
IMPORT Listen
, AttachVector
, IncludeVector
, ExcludeVector
;
41 FROM Storage
IMPORT ALLOCATE
;
42 FROM Assertion
IMPORT Assert
;
43 FROM M2RTS
IMPORT Halt
;
44 FROM libc
IMPORT printf
;
45 FROM Processes
IMPORT displayProcesses
;
51 MinStack
= 16 * 1024 * 1024 ;
55 Status
= (suspended
, ready
, new
, running
) ;
57 COROUTINE
= POINTER TO RECORD
61 source
: ExceptionSource
;
62 wspace
: SYSTEM.ADDRESS
;
65 attached
: SourceList
;
69 SourceList
= POINTER TO RECORD
70 next
: SourceList
; (* next in the list of vectors which are *)
71 (* attached to this coroutine. *)
72 vec
: INTERRUPTSOURCE
; (* the interrupt vector (source) *)
73 curco
: COROUTINE
; (* the coroutine which is waiting on this vec *)
74 chain
: SourceList
; (* the next coroutine waiting on this vec *)
76 ptrToFrom
: POINTER TO COROUTINE
;
81 freeList
: SourceList
;
84 currentCoRoutine
: COROUTINE
;
85 illegalFinish
: ADDRESS
;
88 lock
: INTEGER ; (* semaphore protecting module data structures. *)
91 PROCEDURE NEWCOROUTINE (procBody
: PROC;
92 workspace
: SYSTEM.ADDRESS
;
95 [initProtection
: PROTECTION
]);
97 (* Creates a new coroutine whose body is given by procBody, and
98 returns the identity of the coroutine in cr. workspace is a
99 pointer to the work space allocated to the coroutine; size
100 specifies the size of this workspace in terms of SYSTEM.LOC.
102 The optarg, initProtection, may contain a single parameter
103 which specifies the initial protection level of the coroutine.
110 old
:= TurnInterrupts (MAX (PROTECTION
)) ;
111 IF initProtection
= UnassignedPriority
113 initProtection
:= PROT ()
115 tp
:= initThread (procBody
, size
, initProtection
) ;
118 Halt ('unable to create a new thread', __FILE__
, __FUNCTION__
, __LINE__
)
123 ehblock
:= InitExceptionBlock () ;
126 wspace
:= workspace
;
133 old
:= TurnInterrupts (old
)
137 PROCEDURE TRANSFER (VAR from
: COROUTINE
; to
: COROUTINE
);
138 (* Returns the identity of the calling coroutine in from, and
139 transfers control to the coroutine specified by to.
145 old
:= TurnInterrupts (MAX (PROTECTION
)) ;
147 Transfer (from
, to
) ;
148 (* signal (lock) ; *)
149 old
:= TurnInterrupts (old
)
157 PROCEDURE Transfer (VAR from
: COROUTINE
; to
: COROUTINE
) ;
161 printf ("TRANSFER\n");
162 printf ("current coroutine is: %d\n", currentCoRoutine^.context
);
165 printf ("previous coroutine is: %d\n", previous^.context
)
167 printf ("wishes to context switch to: %d\n", to^.context
);
169 previous
:= currentCoRoutine
;
170 from
:= currentCoRoutine
;
171 IF to^.context
= from^.context
173 Halt ('error when attempting to context switch to the same process',
174 __FILE__
, __FUNCTION__
, __LINE__
)
176 from^.inexcept
:= SetExceptionState (to^.inexcept
) ;
177 from^.source
:= GetExceptionSource () ;
178 currentCoRoutine
:= to
;
179 SetExceptionBlock (currentCoRoutine^.ehblock
) ;
180 SetExceptionSource (currentCoRoutine^.source
) ;
181 transfer (from^.context
, to^.context
)
186 localMain - creates the holder for the main process.
189 PROCEDURE localMain
;
196 lock
:= initSemaphore (1) ;
198 NEW (currentCoRoutine
) ;
199 WITH currentCoRoutine^
DO
200 context
:= currentThread () ;
201 ehblock
:= GetExceptionBlock () ;
202 inexcept
:= IsInExceptionState () ;
203 source
:= GetExceptionSource () ;
210 head
:= currentCoRoutine
;
211 old
:= turnInterrupts (MAX (PROTECTION
)) ; (* was UnassignedPriority *)
218 localInit - checks to see whether we need to initialize our interface to pthreads.
221 PROCEDURE localInit
;
228 Halt ('failed to initialize RTco',
229 __FILE__
, __FUNCTION__
, __LINE__
)
238 PROCEDURE IOTRANSFER (VAR from
: COROUTINE
; to
: COROUTINE
);
239 (* Returns the identity of the calling coroutine in from and
240 transfers control to the coroutine specified by to. On
241 occurrence of an interrupt, associated with the caller, control
242 is transferred back to the caller, and the identity of the
243 interrupted coroutine is returned in from. The calling coroutine
244 must be associated with a source of interrupts.
252 old
:= TurnInterrupts (MAX (PROTECTION
)) ;
255 Halt ("error IOTRANSFER cannot transfer control to the running COROUTINE",
256 __FILE__
, __FUNCTION__
, __LINE__
)
259 l
:= currentCoRoutine^.attached
;
262 printf ("no source of interrupts associated with coroutine\n")
266 ptrToFrom
:= ADR (from
) ;
267 ptrToTo
:= ADR (to
) ;
268 curco
:= currentCoRoutine
;
269 Assert (currentCoRoutine #
NIL) ;
270 prev
:= AttachVector (vec
, l
) ;
272 IF (prev #
NIL) AND (prev # l
)
274 printf ("not expecting multiple COROUTINES to be waiting on a single interrupt source\n")
281 Transfer (from
, to
) ;
283 old
:= TurnInterrupts (old
)
288 New - assigns, l, to a new SourceList.
291 PROCEDURE New (VAR l
: SourceList
) ;
298 freeList
:= freeList^.next
304 Dispose - returns, l, to the freeList.
307 PROCEDURE Dispose (l
: SourceList
) ;
309 l^.next
:= freeList
;
314 PROCEDURE ATTACH (source
: INTERRUPTSOURCE
);
315 (* Associates the specified source of interrupts with the calling
322 l
:= currentCoRoutine^.attached
;
326 l^.curco
:= currentCoRoutine
;
335 next
:= currentCoRoutine^.attached
;
337 curco
:= currentCoRoutine
;
340 currentCoRoutine^.attached
:= l
;
341 IF AttachVector (source
, l
) #
NIL
343 printf ("ATTACH implementation restriction only one coroutine may be attached to a specific interrupt source\n")
349 PROCEDURE DETACH (source
: INTERRUPTSOURCE
);
350 (* Dissociates the specified source of interrupts from the calling
353 l
, prev
: SourceList
;
357 l
:= currentCoRoutine^.attached
;
364 Assert (l
= currentCoRoutine^.attached
) ;
365 currentCoRoutine^.attached
:= currentCoRoutine^.attached^.next
;
367 prev^.next
:= l^.next
382 getAttached - returns the first COROUTINE associated with, source.
383 It returns NIL is no COROUTINE is associated with, source.
386 PROCEDURE getAttached (source
: INTERRUPTSOURCE
) : COROUTINE
;
409 PROCEDURE IsATTACHED (source
: INTERRUPTSOURCE
): BOOLEAN;
410 (* Returns TRUE if and only if the specified source of interrupts is
411 currently associated with a coroutine; otherwise returns FALSE.
418 result
:= getAttached (source
) #
NIL ;
424 PROCEDURE HANDLER (source
: INTERRUPTSOURCE
) : COROUTINE
;
425 (* Returns the coroutine, if any, that is associated with the source
426 of interrupts. The result is undefined if IsATTACHED(source) =
434 co
:= getAttached (source
) ;
440 PROCEDURE CURRENT () : COROUTINE
;
441 (* Returns the identity of the calling coroutine. *)
444 RETURN currentCoRoutine
448 PROCEDURE LISTEN (p
: PROTECTION
) ;
449 (* Momentarily changes the protection of the calling coroutine to p. *)
452 Listen (FALSE, IOTransferHandler
, p
)
457 ListenLoop - should be called instead of users writing:
463 It performs the same function but yields
464 control back to the underlying operating system.
465 It also checks for deadlock.
466 This function returns when an interrupt occurs.
467 (File descriptor becomes ready or time event expires).
470 PROCEDURE ListenLoop
;
473 Listen (TRUE, IOTransferHandler
, MIN (PROTECTION
))
478 removeAttached - removes all sources of interrupt from COROUTINE, c.
481 PROCEDURE removeAttached (c
: COROUTINE
) ;
488 ExcludeVector (l^.vec
) ;
495 IOTransferHandler - handles interrupts related to a pending IOTRANSFER.
498 PROCEDURE IOTransferHandler (InterruptNo
: CARDINAL;
502 ourself
: SourceList
;
506 printf ("IOTransferHandler called\n") ;
507 displayProcesses ("IOTransferHandler") ;
508 printf ("IOTransferHandler vec %d coroutine: %d\n", l^.vec
, l^.curco^.context
);
509 printf ("localInit\n");
514 Halt ('no coroutine attached to this interrupt vector which was initiated by IOTRANSFER',
515 __FILE__
, __FUNCTION__
, __LINE__
)
519 printf ("IOTransferHandler called\n");
520 printf ("before wait (lock)\n");
525 printf ("IOTransferHandler vec %d coroutine 0x%x\n", l^.vec
, l^.curco
);
526 printf ("current coroutine is: %d\n", currentCoRoutine^.context
);
529 printf ("previous coroutine is: %d\n", previous^.context
)
531 printf ("handler wants to context switch to: %d\n", l^.curco^.context
);
532 displayProcesses ("IOTransferHandler")
536 ourself := AttachVector (InterruptNo, chain) ;
539 Halt ('inconsistancy of return result',
540 __FILE__, __FUNCTION__, __LINE__)
544 removeAttached (curco)
546 printf ('odd vector has been chained\n')
549 removeAttached (curco
) ; (* remove all sources of interrupt for l^.curco. *)
550 ptrToFrom^
:= currentCoRoutine
;
551 previous
:= currentCoRoutine
;
552 previous^.inexcept
:= SetExceptionState (curco^.inexcept
) ;
553 previous^.source
:= GetExceptionSource () ;
554 currentCoRoutine
:= curco
;
555 SetExceptionBlock (currentCoRoutine^.ehblock
) ;
556 SetExceptionSource (currentCoRoutine^.source
) ;
558 transfer (previous^.context
, currentCoRoutine^.context
)
561 END IOTransferHandler
;
564 PROCEDURE PROT () : PROTECTION
;
565 (* Returns the protection of the calling coroutine. *)
568 RETURN currentInterruptLevel ()
573 TurnInterrupts - switches processor interrupts to the protection
574 level, to. It returns the old value.
577 PROCEDURE TurnInterrupts (to
: PROTECTION
) : PROTECTION
;
582 old
:= turnInterrupts (to
) ;
583 Listen (FALSE, IOTransferHandler
, to
) ;
589 Init - initialize the global data structures.
596 currentCoRoutine
:= NIL