libcpp, c, middle-end: Optimize initializers using #embed in C
[official-gcc.git] / gcc / m2 / gm2-libs-coroutines / TimerHandler.mod
blob88eb04e819a20d2664871f1c7260ea12857efc13
1 (* TimerHandler.mod provides a simple timer handler for the Executive.
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)
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 TimerHandler[MAX(PROTECTION)] ;
30 FROM COROUTINES IMPORT PROTECTION ;
31 FROM SysStorage IMPORT ALLOCATE ;
32 FROM NumberIO IMPORT CardToStr ;
33 FROM Debug IMPORT Halt, DebugString ;
34 FROM KeyBoardLEDs IMPORT SwitchScroll ;
35 FROM RTint IMPORT ReArmTimeVector, GetTimeVector, InitTimeVector ;
36 FROM Executive IMPORT DESCRIPTOR, Suspend, Resume, GetCurrentProcess,
37 WaitForIO, InitProcess, RotateRunQueue,
38 ProcessName, Ps ;
40 CONST
41 MaxQuantum = 4 ; (* Maximum ticks a process may consume *)
42 (* before being rescheduled. *)
43 BaseTicks = 1000000 ; (* Max resolution of clock ticks per sec *)
44 TimerStackSize = 100000H ; (* Reasonable sized stack for a process *)
45 Debugging = FALSE ; (* Do you want lots of debugging info? *)
46 EnableLED = FALSE ; (* Should the scroll LED be pulsed? *)
48 TYPE
49 EVENT = POINTER TO RECORD
50 EventQ : Queue ;
51 WhichQ : QueueType ;
52 Process : DESCRIPTOR ;
53 NoOfTicks : CARDINAL ;
54 WasCancelled: BOOLEAN ;
55 END ;
57 (* the queue types are either:
59 active queue which has a list of outstanding events
60 dead queue which is essentially the free list
61 solo which is no queue and the event is in limbo
64 QueueType = (active, dead, solo) ;
66 Queue = RECORD
67 Right,
68 Left : EVENT ;
69 END ;
71 VAR
72 TotalTicks : CARDINAL ; (* System up time tick count *)
73 CurrentQuanta : CARDINAL ; (* Currentprocess time quanta allowance *)
74 ActiveQueue, (* Queue of outstanding timer requests *)
75 DeadQueue : EVENT ; (* Free list of events. *)
79 GetTicks - returns the number of ticks since boottime.
82 PROCEDURE GetTicks () : CARDINAL ;
83 VAR
84 ToOldState : PROTECTION ;
85 CopyOfTicks: CARDINAL ;
86 BEGIN
87 (* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
88 CopyOfTicks := TotalTicks ;
89 (* ToOldState := TurnInterrupts(ToOldState) ; (* restore interrupts *) *)
90 RETURN( CopyOfTicks )
91 END GetTicks ;
95 Sleep - suspends the current process for a time, t.
96 The time is measured in ticks.
99 PROCEDURE Sleep (t: CARDINAL) ;
101 ToOldState: PROTECTION ;
102 e : EVENT ;
103 BEGIN
104 (* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
105 e := ArmEvent (t) ;
106 IF WaitOn (e)
107 THEN
108 END ;
109 (* ToOldState := TurnInterrupts(ToOldState) (* restore interrupts *) *)
110 END Sleep ;
114 More lower system calls to the timer procedures follow,
115 they are necessary to allow handling multiple events.
120 ArmEvent - initializes an event, e, to occur at time, t.
121 The time, t, is measured in ticks.
122 The event is NOT placed onto the event queue.
125 PROCEDURE ArmEvent (t: CARDINAL) : EVENT ;
127 e : EVENT ;
128 ToOldState: PROTECTION ;
129 Ticks : CARDINAL ;
130 BEGIN
131 (* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
132 e := CreateSolo() ;
134 (* your code needs to go here *)
135 WITH e^ DO (* remove for student *)
136 InitQueue(EventQ) ; (* not on a queue yet *) (* remove for student *)
137 WhichQ := solo ; (* and set the queue state accordingly *) (* remove for student *)
138 Process := NIL ; (* no process waiting event yet *) (* remove for student *)
139 NoOfTicks := t ; (* absolute number of ticks *) (* remove for student *)
140 WasCancelled := FALSE ; (* has not been cancelled *) (* remove for student *)
141 END ; (* remove for student *)
143 (* ToOldState := TurnInterrupts(ToOldState) ; (* restore interrupts *) *)
144 RETURN( e )
145 END ArmEvent ;
149 WaitOn - places event, e, onto the event queue and then the calling
150 process suspends. It is resumed up by either the event
151 expiring or the event, e, being cancelled.
152 TRUE is returned if the event was cancelled
153 FALSE is returned if the event expires.
154 The event, e, is always assigned to NIL when the function
155 finishes.
158 PROCEDURE WaitOn (VAR e: EVENT) : BOOLEAN ;
160 ToOldState: PROTECTION ;
161 Cancelled : BOOLEAN ;
162 BEGIN
163 (* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
164 IF e=NIL
165 THEN
166 Halt ('event should never be NIL',
167 __FILE__, __FUNCTION__, __LINE__)
168 ELSE
169 WITH e^ DO
170 (* we will just check to see whether someone has cancelled this *)
171 (* event before it ever got to the queue... *)
172 IF NOT WasCancelled
173 THEN
174 (* right so it wasn't cancelled. Lets place it on the queue and *)
175 (* go to sleep. *)
176 Process := GetCurrentProcess() ; (* so we know who is waiting *)
177 OnActiveQueue(e) ; (* add to the queue and then *)
179 IF Debugging
180 THEN
181 DisplayActive ; (* debugging *)
182 END ;
184 Suspend (* wait for Resume (we sleep) *)
185 END ;
186 (* At this point we have either been cancelled or not. We must *)
187 (* check the event again as we might have been sleeping (Suspend) *)
188 Cancelled := WasCancelled
190 END ;
191 OnDeadQueue(e) ; (* now it is safe to throw this event away *)
192 e := NIL ;
193 (* ToOldState := TurnInterrupts(ToOldState) ; (* restore interrupts *) *)
194 RETURN Cancelled
195 END WaitOn ;
199 Cancel - cancels the event, e, on the event queue and makes
200 the appropriate process runnable again.
201 TRUE is returned if the event was cancelled and
202 FALSE is returned is the event was not found or
203 no process was waiting on this event.
206 PROCEDURE Cancel (e: EVENT) : BOOLEAN ;
208 ToOldState: PROTECTION ;
209 Cancelled : BOOLEAN ;
210 Private : DESCRIPTOR ;
211 BEGIN
212 (* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
213 IF IsOnActiveQueue(e)
214 THEN
215 WITH e^ DO
216 Cancelled := NOT WasCancelled ;
217 IF WasCancelled
218 THEN
219 Halt ('inconsistancy event has been cancelled and it is on queue',
220 __FILE__, __FUNCTION__, __LINE__)
221 END ;
222 OnSoloQueue(e) ;
223 WasCancelled := TRUE ;
224 IF Process#NIL (* double check that it has not *)
225 (* already been cancelled *)
226 THEN
227 Private := Process ; (* we use our own Private variable *)
228 Process := NIL ; (* as we need to set Process to NIL *)
229 Process := Resume(Private) (* before we Resume. Otherwise *)
230 (* there is the possibility that it *)
231 (* might be reused before we := NIL *)
232 (* (because when we touch Resume *)
233 (* another process could run and..) *)
236 ELSE
237 Cancelled := FALSE
238 END ;
239 (* ToOldState := TurnInterrupts(ToOldState) ; (* restore interrupts *) *)
240 RETURN( Cancelled )
241 END Cancel ;
245 ReArmEvent - removes an event, e, from the event queue. A new time
246 is given to this event and it is then re-inserted onto the
247 event queue in the correct place.
248 TRUE is returned if this occurred
249 FALSE is returned if the event was not found.
252 PROCEDURE ReArmEvent (e: EVENT; t: CARDINAL) : BOOLEAN ;
254 ToOldState: PROTECTION ;
255 ReArmed : BOOLEAN ;
256 BEGIN
257 (* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; (* disable interrupts *) *)
258 WITH e^ DO
259 IF WasCancelled
260 THEN
261 ReArmed := FALSE
262 ELSIF IsOnActiveQueue(e) OR IsOnSoloQueue(e)
263 THEN
264 ReArmed := TRUE ;
265 OnSoloQueue(e) ; (* remove from queue *)
266 NoOfTicks := t ; (* give it a new time *)
267 OnActiveQueue(e) (* back on queue *)
268 ELSE
269 Halt ('ReArm should not be asked to ReArm a dead event',
270 __FILE__, __FUNCTION__, __LINE__)
272 END ;
273 (* ToOldState := TurnInterrupts(ToOldState) ; (* restore interrupts *) *)
274 RETURN( ReArmed )
275 END ReArmEvent ;
279 StartClock - ticks is milli seconds.
282 PROCEDURE StartClock (vec: CARDINAL; ticks: CARDINAL) ;
283 BEGIN
284 ReArmTimeVector (vec, ticks MOD BaseTicks, ticks DIV BaseTicks)
285 END StartClock ;
289 LoadClock - returns the number of milli seconds.
292 PROCEDURE LoadClock (vec: CARDINAL) : CARDINAL ;
294 micro, secs: CARDINAL ;
295 BEGIN
296 GetTimeVector (vec, micro, secs) ;
297 RETURN secs * BaseTicks + micro
298 END LoadClock ;
302 Timer - is a process which serves the clock interrupt.
303 Its function is fourfold:
305 (i) to maintain the timer event queue
306 (ii) to give some fairness to processes via round robin scheduling
307 (iii) to keep a count of the total ticks so far (time of day)
308 (iv) provide a heartbeat sign of life via the scroll lock LED
311 PROCEDURE Timer ;
313 CurrentCount: CARDINAL ;
314 ToOldState : PROTECTION ;
315 ScrollLED : BOOLEAN ;
316 TimerIntNo : CARDINAL ;
317 r : INTEGER ;
318 BEGIN
319 (* ToOldState := TurnInterrupts(MAX(PROTECTION)) ; *)
320 ScrollLED := FALSE ;
321 TimerIntNo := InitTimeVector ((BaseTicks DIV TicksPerSecond) MOD BaseTicks,
322 (BaseTicks DIV TicksPerSecond) DIV BaseTicks,
323 MAX (PROTECTION)) ;
324 LOOP
325 WaitForIO (TimerIntNo) ;
327 (* Get current clock count *)
328 CurrentCount := (* LoadClock(TimerIntNo) ; *) 0 ;
329 (* Now compenstate for lost ticks *)
330 StartClock (TimerIntNo, CurrentCount + (BaseTicks DIV TicksPerSecond)) ;
332 INC (TotalTicks) ; (* (iii) *)
333 IF EnableLED
334 THEN
335 (* now pulse scroll LED *)
336 IF (TotalTicks MOD TicksPerSecond) = 0
337 THEN
338 ScrollLED := NOT ScrollLED ;
339 (* r := printf("<scroll %d>", TotalTicks); *)
340 SwitchScroll(ScrollLED) (* (iv) *)
342 END ;
343 IF (TotalTicks MOD MaxQuantum) = 0
344 THEN
345 RotateRunQueue (* (ii) *)
346 END ;
348 CheckActiveQueue (* (i) *)
350 END Timer ;
354 CheckActiveQueue - purpose is:
356 (i) to remove all events which have expired
357 (ii) resume all processes waiting on these events
358 (iii) decrement the first event with a non zero NoOfTicks
361 PROCEDURE CheckActiveQueue ;
363 e : EVENT ;
364 Private: DESCRIPTOR ;
365 BEGIN
366 IF Debugging
367 THEN
368 DebugString('inside CheckActiveQueue\n') ;
369 DisplayActive
370 END ;
371 WHILE (ActiveQueue#NIL) AND (ActiveQueue^.NoOfTicks=0) DO (* (i) *)
372 e := ActiveQueue ;
373 OnSoloQueue(e) ;
374 (* note we do not put it onto the dead queue. The process
375 waiting for the event will place, e, onto the dead queue *)
376 WITH e^ DO
377 IF (NOT WasCancelled) AND (Process#NIL)
378 THEN
379 Private := Process ; (* we use our own Private variable *)
380 Process := NIL ; (* as we might context switch in *)
381 Process := Resume(Private) ; (* resume. (ii) *)
382 IF Debugging
383 THEN
388 END ;
389 IF ActiveQueue#NIL
390 THEN
391 DEC(ActiveQueue^.NoOfTicks) (* (iii) *)
392 END ;
393 IF Debugging
394 THEN
395 DebugString('after CheckActiveQueue\n') ;
396 DisplayActive
397 END ;
398 END CheckActiveQueue ;
402 CreateSolo - create a new event. It does this by either getting an event from
403 the dead queue or (if the dead queue is empty) an event is created
404 by using NEW.
407 PROCEDURE CreateSolo () : EVENT ;
409 e: EVENT ;
410 BEGIN
411 IF DeadQueue=NIL
412 THEN
413 NEW(e)
414 ELSE
415 e := DeadQueue ;
416 SubFrom(DeadQueue, e)
417 END ;
418 e^.WhichQ := solo ;
419 RETURN( e )
420 END CreateSolo ;
424 RemoveFromDead - removes event, e, from the dead queue.
427 PROCEDURE RemoveFromDead (e: EVENT) ;
428 BEGIN
429 SubFrom(DeadQueue, e)
430 END RemoveFromDead ;
434 OnDeadQueue - places an event onto the dead queue.
437 PROCEDURE OnDeadQueue (e: EVENT) ;
438 BEGIN
439 IF e#NIL
440 THEN
441 OnSoloQueue(e) ; (* put on solo queue first *)
442 AddTo(DeadQueue, e) ; (* now safe to put on dead queue *)
443 e^.WhichQ := dead
445 END OnDeadQueue ;
449 OnSoloQueue - places an event onto the solo queue.
452 PROCEDURE OnSoloQueue (e: EVENT) ;
453 BEGIN
454 IF e#NIL
455 THEN
456 IF IsOnActiveQueue(e)
457 THEN
458 RemoveFromActive(e)
459 ELSIF IsOnDeadQueue(e)
460 THEN
461 RemoveFromDead(e)
462 END ;
463 e^.WhichQ := solo
465 END OnSoloQueue ;
469 OnActiveQueue - places an event onto the active queue.
472 PROCEDURE OnActiveQueue (e: EVENT) ;
473 BEGIN
474 IF e#NIL
475 THEN
476 IF IsOnDeadQueue(e)
477 THEN
478 Halt ('illegal state change',
479 __FILE__, __FUNCTION__, __LINE__)
480 ELSIF IsOnSoloQueue(e)
481 THEN
482 RelativeAddToActive(e) ;
483 e^.WhichQ := active
486 END OnActiveQueue ;
490 IsOnSoloQueue - returns TRUE if event, e, is on the solo queue.
493 PROCEDURE IsOnSoloQueue (e: EVENT) : BOOLEAN ;
494 BEGIN
495 RETURN( (e#NIL) AND (e^.WhichQ=solo) )
496 END IsOnSoloQueue ;
500 IsOnDeadQueue - returns TRUE if event, e, is on the dead queue.
503 PROCEDURE IsOnDeadQueue (e: EVENT) : BOOLEAN ;
504 BEGIN
505 RETURN( (e#NIL) AND (e^.WhichQ=dead) )
506 END IsOnDeadQueue ;
510 IsOnActiveQueue - returns TRUE if event, e, is on the active queue.
513 PROCEDURE IsOnActiveQueue (e: EVENT) : BOOLEAN ;
514 BEGIN
515 RETURN( (e#NIL) AND (e^.WhichQ=active) )
516 END IsOnActiveQueue ;
520 RemoveFromActive - removes an event, e, from the active queue.
523 PROCEDURE RemoveFromActive (e: EVENT) ;
524 BEGIN
525 IF ActiveQueue=e
526 THEN
527 SubFrom(ActiveQueue, e) ;
528 (* providing that the ActiveQueue is non empty we need to
529 modify first event ticks as we have removed the first event, e. *)
530 IF ActiveQueue#NIL
531 THEN
532 INC(ActiveQueue^.NoOfTicks, e^.NoOfTicks)
534 ELSE
535 (* providing that event, e, is not the last event on the list then
536 update the next event by the time of, e. *)
537 IF e^.EventQ.Right#ActiveQueue
538 THEN
539 INC(e^.EventQ.Right^.NoOfTicks, e^.NoOfTicks)
540 END ;
541 SubFrom(ActiveQueue, e)
543 END RemoveFromActive ;
547 InsertBefore - insert an event, new, on a circular event queue BEFORE
548 event, pos.
551 PROCEDURE InsertBefore (VAR Head: EVENT; pos, new: EVENT) ;
552 BEGIN
553 IF Head=NIL
554 THEN
555 (* empty queue *)
556 Head := new ;
557 new^.EventQ.Right := new ;
558 new^.EventQ.Left := new
559 ELSIF Head=pos
560 THEN
561 (* insert before the first element on the queue *)
562 new^.EventQ.Right := pos ;
563 new^.EventQ.Left := pos^.EventQ.Left ;
564 pos^.EventQ.Left^.EventQ.Right := new ;
565 pos^.EventQ.Left := new ;
566 Head := new
567 ELSE
568 (* insert before any other element *)
569 new^.EventQ.Right := pos ;
570 new^.EventQ.Left := pos^.EventQ.Left ;
571 pos^.EventQ.Left^.EventQ.Right := new ;
572 pos^.EventQ.Left := new
574 END InsertBefore ;
578 InsertAfter - place an event, new, AFTER the event pos on any circular event queue.
581 PROCEDURE InsertAfter (pos, new: EVENT) ;
582 BEGIN
583 new^.EventQ.Right := pos^.EventQ.Right ;
584 new^.EventQ.Left := pos ;
585 pos^.EventQ.Right^.EventQ.Left := new ;
586 pos^.EventQ.Right := new
587 END InsertAfter ;
591 RelativeAddToActive - the active event queue is an ordered queue of
592 relative time events.
593 The event, e, is inserted at the appropriate
594 position in the queue. The event, e, enters
595 this routine with an absolute NoOfTicks field which
596 is then used to work out the relative position
597 of the event. After the position is found then
598 the absolute NoOfTicks field is altered to a
599 relative value and inserted on the queue.
602 PROCEDURE RelativeAddToActive (e: EVENT) ;
604 t : EVENT ;
605 sum: CARDINAL ;
606 BEGIN
607 IF ActiveQueue = NIL
608 THEN
609 (* simple as the queue is empty (relative=absolute) *)
610 InsertBefore (ActiveQueue, ActiveQueue, e)
611 ELSE
612 (* at the end of the while loop sum will contain the total of all
613 events up to but not including, t.
614 If the value of sum is < e^.NoOfTicks then e must be placed at the end
615 >= e^.NoOfTicks then e needs to be placed in the middle
618 sum := ActiveQueue^.NoOfTicks ;
619 t := ActiveQueue^.EventQ.Right ; (* second event *)
620 WHILE (sum < e^.NoOfTicks) AND (t # ActiveQueue) DO
621 INC (sum, t^.NoOfTicks) ;
622 t := t^.EventQ.Right
623 END ;
624 IF sum < e^.NoOfTicks
625 THEN
626 (* e will occur after all the current ActiveQueue has expired therefore
627 we must add it to the end of the ActiveQueue. *)
628 DEC (e^.NoOfTicks, sum) ;
629 InsertAfter (ActiveQueue^.EventQ.Left, e)
630 ELSE
631 (* as sum >= e^.NoOfTicks we know that e is scheduled to occur
632 in the middle of the queue but before t^.Left
634 DEC (e^.NoOfTicks, sum-t^.EventQ.Left^.NoOfTicks) ;
635 InsertBefore (ActiveQueue, t^.EventQ.Left, e)
636 END ;
637 (* the first event after e must have its relative NoOfTicks altered *)
638 IF e^.EventQ.Right # ActiveQueue
639 THEN
640 DEC (e^.EventQ.Right^.NoOfTicks, e^.NoOfTicks)
643 END RelativeAddToActive ;
647 AddTo - adds an event to a specified queue.
650 PROCEDURE AddTo (VAR Head: EVENT; e: EVENT) ;
651 BEGIN
652 IF Head=NIL
653 THEN
654 Head := e ;
655 e^.EventQ.Left := e ;
656 e^.EventQ.Right := e
657 ELSE
658 e^.EventQ.Right := Head ;
659 e^.EventQ.Left := Head^.EventQ.Left ;
660 Head^.EventQ.Left^.EventQ.Right := e ;
661 Head^.EventQ.Left := e
663 END AddTo ;
667 SubFrom - removes an event from a queue.
670 PROCEDURE SubFrom (VAR Head: EVENT; e: EVENT) ;
671 BEGIN
672 IF (e^.EventQ.Left = Head) AND (e = Head)
673 THEN
674 Head := NIL
675 ELSE
676 IF Head = e
677 THEN
678 Head := Head^.EventQ.Right
679 END ;
680 e^.EventQ.Left^.EventQ.Right := e^.EventQ.Right ;
681 e^.EventQ.Right^.EventQ.Left := e^.EventQ.Left
683 END SubFrom ;
687 DisplayActive - display the active queue.
690 PROCEDURE DisplayActive ;
692 e: EVENT ;
693 BEGIN
694 e := ActiveQueue ;
695 IF e#NIL
696 THEN
697 REPEAT
698 DisplayEvent(e) ;
699 e := e^.EventQ.Right
700 UNTIL e=ActiveQueue
702 END DisplayActive ;
706 DisplayEvent - display a single event, e.
709 PROCEDURE DisplayEvent (e: EVENT) ;
711 a: ARRAY [0..20] OF CHAR ;
712 BEGIN
713 WITH e^ DO
714 CardToStr(NoOfTicks, 6, a) ;
715 DebugString(a) ;
716 DebugString(' process (') ;
717 IF Process=NIL
718 THEN
719 DebugString('is NIL') ;
720 ELSE
721 ProcessName(Process)
722 END ;
723 DebugString(')') ;
724 IF WasCancelled
725 THEN
726 DebugString(' has been cancelled')
728 END ;
729 DebugString('\n')
730 END DisplayEvent ;
734 InitQueue -
737 PROCEDURE InitQueue (VAR q: Queue) ;
738 BEGIN
739 q.Right := NIL ;
740 q.Left := NIL
741 END InitQueue ;
745 Init - starts the timer process and initializes some queues.
748 PROCEDURE Init ;
750 d: DESCRIPTOR ;
751 BEGIN
752 TotalTicks := 0 ;
753 CurrentQuanta := 0 ;
754 ActiveQueue := NIL ;
755 DeadQueue := NIL ;
756 d := Resume(InitProcess(Timer, TimerStackSize, 'Timer'))
757 END Init ;
760 BEGIN
761 Init
762 END TimerHandler.