1 ------------------------------------------------------------------------------
3 -- GNU ADA RUN-TIME LIBRARY (GNARL) COMPONENTS --
5 -- S Y S T E M . T A S K _ P R I M I T I V E S . O P E R A T I O N S --
9 -- Copyright (C) 1992-2001, Free Software Foundation, Inc. --
11 -- GNARL is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 2, or (at your option) any later ver- --
14 -- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNARL; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
20 -- MA 02111-1307, USA. --
22 -- As a special exception, if other files instantiate generics from this --
23 -- unit, or you link this unit with other files to produce an executable, --
24 -- this unit does not by itself cause the resulting executable to be --
25 -- covered by the GNU General Public License. This exception does not --
26 -- however invalidate any other reasons why the executable file might be --
27 -- covered by the GNU Public License. --
29 -- GNARL was developed by the GNARL team at Florida State University. It is --
30 -- now maintained by Ada Core Technologies, Inc. (http://www.gnat.com). --
32 ------------------------------------------------------------------------------
34 -- RT GNU/Linux version
36 -- ???? Later, look at what we might want to provide for interrupt
39 pragma Suppress
(All_Checks
);
42 -- Turn off polling, we do not want ATC polling to take place during
43 -- tasking operations. It causes infinite loops and other problems.
45 with System
.Machine_Code
;
48 with System
.OS_Interface
;
49 -- used for various types, constants, and operations
51 with System
.OS_Primitives
;
52 -- used for Delay_Modes
54 with System
.Parameters
;
57 with System
.Storage_Elements
;
60 -- used for Ada_Task_Control_Block
63 with Ada
.Unchecked_Conversion
;
65 package body System
.Task_Primitives
.Operations
is
67 use System
.Machine_Code
,
72 System
.Storage_Elements
;
74 --------------------------------
75 -- RT GNU/Linux specific Data --
76 --------------------------------
78 -- Define two important parameters necessary for a GNU/Linux kernel module.
79 -- Any module that is going to be loaded into the kernel space needs these
82 Mod_Use_Count
: Integer;
83 pragma Export
(C
, Mod_Use_Count
, "mod_use_count_");
84 -- for module usage tracking by the kernel
86 type Aliased_String
is array (Positive range <>) of aliased Character;
87 pragma Convention
(C
, Aliased_String
);
89 Kernel_Version
: constant Aliased_String
:= "2.0.33" & ASCII
.Nul
;
90 pragma Export
(C
, Kernel_Version
, "kernel_version");
91 -- So that insmod can find the version number.
93 -- The following procedures have their name specified by the GNU/Linux
94 -- module loader. Note that they simply correspond to adainit/adafinal.
96 function Init_Module
return Integer;
97 pragma Export
(C
, Init_Module
, "init_module");
99 procedure Cleanup_Module
;
100 pragma Export
(C
, Cleanup_Module
, "cleanup_module");
106 LF
: constant String := ASCII
.LF
& ASCII
.Nul
;
108 LFHT
: constant String := ASCII
.LF
& ASCII
.HT
;
109 -- used in inserted assembly code
111 Max_Tasks
: constant := 10;
112 -- ??? Eventually, this should probably be in System.Parameters.
114 Known_Tasks
: array (0 .. Max_Tasks
) of Task_ID
;
115 -- Global array of tasks read by gdb, and updated by Create_Task and
116 -- Finalize_TCB. It's from System.Tasking.Debug. We moved it here to
117 -- cut the dependence on that package. Consider moving it here or to
118 -- this package specification, permanently????
120 Max_Sensible_Delay
: constant RTIME
:=
121 365 * 24 * 60 * 60 * RT_TICKS_PER_SEC
;
122 -- Max of one year delay, needed to prevent exceptions for large
123 -- delay values. It seems unlikely that any test will notice this
125 -- ??? This is really declared in System.OS_Primitives,
126 -- and the type is Duration, here its type is RTIME.
128 Tick_Count
: constant := RT_TICKS_PER_SEC
/ 20;
129 Nano_Count
: constant := 50_000_000
;
130 -- two constants used in conversions between RTIME and Duration.
132 Addr_Bytes
: constant Storage_Offset
:=
133 System
.Address
'Max_Size_In_Storage_Elements;
134 -- number of bytes needed for storing an address.
136 Guess
: constant RTIME
:= 10;
137 -- an approximate amount of RTIME used in scheduler to awake a task having
138 -- its resume time within 'current time + Guess'
139 -- The value of 10 is estimated here and may need further refinement
141 TCB_Array
: array (0 .. Max_Tasks
)
142 of aliased Restricted_Ada_Task_Control_Block
(Entry_Num
=> 0);
143 pragma Volatile_Components
(TCB_Array
);
145 Available_TCBs
: Task_ID
;
146 pragma Atomic
(Available_TCBs
);
147 -- Head of linear linked list of available TCB's, linked using TCB's
148 -- LL.Next. This list is Initialized to contain a fixed number of tasks,
149 -- when the runtime system starts up.
151 Current_Task
: Task_ID
;
152 pragma Export
(C
, Current_Task
, "current_task");
153 pragma Atomic
(Current_Task
);
154 -- This is the task currently running. We need the pragma here to specify
155 -- the link-name for Current_Task is "current_task", rather than the long
156 -- name (including the package name) that the Ada compiler would normally
157 -- generate. "current_task" is referenced in procedure Rt_Switch_To below
159 Idle_Task
: aliased Restricted_Ada_Task_Control_Block
(Entry_Num
=> 0);
160 -- Tail of the circular queue of ready to run tasks.
162 Scheduler_Idle
: Boolean := False;
163 -- True when the scheduler is idle (no task other than the idle task
164 -- is on the ready queue).
166 In_Elab_Code
: Boolean := True;
167 -- True when we are elaborating our application.
168 -- Init_Module will set this flag to false and never revert it.
170 Timer_Queue
: aliased Restricted_Ada_Task_Control_Block
(Entry_Num
=> 0);
171 -- Header of the queue of delayed real-time tasks.
172 -- Timer_Queue.LL has to be initialized properly before being used
174 Timer_Expired
: Boolean := False;
175 -- flag to show whether the Timer_Queue needs to be checked
176 -- when it becomes true, it means there is a task in the
177 -- Timer_Queue having to be awakened and be moved to ready queue
179 Environment_Task_ID
: Task_ID
;
180 -- A variable to hold Task_ID for the environment task.
181 -- Once initialized, this behaves as a constant.
182 -- In the current implementation, this is the task assigned permanently
183 -- as the regular GNU/Linux kernel.
185 Single_RTS_Lock
: aliased RTS_Lock
;
186 -- This is a lock to allow only one thread of control in the RTS at
187 -- a time; it is used to execute in mutual exclusion from all other tasks.
188 -- Used mainly in Single_Lock mode, but also to protect All_Tasks_List
190 -- The followings are internal configuration constants needed.
191 Next_Serial_Number
: Task_Serial_Number
:= 100;
192 pragma Volatile
(Next_Serial_Number
);
193 -- We start at 100, to reserve some special values for
194 -- using in error checking.
196 GNU_Linux_Irq_State
: Integer := 0;
197 -- This needs comments ???
199 type Duration_As_Integer
is delta 1.0
200 range -2.0**(Duration'Size - 1) .. 2.0**(Duration'Size - 1) - 1.0;
201 -- used for output RTIME value during debugging
203 type Address_Ptr
is access all System
.Address
;
204 pragma Convention
(C
, Address_Ptr
);
206 --------------------------------
207 -- Local conversion functions --
208 --------------------------------
210 function To_Task_ID
is new
211 Ada
.Unchecked_Conversion
(System
.Address
, Task_ID
);
213 function To_Address
is new
214 Ada
.Unchecked_Conversion
(Task_ID
, System
.Address
);
216 function RTIME_To_D_Int
is new
217 Ada
.Unchecked_Conversion
(RTIME
, Duration_As_Integer
);
219 function Raw_RTIME
is new
220 Ada
.Unchecked_Conversion
(Duration, RTIME
);
222 function Raw_Duration
is new
223 Ada
.Unchecked_Conversion
(RTIME
, Duration);
225 function To_Duration
(T
: RTIME
) return Duration;
226 pragma Inline
(To_Duration
);
228 function To_RTIME
(D
: Duration) return RTIME
;
229 pragma Inline
(To_RTIME
);
231 function To_Integer
is new
232 Ada
.Unchecked_Conversion
(System
.Parameters
.Size_Type
, Integer);
234 function To_Address_Ptr
is
235 new Ada
.Unchecked_Conversion
(System
.Address
, Address_Ptr
);
237 function To_RTS_Lock_Ptr
is new
238 Ada
.Unchecked_Conversion
(Lock_Ptr
, RTS_Lock_Ptr
);
240 -----------------------------------
241 -- Local Subprogram Declarations --
242 -----------------------------------
244 procedure Rt_Switch_To
(Tsk
: Task_ID
);
245 pragma Inline
(Rt_Switch_To
);
246 -- switch from the 'current_task' to 'Tsk'
247 -- and 'Tsk' then becomes 'current_task'
249 procedure R_Save_Flags
(F
: out Integer);
250 pragma Inline
(R_Save_Flags
);
251 -- save EFLAGS register to 'F'
253 procedure R_Restore_Flags
(F
: Integer);
254 pragma Inline
(R_Restore_Flags
);
255 -- restore EFLAGS register from 'F'
258 pragma Inline
(R_Cli
);
259 -- disable interrupts
262 pragma Inline
(R_Sti
);
265 procedure Timer_Wrapper
;
266 -- the timer handler. It sets Timer_Expired flag to True and
267 -- then calls Rt_Schedule
269 procedure Rt_Schedule
;
272 procedure Insert_R
(T
: Task_ID
);
273 pragma Inline
(Insert_R
);
274 -- insert 'T' into the tail of the ready queue for its active
276 -- if original queue is 6 5 4 4 3 2 and T has priority of 4
277 -- then after T is inserted the queue becomes 6 5 4 4 T 3 2
279 procedure Insert_RF
(T
: Task_ID
);
280 pragma Inline
(Insert_RF
);
281 -- insert 'T' into the front of the ready queue for its active
283 -- if original queue is 6 5 4 4 3 2 and T has priority of 4
284 -- then after T is inserted the queue becomes 6 5 T 4 4 3 2
286 procedure Delete_R
(T
: Task_ID
);
287 pragma Inline
(Delete_R
);
288 -- delete 'T' from the ready queue. If 'T' is not in any queue
289 -- the operation has no effect
291 procedure Insert_T
(T
: Task_ID
);
292 pragma Inline
(Insert_T
);
293 -- insert 'T' into the waiting queue according to its Resume_Time.
294 -- If there are tasks in the waiting queue that have the same
295 -- Resume_Time as 'T', 'T' is then inserted into the queue for
296 -- its active priority
298 procedure Delete_T
(T
: Task_ID
);
299 pragma Inline
(Delete_T
);
300 -- delete 'T' from the waiting queue.
302 procedure Move_Top_Task_From_Timer_Queue_To_Ready_Queue
;
303 pragma Inline
(Move_Top_Task_From_Timer_Queue_To_Ready_Queue
);
304 -- remove the task in the front of the waiting queue and insert it
305 -- into the tail of the ready queue for its active priority
307 -------------------------
308 -- Local Subprograms --
309 -------------------------
311 procedure Rt_Switch_To
(Tsk
: Task_ID
) is
313 pragma Debug
(Printk
("procedure Rt_Switch_To called" & LF
));
316 "pushl %%eax" & LFHT
&
317 "pushl %%ebp" & LFHT
&
318 "pushl %%edi" & LFHT
&
319 "pushl %%esi" & LFHT
&
320 "pushl %%edx" & LFHT
&
321 "pushl %%ecx" & LFHT
&
322 "pushl %%ebx" & LFHT
&
324 "movl current_task, %%edx" & LFHT
&
325 "cmpl $0, 36(%%edx)" & LFHT
&
326 -- 36 is hard-coded, 36(%%edx) is actually
327 -- Current_Task.Common.LL.Uses_Fp
330 "sub $108,%%esp" & LFHT
&
331 "fsave (%%esp)" & LFHT
&
332 "25: pushl $1f" & LFHT
&
333 "movl %%esp, 32(%%edx)" & LFHT
&
334 -- 32 is hard-coded, 32(%%edx) is actually
335 -- Current_Task.Common.LL.Stack
337 "movl 32(%%ecx), %%esp" & LFHT
&
338 -- 32 is hard-coded, 32(%%ecx) is actually Tsk.Common.LL.Stack.
339 -- Tsk is the task to be switched to
341 "movl %%ecx, current_task" & LFHT
&
343 "1: cmpl $0, 36(%%ecx)" & LFHT
&
344 -- 36(%%exc) is Tsk.Common.LL.Stack (hard coded)
346 "frstor (%%esp)" & LFHT
&
347 "add $108,%%esp" & LFHT
&
348 "26: popl %%ebx" & LFHT
&
349 "popl %%ecx" & LFHT
&
350 "popl %%edx" & LFHT
&
351 "popl %%esi" & LFHT
&
352 "popl %%edi" & LFHT
&
353 "popl %%ebp" & LFHT
&
355 Outputs
=> No_Output_Operands
,
356 Inputs
=> Task_ID
'Asm_Input ("c", Tsk
),
361 procedure R_Save_Flags
(F
: out Integer) is
366 Outputs
=> Integer'Asm_Output ("=g", F
),
367 Inputs
=> No_Input_Operands
,
372 procedure R_Restore_Flags
(F
: Integer) is
377 Outputs
=> No_Output_Operands
,
378 Inputs
=> Integer'Asm_Input ("g", F
),
387 Outputs
=> No_Output_Operands
,
388 Inputs
=> No_Input_Operands
,
397 Outputs
=> No_Output_Operands
,
398 Inputs
=> No_Input_Operands
,
403 -- A wrapper for Rt_Schedule, works as the timer handler
405 procedure Timer_Wrapper
is
407 pragma Debug
(Printk
("procedure Timer_Wrapper called" & LF
));
409 Timer_Expired
:= True;
413 procedure Rt_Schedule
is
418 procedure Debug_Timer_Queue
;
419 -- Check the state of the Timer Queue.
421 procedure Debug_Timer_Queue
is
423 if Timer_Queue
.Common
.LL
.Succ
/= Timer_Queue
'Address then
424 Printk
("Timer_Queue not empty" & LF
);
427 if To_Task_ID
(Timer_Queue
.Common
.LL
.Succ
).Common
.LL
.Resume_Time
<
430 Printk
("and need to move top task to ready queue" & LF
);
432 end Debug_Timer_Queue
;
435 pragma Debug
(Printk
("procedure Rt_Schedule called" & LF
));
437 -- Scheduler_Idle means that this call comes from an interrupt
438 -- handler (e.g timer) that interrupted the idle loop below.
440 if Scheduler_Idle
then
445 R_Save_Flags
(Flags
);
448 Scheduler_Idle
:= False;
450 if Timer_Expired
then
451 pragma Debug
(Printk
("Timer expired" & LF
));
452 Timer_Expired
:= False;
454 -- Check for expired time delays.
457 -- Need another (circular) queue for delayed tasks, this one ordered
458 -- by wakeup time, so the one at the front has the earliest resume
459 -- time. Wake up all the tasks sleeping on time delays that should
460 -- be awakened at this time.
462 -- ??? This is not very good, since we may waste time here waking
463 -- up a bunch of lower priority tasks, adding to the blocking time
464 -- of higher priority ready tasks, but we don't see how to get
465 -- around this without adding more wasted time elsewhere.
467 pragma Debug
(Debug_Timer_Queue
);
469 while Timer_Queue
.Common
.LL
.Succ
/= Timer_Queue
'Address and then
471 (Timer_Queue
.Common
.LL
.Succ
).Common
.LL
.Resume_Time
< Now
+ Guess
473 To_Task_ID
(Timer_Queue
.Common
.LL
.Succ
).Common
.LL
.State
:=
475 Move_Top_Task_From_Timer_Queue_To_Ready_Queue
;
478 -- Arm the timer if necessary.
479 -- ??? This may be wasteful, if the tasks on the timer queue are
480 -- of lower priority than the current task's priority. The problem
481 -- is that we can't tell this without scanning the whole timer
482 -- queue. This scanning takes extra time.
484 if Timer_Queue
.Common
.LL
.Succ
/= Timer_Queue
'Address then
485 -- Timer_Queue is not empty, so set the timer to interrupt at
486 -- the next resume time. The Wakeup procedure must also do this,
487 -- and must do it while interrupts are disabled so that there is
488 -- no danger of interleaving with this code.
490 (To_Task_ID
(Timer_Queue
.Common
.LL
.Succ
).Common
.LL
.Resume_Time
);
496 Top_Task
:= To_Task_ID
(Idle_Task
.Common
.LL
.Succ
);
498 -- If the ready queue is empty, the kernel has to wait until the timer
499 -- or another interrupt makes a task ready.
501 if Top_Task
= To_Task_ID
(Idle_Task
'Address) then
502 Scheduler_Idle
:= True;
503 R_Restore_Flags
(Flags
);
504 pragma Debug
(Printk
("!!!kernel idle!!!" & LF
));
508 if Top_Task
= Current_Task
then
509 pragma Debug
(Printk
("Rt_Schedule: Top_Task = Current_Task" & LF
));
510 -- if current task continues, just return.
512 R_Restore_Flags
(Flags
);
516 if Top_Task
= Environment_Task_ID
then
518 ("Rt_Schedule: Top_Task = Environment_Task" & LF
));
519 -- If there are no RT tasks ready, we execute the regular
520 -- GNU/Linux kernel, and allow the regular GNU/Linux interrupt
521 -- handlers to preempt the current task again.
523 if not In_Elab_Code
then
524 SFIF
:= GNU_Linux_Irq_State
;
527 elsif Current_Task
= Environment_Task_ID
then
529 ("Rt_Schedule: Current_Task = Environment_Task" & LF
));
530 -- We are going to preempt the regular GNU/Linux kernel to
531 -- execute an RT task, so don't allow the regular GNU/Linux
532 -- interrupt handlers to preempt the current task any more.
534 GNU_Linux_Irq_State
:= SFIF
;
538 Top_Task
.Common
.LL
.State
:= RT_TASK_READY
;
539 Rt_Switch_To
(Top_Task
);
540 R_Restore_Flags
(Flags
);
543 procedure Insert_R
(T
: Task_ID
) is
544 Q
: Task_ID
:= To_Task_ID
(Idle_Task
.Common
.LL
.Succ
);
546 pragma Debug
(Printk
("procedure Insert_R called" & LF
));
548 pragma Assert
(T
.Common
.LL
.Succ
= To_Address
(T
));
549 pragma Assert
(T
.Common
.LL
.Pred
= To_Address
(T
));
551 -- T is inserted in the queue between a task that has higher
552 -- or the same Active_Priority as T and a task that has lower
553 -- Active_Priority than T
555 while Q
/= To_Task_ID
(Idle_Task
'Address)
556 and then T
.Common
.LL
.Active_Priority
<= Q
.Common
.LL
.Active_Priority
558 Q
:= To_Task_ID
(Q
.Common
.LL
.Succ
);
561 -- Q is successor of T
563 T
.Common
.LL
.Succ
:= To_Address
(Q
);
564 T
.Common
.LL
.Pred
:= Q
.Common
.LL
.Pred
;
565 To_Task_ID
(T
.Common
.LL
.Pred
).Common
.LL
.Succ
:= To_Address
(T
);
566 Q
.Common
.LL
.Pred
:= To_Address
(T
);
569 procedure Insert_RF
(T
: Task_ID
) is
570 Q
: Task_ID
:= To_Task_ID
(Idle_Task
.Common
.LL
.Succ
);
572 pragma Debug
(Printk
("procedure Insert_RF called" & LF
));
574 pragma Assert
(T
.Common
.LL
.Succ
= To_Address
(T
));
575 pragma Assert
(T
.Common
.LL
.Pred
= To_Address
(T
));
577 -- T is inserted in the queue between a task that has higher
578 -- Active_Priority as T and a task that has lower or the same
579 -- Active_Priority as T
581 while Q
/= To_Task_ID
(Idle_Task
'Address) and then
582 T
.Common
.LL
.Active_Priority
< Q
.Common
.LL
.Active_Priority
584 Q
:= To_Task_ID
(Q
.Common
.LL
.Succ
);
587 -- Q is successor of T
589 T
.Common
.LL
.Succ
:= To_Address
(Q
);
590 T
.Common
.LL
.Pred
:= Q
.Common
.LL
.Pred
;
591 To_Task_ID
(T
.Common
.LL
.Pred
).Common
.LL
.Succ
:= To_Address
(T
);
592 Q
.Common
.LL
.Pred
:= To_Address
(T
);
595 procedure Delete_R
(T
: Task_ID
) is
596 Tpred
: constant Task_ID
:= To_Task_ID
(T
.Common
.LL
.Pred
);
597 Tsucc
: constant Task_ID
:= To_Task_ID
(T
.Common
.LL
.Succ
);
600 pragma Debug
(Printk
("procedure Delete_R called" & LF
));
602 -- checking whether T is in the queue is not necessary because
603 -- if T is not in the queue, following statements changes
604 -- nothing. But T cannot be in the Timer_Queue, otherwise
605 -- activate the check below, note that checking whether T is
606 -- in a queue is a relatively expensive operation
608 Tpred
.Common
.LL
.Succ
:= To_Address
(Tsucc
);
609 Tsucc
.Common
.LL
.Pred
:= To_Address
(Tpred
);
610 T
.Common
.LL
.Succ
:= To_Address
(T
);
611 T
.Common
.LL
.Pred
:= To_Address
(T
);
614 procedure Insert_T
(T
: Task_ID
) is
615 Q
: Task_ID
:= To_Task_ID
(Timer_Queue
.Common
.LL
.Succ
);
617 pragma Debug
(Printk
("procedure Insert_T called" & LF
));
619 pragma Assert
(T
.Common
.LL
.Succ
= To_Address
(T
));
621 while Q
/= To_Task_ID
(Timer_Queue
'Address) and then
622 T
.Common
.LL
.Resume_Time
> Q
.Common
.LL
.Resume_Time
624 Q
:= To_Task_ID
(Q
.Common
.LL
.Succ
);
627 -- Q is the task that has Resume_Time equal to or greater than that
628 -- of T. If they have the same Resume_Time, continue looking for the
629 -- location T is to be inserted using its Active_Priority
631 while Q
/= To_Task_ID
(Timer_Queue
'Address) and then
632 T
.Common
.LL
.Resume_Time
= Q
.Common
.LL
.Resume_Time
634 exit when T
.Common
.LL
.Active_Priority
> Q
.Common
.LL
.Active_Priority
;
635 Q
:= To_Task_ID
(Q
.Common
.LL
.Succ
);
638 -- Q is successor of T
640 T
.Common
.LL
.Succ
:= To_Address
(Q
);
641 T
.Common
.LL
.Pred
:= Q
.Common
.LL
.Pred
;
642 To_Task_ID
(T
.Common
.LL
.Pred
).Common
.LL
.Succ
:= To_Address
(T
);
643 Q
.Common
.LL
.Pred
:= To_Address
(T
);
646 procedure Delete_T
(T
: Task_ID
) is
647 Tpred
: constant Task_ID
:= To_Task_ID
(T
.Common
.LL
.Pred
);
648 Tsucc
: constant Task_ID
:= To_Task_ID
(T
.Common
.LL
.Succ
);
651 pragma Debug
(Printk
("procedure Delete_T called" & LF
));
653 pragma Assert
(T
/= To_Task_ID
(Timer_Queue
'Address));
655 Tpred
.Common
.LL
.Succ
:= To_Address
(Tsucc
);
656 Tsucc
.Common
.LL
.Pred
:= To_Address
(Tpred
);
657 T
.Common
.LL
.Succ
:= To_Address
(T
);
658 T
.Common
.LL
.Pred
:= To_Address
(T
);
661 procedure Move_Top_Task_From_Timer_Queue_To_Ready_Queue
is
662 Top_Task
: Task_ID
:= To_Task_ID
(Timer_Queue
.Common
.LL
.Succ
);
664 pragma Debug
(Printk
("procedure Move_Top_Task called" & LF
));
666 if Top_Task
/= To_Task_ID
(Timer_Queue
'Address) then
668 Top_Task
.Common
.LL
.State
:= RT_TASK_READY
;
671 end Move_Top_Task_From_Timer_Queue_To_Ready_Queue
;
677 function Self
return Task_ID
is
679 pragma Debug
(Printk
("function Self called" & LF
));
684 ---------------------
685 -- Initialize_Lock --
686 ---------------------
688 procedure Initialize_Lock
(Prio
: System
.Any_Priority
; L
: access Lock
) is
690 pragma Debug
(Printk
("procedure Initialize_Lock called" & LF
));
692 L
.Ceiling_Priority
:= Prio
;
693 L
.Owner
:= System
.Null_Address
;
696 procedure Initialize_Lock
(L
: access RTS_Lock
; Level
: Lock_Level
) is
698 pragma Debug
(Printk
("procedure Initialize_Lock (RTS) called" & LF
));
700 L
.Ceiling_Priority
:= System
.Any_Priority
'Last;
701 L
.Owner
:= System
.Null_Address
;
708 procedure Finalize_Lock
(L
: access Lock
) is
710 pragma Debug
(Printk
("procedure Finalize_Lock called" & LF
));
714 procedure Finalize_Lock
(L
: access RTS_Lock
) is
716 pragma Debug
(Printk
("procedure Finalize_Lock (RTS) called" & LF
));
724 procedure Write_Lock
(L
: access Lock
; Ceiling_Violation
: out Boolean) is
725 Prio
: constant System
.Any_Priority
:=
726 Current_Task
.Common
.LL
.Active_Priority
;
729 pragma Debug
(Printk
("procedure Write_Lock called" & LF
));
731 Ceiling_Violation
:= False;
733 if Prio
> L
.Ceiling_Priority
then
734 -- Ceiling violation.
735 -- This should never happen, unless something is seriously
736 -- wrong with task T or the entire run-time system.
737 -- ???? extreme error recovery, e.g. shut down the system or task
739 Ceiling_Violation
:= True;
740 pragma Debug
(Printk
("Ceiling Violation in Write_Lock" & LF
));
744 L
.Pre_Locking_Priority
:= Prio
;
745 L
.Owner
:= To_Address
(Current_Task
);
746 Current_Task
.Common
.LL
.Active_Priority
:= L
.Ceiling_Priority
;
748 if Current_Task
.Common
.LL
.Outer_Lock
= null then
749 -- If this lock is not nested, record a pointer to it.
751 Current_Task
.Common
.LL
.Outer_Lock
:=
752 To_RTS_Lock_Ptr
(L
.all'Unchecked_Access);
757 (L
: access RTS_Lock
; Global_Lock
: Boolean := False)
759 Prio
: constant System
.Any_Priority
:=
760 Current_Task
.Common
.LL
.Active_Priority
;
763 pragma Debug
(Printk
("procedure Write_Lock (RTS) called" & LF
));
765 if Prio
> L
.Ceiling_Priority
then
766 -- Ceiling violation.
767 -- This should never happen, unless something is seriously
768 -- wrong with task T or the entire runtime system.
769 -- ???? extreme error recovery, e.g. shut down the system or task
771 Printk
("Ceiling Violation in Write_Lock (RTS)" & LF
);
775 L
.Pre_Locking_Priority
:= Prio
;
776 L
.Owner
:= To_Address
(Current_Task
);
777 Current_Task
.Common
.LL
.Active_Priority
:= L
.Ceiling_Priority
;
779 if Current_Task
.Common
.LL
.Outer_Lock
= null then
780 Current_Task
.Common
.LL
.Outer_Lock
:= L
.all'Unchecked_Access;
784 procedure Write_Lock
(T
: Task_ID
) is
785 Prio
: constant System
.Any_Priority
:=
786 Current_Task
.Common
.LL
.Active_Priority
;
789 pragma Debug
(Printk
("procedure Write_Lock (Task_ID) called" & LF
));
791 if Prio
> T
.Common
.LL
.L
.Ceiling_Priority
then
792 -- Ceiling violation.
793 -- This should never happen, unless something is seriously
794 -- wrong with task T or the entire runtime system.
795 -- ???? extreme error recovery, e.g. shut down the system or task
797 Printk
("Ceiling Violation in Write_Lock (Task)" & LF
);
801 T
.Common
.LL
.L
.Pre_Locking_Priority
:= Prio
;
802 T
.Common
.LL
.L
.Owner
:= To_Address
(Current_Task
);
803 Current_Task
.Common
.LL
.Active_Priority
:= T
.Common
.LL
.L
.Ceiling_Priority
;
805 if Current_Task
.Common
.LL
.Outer_Lock
= null then
806 Current_Task
.Common
.LL
.Outer_Lock
:= T
.Common
.LL
.L
'Access;
814 procedure Read_Lock
(L
: access Lock
; Ceiling_Violation
: out Boolean) is
816 pragma Debug
(Printk
("procedure Read_Lock called" & LF
));
817 Write_Lock
(L
, Ceiling_Violation
);
824 procedure Unlock
(L
: access Lock
) is
827 pragma Debug
(Printk
("procedure Unlock called" & LF
));
829 if L
.Owner
/= To_Address
(Current_Task
) then
833 Printk
("The caller is not the owner of the lock" & LF
);
837 L
.Owner
:= System
.Null_Address
;
839 -- Now that the lock is released, lower own priority,
841 if Current_Task
.Common
.LL
.Outer_Lock
=
842 To_RTS_Lock_Ptr
(L
.all'Unchecked_Access)
844 -- This lock is the outer-most one, reset own priority to
847 Current_Task
.Common
.LL
.Active_Priority
:=
848 Current_Task
.Common
.Current_Priority
;
849 Current_Task
.Common
.LL
.Outer_Lock
:= null;
852 -- If this lock is nested, pop the old active priority.
854 Current_Task
.Common
.LL
.Active_Priority
:= L
.Pre_Locking_Priority
;
857 -- Reschedule the task if necessary. Note we only need to reschedule
858 -- the task if its Active_Priority becomes less than the one following
859 -- it. The check depends on the fact that Environment_Task (tail of
860 -- the ready queue) has the lowest Active_Priority
862 if Current_Task
.Common
.LL
.Active_Priority
863 < To_Task_ID
(Current_Task
.Common
.LL
.Succ
).Common
.LL
.Active_Priority
865 R_Save_Flags
(Flags
);
867 Delete_R
(Current_Task
);
868 Insert_RF
(Current_Task
);
869 R_Restore_Flags
(Flags
);
874 procedure Unlock
(L
: access RTS_Lock
; Global_Lock
: Boolean := False) is
877 pragma Debug
(Printk
("procedure Unlock (RTS_Lock) called" & LF
));
879 if L
.Owner
/= To_Address
(Current_Task
) then
881 Printk
("The caller is not the owner of the lock" & LF
);
885 L
.Owner
:= System
.Null_Address
;
887 if Current_Task
.Common
.LL
.Outer_Lock
= L
.all'Unchecked_Access then
888 Current_Task
.Common
.LL
.Active_Priority
:=
889 Current_Task
.Common
.Current_Priority
;
890 Current_Task
.Common
.LL
.Outer_Lock
:= null;
893 Current_Task
.Common
.LL
.Active_Priority
:= L
.Pre_Locking_Priority
;
896 -- Reschedule the task if necessary
898 if Current_Task
.Common
.LL
.Active_Priority
899 < To_Task_ID
(Current_Task
.Common
.LL
.Succ
).Common
.LL
.Active_Priority
901 R_Save_Flags
(Flags
);
903 Delete_R
(Current_Task
);
904 Insert_RF
(Current_Task
);
905 R_Restore_Flags
(Flags
);
910 procedure Unlock
(T
: Task_ID
) is
912 pragma Debug
(Printk
("procedure Unlock (Task_ID) called" & LF
));
913 Unlock
(T
.Common
.LL
.L
'Access);
920 -- Unlock Self_ID.Common.LL.L and suspend Self_ID, atomically.
921 -- Before return, lock Self_ID.Common.LL.L again
922 -- Self_ID can only be reactivated by calling Wakeup.
923 -- Unlock code is repeated intentionally.
927 Reason
: ST
.Task_States
)
931 pragma Debug
(Printk
("procedure Sleep called" & LF
));
933 -- Note that Self_ID is actually Current_Task, that is, only the
934 -- task that is running can put itself into sleep. To preserve
935 -- consistency, we use Self_ID throughout the code here
937 Self_ID
.Common
.State
:= Reason
;
938 Self_ID
.Common
.LL
.State
:= RT_TASK_DORMANT
;
940 R_Save_Flags
(Flags
);
945 -- Arrange to unlock Self_ID's ATCB lock. The following check
946 -- may be unnecessary because the specification of Sleep says
947 -- the caller should hold its own ATCB lock before calling Sleep
949 if Self_ID
.Common
.LL
.L
.Owner
= To_Address
(Self_ID
) then
950 Self_ID
.Common
.LL
.L
.Owner
:= System
.Null_Address
;
952 if Self_ID
.Common
.LL
.Outer_Lock
= Self_ID
.Common
.LL
.L
'Access then
953 Self_ID
.Common
.LL
.Active_Priority
:=
954 Self_ID
.Common
.Current_Priority
;
955 Self_ID
.Common
.LL
.Outer_Lock
:= null;
958 Self_ID
.Common
.LL
.Active_Priority
:=
959 Self_ID
.Common
.LL
.L
.Pre_Locking_Priority
;
963 R_Restore_Flags
(Flags
);
966 -- Before leave, regain the lock
968 Write_Lock
(Self_ID
);
975 -- Arrange to be awakened after/at Time (depending on Mode) then Unlock
976 -- Self_ID.Common.LL.L and suspend self. If the timeout expires first,
977 -- that should awaken the task. If it's awakened (by some other task
978 -- calling Wakeup) before the timeout expires, the timeout should be
981 -- This is for use within the run-time system, so abort is
982 -- assumed to be already deferred, and the caller should be
983 -- holding its own ATCB lock.
985 procedure Timed_Sleep
988 Mode
: ST
.Delay_Modes
;
989 Reason
: Task_States
;
990 Timedout
: out Boolean;
991 Yielded
: out Boolean)
997 pragma Debug
(Printk
("procedure Timed_Sleep called" & LF
));
1001 -- ??? These two boolean seems not relevant here
1003 if Mode
= Relative
then
1004 Abs_Time
:= To_RTIME
(Time
) + Rt_Get_Time
;
1006 Abs_Time
:= To_RTIME
(Time
);
1009 Self_ID
.Common
.LL
.Resume_Time
:= Abs_Time
;
1010 Self_ID
.Common
.LL
.State
:= RT_TASK_DELAYED
;
1012 R_Save_Flags
(Flags
);
1017 -- Check if the timer needs to be set
1019 if Timer_Queue
.Common
.LL
.Succ
= To_Address
(Self_ID
) then
1020 Rt_Set_Timer
(Abs_Time
);
1023 -- Another way to do it
1026 -- To_Task_ID (Timer_Queue.Common.LL.Succ).Common.LL.Resume_Time
1028 -- Rt_Set_Timer (Abs_Time);
1031 -- Arrange to unlock Self_ID's ATCB lock. see comments in Sleep
1033 if Self_ID
.Common
.LL
.L
.Owner
= To_Address
(Self_ID
) then
1034 Self_ID
.Common
.LL
.L
.Owner
:= System
.Null_Address
;
1036 if Self_ID
.Common
.LL
.Outer_Lock
= Self_ID
.Common
.LL
.L
'Access then
1037 Self_ID
.Common
.LL
.Active_Priority
:=
1038 Self_ID
.Common
.Current_Priority
;
1039 Self_ID
.Common
.LL
.Outer_Lock
:= null;
1042 Self_ID
.Common
.LL
.Active_Priority
:=
1043 Self_ID
.Common
.LL
.L
.Pre_Locking_Priority
;
1047 R_Restore_Flags
(Flags
);
1050 -- Before leaving, regain the lock
1052 Write_Lock
(Self_ID
);
1059 -- This is for use in implementing delay statements, so we assume
1060 -- the caller is not abort-deferred and is holding no locks.
1061 -- Self_ID can only be awakened after the timeout, no Wakeup on it.
1063 procedure Timed_Delay
1066 Mode
: ST
.Delay_Modes
)
1072 pragma Debug
(Printk
("procedure Timed_Delay called" & LF
));
1074 -- Only the little window between deferring abort and
1075 -- locking Self_ID is the reason we need to
1076 -- check for pending abort and priority change below! :(
1078 Write_Lock
(Self_ID
);
1080 -- Take the lock in case its ATCB needs to be modified
1082 if Mode
= Relative
then
1083 Abs_Time
:= To_RTIME
(Time
) + Rt_Get_Time
;
1085 Abs_Time
:= To_RTIME
(Time
);
1088 Self_ID
.Common
.LL
.Resume_Time
:= Abs_Time
;
1089 Self_ID
.Common
.LL
.State
:= RT_TASK_DELAYED
;
1091 R_Save_Flags
(Flags
);
1096 -- Check if the timer needs to be set
1098 if Timer_Queue
.Common
.LL
.Succ
= To_Address
(Self_ID
) then
1099 Rt_Set_Timer
(Abs_Time
);
1102 -- Arrange to unlock Self_ID's ATCB lock.
1103 -- Note that the code below is slightly different from Unlock, so
1104 -- it is more than inline it.
1106 if To_Task_ID
(Self_ID
.Common
.LL
.L
.Owner
) = Self_ID
then
1107 Self_ID
.Common
.LL
.L
.Owner
:= System
.Null_Address
;
1109 if Self_ID
.Common
.LL
.Outer_Lock
= Self_ID
.Common
.LL
.L
'Access then
1110 Self_ID
.Common
.LL
.Active_Priority
:=
1111 Self_ID
.Common
.Current_Priority
;
1112 Self_ID
.Common
.LL
.Outer_Lock
:= null;
1115 Self_ID
.Common
.LL
.Active_Priority
:=
1116 Self_ID
.Common
.LL
.L
.Pre_Locking_Priority
;
1120 R_Restore_Flags
(Flags
);
1124 ---------------------
1125 -- Monotonic_Clock --
1126 ---------------------
1128 -- RTIME is represented as a 64-bit signed count of ticks,
1129 -- where there are 1_193_180 ticks per second.
1131 -- Let T be a count of ticks and N the corresponding count of nanoseconds.
1132 -- From the following relationship
1133 -- T / (ticks_per_second) = N / (ns_per_second)
1134 -- where ns_per_second is 1_000_000_000 (number of nanoseconds in
1135 -- a second), we get
1136 -- T * (ns_per_second) = N * (ticks_per_second)
1138 -- T * 1_000_000_000 = N * 1_193_180
1139 -- which can be reduced to
1140 -- T * 50_000_000 = N * 59_659
1141 -- Let Nano_Count = 50_000_000 and Tick_Count = 59_659, we then have
1142 -- T * Nano_Count = N * Tick_Count
1145 -- These numbers are small enough that we can do arithmetic
1146 -- on them without overflowing 64 bits. To see this, observe
1148 -- 10**3 = 1000 < 1024 = 2**10
1149 -- Tick_Count < 60 * 1000 < 64 * 1024 < 2**16
1150 -- Nano_Count < 50 * 1000 * 1000 < 64 * 1024 * 1024 < 2**26
1152 -- It follows that if 0 <= R < Tick_Count, we can compute
1153 -- R * Nano_Count < 2**42 without overflow in 64 bits.
1154 -- Similarly, if 0 <= R < Nano_Count, we can compute
1155 -- R * Tick_Count < 2**42 without overflow in 64 bits.
1157 -- GNAT represents Duration as a count of nanoseconds internally.
1159 -- To convert T from RTIME to Duration, let
1160 -- Q = T / Tick_Count, with truncation
1161 -- R = T - Q * Tick_Count, the remainder 0 <= R < Tick_Count
1164 -- = T * Nano_Count - Q * Tick_Count * Nano_Count
1165 -- + Q * Tick_Count * Nano_Count
1166 -- = (T - Q * Tick_Count) * Nano_Count
1167 -- + (Q * Nano_Count) * Tick_Count
1168 -- = R * Nano_Count + (Q * Nano_Count) * Tick_Count
1171 -- Q1 = R * Nano_Count / Tick_Count, with truncation
1172 -- R1 = R * Nano_Count - Q1 * Tick_Count, 0 <= R1 <Tick_Count
1173 -- R * Nano_Count = Q1 * Tick_Count + R1
1176 -- = R * Nano_Count + (Q * Nano_Count) * Tick_Count
1177 -- = Q1 * Tick_Count + R1 + (Q * Nano_Count) * Tick_Count
1178 -- = R1 + (Q * Nano_Count + Q1) * Tick_Count
1180 -- N = Q * Nano_Count + Q1 + R1 /Tick_Count,
1181 -- where 0 <= R1 /Tick_Count < 1
1183 function To_Duration
(T
: RTIME
) return Duration is
1186 Q
:= T
/ Tick_Count
;
1187 RN
:= (T
- Q
* Tick_Count
) * Nano_Count
;
1188 Q1
:= RN
/ Tick_Count
;
1189 return Raw_Duration
(Q
* Nano_Count
+ Q1
);
1192 -- To convert D from Duration to RTIME,
1193 -- Let D be a Duration value, and N be the representation of D as an
1194 -- integer count of nanoseconds. Let
1195 -- Q = N / Nano_Count, with truncation
1196 -- R = N - Q * Nano_Count, the remainder 0 <= R < Nano_Count
1199 -- = N * Tick_Count - Q * Nano_Count * Tick_Count
1200 -- + Q * Nano_Count * Tick_Count
1201 -- = (N - Q * Nano_Count) * Tick_Count
1202 -- + (Q * Tick_Count) * Nano_Count
1203 -- = R * Tick_Count + (Q * Tick_Count) * Nano_Count
1205 -- Q1 = R * Tick_Count / Nano_Count, with truncation
1206 -- R1 = R * Tick_Count - Q1 * Nano_Count, 0 <= R1 < Nano_Count
1207 -- R * Tick_Count = Q1 * Nano_Count + R1
1210 -- = R * Tick_Count + (Q * Tick_Count) * Nano_Count
1211 -- = Q1 * Nano_Count + R1 + (Q * Tick_Count) * Nano_Count
1212 -- = (Q * Tick_Count + Q1) * Nano_Count + R1
1214 -- T = Q * Tick_Count + Q1 + R1 / Nano_Count,
1215 -- where 0 <= R1 / Nano_Count < 1
1217 function To_RTIME
(D
: Duration) return RTIME
is
1218 N
: RTIME
:= Raw_RTIME
(D
);
1222 Q
:= N
/ Nano_Count
;
1223 RT
:= (N
- Q
* Nano_Count
) * Tick_Count
;
1224 Q1
:= RT
/ Nano_Count
;
1225 return Q
* Tick_Count
+ Q1
;
1228 function Monotonic_Clock
return Duration is
1230 pragma Debug
(Printk
("procedure Clock called" & LF
));
1232 return To_Duration
(Rt_Get_Time
);
1233 end Monotonic_Clock
;
1239 function RT_Resolution
return Duration is
1248 procedure Wakeup
(T
: Task_ID
; Reason
: ST
.Task_States
) is
1251 pragma Debug
(Printk
("procedure Wakeup called" & LF
));
1253 T
.Common
.State
:= Reason
;
1254 T
.Common
.LL
.State
:= RT_TASK_READY
;
1256 R_Save_Flags
(Flags
);
1259 if Timer_Queue
.Common
.LL
.Succ
= To_Address
(T
) then
1260 -- T is the first task in Timer_Queue, further check
1262 if T
.Common
.LL
.Succ
= Timer_Queue
'Address then
1263 -- T is the only task in Timer_Queue, so deactivate timer
1268 -- T is the first task in Timer_Queue, so set timer to T's
1269 -- successor's Resume_Time
1271 Rt_Set_Timer
(To_Task_ID
(T
.Common
.LL
.Succ
).Common
.LL
.Resume_Time
);
1277 -- If T is in Timer_Queue, T is removed. If not, nothing happened
1280 R_Restore_Flags
(Flags
);
1289 procedure Yield
(Do_Yield
: Boolean := True) is
1292 pragma Debug
(Printk
("procedure Yield called" & LF
));
1294 pragma Assert
(Current_Task
/= To_Task_ID
(Idle_Task
'Address));
1296 R_Save_Flags
(Flags
);
1298 Delete_R
(Current_Task
);
1299 Insert_R
(Current_Task
);
1301 -- Remove Current_Task from the top of the Ready_Queue
1302 -- and reinsert it back at proper position (the end of
1303 -- tasks with the same active priority).
1305 R_Restore_Flags
(Flags
);
1313 -- This version implicitly assume that T is the Current_Task
1315 procedure Set_Priority
1317 Prio
: System
.Any_Priority
;
1318 Loss_Of_Inheritance
: Boolean := False)
1322 pragma Debug
(Printk
("procedure Set_Priority called" & LF
));
1323 pragma Assert
(T
= Self
);
1325 T
.Common
.Current_Priority
:= Prio
;
1327 if T
.Common
.LL
.Outer_Lock
/= null then
1328 -- If the task T is holding any lock, defer the priority change
1329 -- until the lock is released. That is, T's Active_Priority will
1330 -- be set to Prio after it unlocks the outer-most lock. See
1331 -- Unlock for detail.
1332 -- Nothing needs to be done here for this case
1336 -- If T is not holding any lock, change the priority right away.
1338 R_Save_Flags
(Flags
);
1340 T
.Common
.LL
.Active_Priority
:= Prio
;
1344 -- Insert at the front of the queue for its new priority
1346 R_Restore_Flags
(Flags
);
1356 function Get_Priority
(T
: Task_ID
) return System
.Any_Priority
is
1358 pragma Debug
(Printk
("procedure Get_Priority called" & LF
));
1360 return T
.Common
.Current_Priority
;
1367 -- Do any target-specific initialization that is needed for a new task
1368 -- that has to be done by the task itself. This is called from the task
1369 -- wrapper, immediately after the task starts execution.
1371 procedure Enter_Task
(Self_ID
: Task_ID
) is
1373 -- Use this as "hook" to re-enable interrupts.
1374 pragma Debug
(Printk
("procedure Enter_Task called" & LF
));
1383 function New_ATCB
(Entry_Num
: Task_Entry_Index
) return Task_ID
is
1384 T
: constant Task_ID
:= Available_TCBs
;
1386 pragma Debug
(Printk
("function New_ATCB called" & LF
));
1388 if Entry_Num
/= 0 then
1389 -- We are preallocating all TCBs, so they must all have the
1390 -- same number of entries, which means the value of
1391 -- Entry_Num must be bounded. We probably could choose a
1392 -- non-zero upper bound here, but the Ravenscar Profile
1393 -- specifies that there be no task entries.
1395 -- Later, do something better for recovery from this error.
1401 Available_TCBs
:= To_Task_ID
(T
.Common
.LL
.Next
);
1402 T
.Common
.LL
.Next
:= System
.Null_Address
;
1403 Known_Tasks
(T
.Known_Tasks_Index
) := T
;
1409 ----------------------
1410 -- Initialize_TCB --
1411 ----------------------
1413 procedure Initialize_TCB
(Self_ID
: Task_ID
; Succeeded
: out Boolean) is
1415 pragma Debug
(Printk
("procedure Initialize_TCB called" & LF
));
1417 -- Give the task a unique serial number.
1419 Self_ID
.Serial_Number
:= Next_Serial_Number
;
1420 Next_Serial_Number
:= Next_Serial_Number
+ 1;
1421 pragma Assert
(Next_Serial_Number
/= 0);
1423 Self_ID
.Common
.LL
.L
.Ceiling_Priority
:= System
.Any_Priority
'Last;
1424 Self_ID
.Common
.LL
.L
.Owner
:= System
.Null_Address
;
1432 procedure Create_Task
1434 Wrapper
: System
.Address
;
1435 Stack_Size
: System
.Parameters
.Size_Type
;
1436 Priority
: System
.Any_Priority
;
1437 Succeeded
: out Boolean)
1439 Adjusted_Stack_Size
: Integer;
1440 Bottom
: System
.Address
;
1444 pragma Debug
(Printk
("procedure Create_Task called" & LF
));
1448 if T
.Common
.LL
.Magic
= RT_TASK_MAGIC
then
1453 if Stack_Size
= Unspecified_Size
then
1454 Adjusted_Stack_Size
:= To_Integer
(Default_Stack_Size
);
1455 elsif Stack_Size
< Minimum_Stack_Size
then
1456 Adjusted_Stack_Size
:= To_Integer
(Minimum_Stack_Size
);
1458 Adjusted_Stack_Size
:= To_Integer
(Stack_Size
);
1461 Bottom
:= Kmalloc
(Adjusted_Stack_Size
, GFP_KERNEL
);
1463 if Bottom
= System
.Null_Address
then
1468 T
.Common
.LL
.Uses_Fp
:= 1;
1470 -- This field has to be reset to 1 if T uses FP unit. But, without
1471 -- a library-level procedure provided by this package, it cannot
1472 -- be set easily. So temporarily, set it to 1 (which means all the
1473 -- tasks will use FP unit. ???
1475 T
.Common
.LL
.Magic
:= RT_TASK_MAGIC
;
1476 T
.Common
.LL
.State
:= RT_TASK_READY
;
1477 T
.Common
.LL
.Succ
:= To_Address
(T
);
1478 T
.Common
.LL
.Pred
:= To_Address
(T
);
1479 T
.Common
.LL
.Active_Priority
:= Priority
;
1480 T
.Common
.Current_Priority
:= Priority
;
1482 T
.Common
.LL
.Stack_Bottom
:= Bottom
;
1483 T
.Common
.LL
.Stack
:= Bottom
+ Storage_Offset
(Adjusted_Stack_Size
);
1485 -- Store the value T into the stack, so that Task_wrapper (defined
1486 -- in System.Tasking.Stages) will find that value for its parameter
1487 -- Self_ID, when the scheduler eventually transfers control to the
1490 T
.Common
.LL
.Stack
:= T
.Common
.LL
.Stack
- Addr_Bytes
;
1491 To_Address_Ptr
(T
.Common
.LL
.Stack
).all := To_Address
(T
);
1493 -- Leave space for the return address, which will not be used,
1494 -- since the task wrapper should never return.
1496 T
.Common
.LL
.Stack
:= T
.Common
.LL
.Stack
- Addr_Bytes
;
1497 To_Address_Ptr
(T
.Common
.LL
.Stack
).all := System
.Null_Address
;
1499 -- Put the entry point address of the task wrapper
1500 -- procedure on the new top of the stack.
1502 T
.Common
.LL
.Stack
:= T
.Common
.LL
.Stack
- Addr_Bytes
;
1503 To_Address_Ptr
(T
.Common
.LL
.Stack
).all := Wrapper
;
1505 R_Save_Flags
(Flags
);
1508 R_Restore_Flags
(Flags
);
1515 procedure Finalize_TCB
(T
: Task_ID
) is
1517 pragma Debug
(Printk
("procedure Finalize_TCB called" & LF
));
1519 pragma Assert
(T
.Common
.LL
.Succ
= To_Address
(T
));
1521 if T
.Common
.LL
.State
= RT_TASK_DORMANT
then
1522 Known_Tasks
(T
.Known_Tasks_Index
) := null;
1523 T
.Common
.LL
.Next
:= To_Address
(Available_TCBs
);
1524 Available_TCBs
:= T
;
1525 Kfree
(T
.Common
.LL
.Stack_Bottom
);
1533 procedure Exit_Task
is
1536 pragma Debug
(Printk
("procedure Exit_Task called" & LF
));
1537 pragma Assert
(Current_Task
/= To_Task_ID
(Idle_Task
'Address));
1538 pragma Assert
(Current_Task
/= Environment_Task_ID
);
1540 R_Save_Flags
(Flags
);
1542 Current_Task
.Common
.LL
.State
:= RT_TASK_DORMANT
;
1543 Current_Task
.Common
.LL
.Magic
:= 0;
1544 Delete_R
(Current_Task
);
1545 R_Restore_Flags
(Flags
);
1553 -- ??? Not implemented for now
1555 procedure Abort_Task
(T
: Task_ID
) is
1556 -- Should cause T to raise Abort_Signal the next time it
1558 -- ??? Can this ever be called when T = Current_Task?
1559 -- To be safe, do nothing in this case.
1561 pragma Debug
(Printk
("procedure Abort_Task called" & LF
));
1569 -- Dummy versions. The only currently working versions is for solaris
1571 -- We should probably copy the working versions over from the Solaris
1572 -- version of this package, with any appropriate changes, since without
1573 -- the checks on it will probably be nearly impossible to debug the
1576 -- Not implemented for now
1578 function Check_Exit
(Self_ID
: Task_ID
) return Boolean is
1580 pragma Debug
(Printk
("function Check_Exit called" & LF
));
1585 --------------------
1586 -- Check_No_Locks --
1587 --------------------
1589 function Check_No_Locks
(Self_ID
: Task_ID
) return Boolean is
1591 pragma Debug
(Printk
("function Check_No_Locks called" & LF
));
1593 if Self_ID
.Common
.LL
.Outer_Lock
= null then
1600 ----------------------
1601 -- Environment_Task --
1602 ----------------------
1604 function Environment_Task
return Task_ID
is
1606 return Environment_Task_ID
;
1607 end Environment_Task
;
1613 procedure Lock_RTS
is
1615 Write_Lock
(Single_RTS_Lock
'Access, Global_Lock
=> True);
1622 procedure Unlock_RTS
is
1624 Unlock
(Single_RTS_Lock
'Access, Global_Lock
=> True);
1631 -- Not implemented for now
1633 procedure Stack_Guard
(T
: Task_ID
; On
: Boolean) is
1638 --------------------
1640 --------------------
1642 function Get_Thread_Id
(T
: Task_ID
) return OSI
.Thread_Id
is
1644 return To_Address
(T
);
1651 function Suspend_Task
1653 Thread_Self
: OSI
.Thread_Id
) return Boolean is
1662 function Resume_Task
1664 Thread_Self
: OSI
.Thread_Id
) return Boolean is
1673 function Init_Module
return Integer is
1675 pragma Import
(C
, adainit
);
1679 In_Elab_Code
:= False;
1680 Set_Priority
(Environment_Task_ID
, Any_Priority
'First);
1684 --------------------
1685 -- Cleanup_Module --
1686 --------------------
1688 procedure Cleanup_Module
is
1690 pragma Import
(C
, adafinal
);
1700 -- The environment task is "special". The TCB of the environment task is
1701 -- not in the TCB_Array above. Logically, all initialization code for the
1702 -- runtime system is executed by the environment task, but until the
1703 -- environment task has initialized its own TCB we dare not execute any
1704 -- calls that try to access the TCB of Current_Task. It is allocated by
1705 -- target-independent runtime system code, in System.Tasking.Initializa-
1706 -- tion.Init_RTS, before the call to this procedure Initialize. The
1707 -- target-independent runtime system initializes all the components that
1708 -- are target-independent, but this package needs to be given a chance to
1709 -- initialize the target-dependent data. We do that in this procedure.
1711 -- In the present implementation, Environment_Task is set to be the
1712 -- regular GNU/Linux kernel task.
1714 procedure Initialize
(Environment_Task
: Task_ID
) is
1716 pragma Debug
(Printk
("procedure Initialize called" & LF
));
1718 Environment_Task_ID
:= Environment_Task
;
1720 -- Build the list of available ATCB's.
1722 Available_TCBs
:= To_Task_ID
(TCB_Array
(1)'Address);
1724 for J
in TCB_Array
'First + 1 .. TCB_Array
'Last - 1 loop
1725 -- Note that the zeroth element in TCB_Array is not used, see
1726 -- comments following the declaration of TCB_Array
1728 TCB_Array
(J
).Common
.LL
.Next
:= TCB_Array
(J
+ 1)'Address;
1731 TCB_Array
(TCB_Array
'Last).Common
.LL
.Next
:= System
.Null_Address
;
1733 -- Initialize the idle task, which is the head of Ready_Queue.
1735 Idle_Task
.Common
.LL
.Magic
:= RT_TASK_MAGIC
;
1736 Idle_Task
.Common
.LL
.State
:= RT_TASK_READY
;
1737 Idle_Task
.Common
.Current_Priority
:= System
.Any_Priority
'First;
1738 Idle_Task
.Common
.LL
.Active_Priority
:= System
.Any_Priority
'First;
1739 Idle_Task
.Common
.LL
.Succ
:= Idle_Task
'Address;
1740 Idle_Task
.Common
.LL
.Pred
:= Idle_Task
'Address;
1742 -- Initialize the regular GNU/Linux kernel task.
1744 Environment_Task
.Common
.LL
.Magic
:= RT_TASK_MAGIC
;
1745 Environment_Task
.Common
.LL
.State
:= RT_TASK_READY
;
1746 Environment_Task
.Common
.Current_Priority
:= System
.Any_Priority
'First;
1747 Environment_Task
.Common
.LL
.Active_Priority
:= System
.Any_Priority
'First;
1748 Environment_Task
.Common
.LL
.Succ
:= To_Address
(Environment_Task
);
1749 Environment_Task
.Common
.LL
.Pred
:= To_Address
(Environment_Task
);
1751 -- Initialize the head of Timer_Queue
1753 Timer_Queue
.Common
.LL
.Succ
:= Timer_Queue
'Address;
1754 Timer_Queue
.Common
.LL
.Pred
:= Timer_Queue
'Address;
1755 Timer_Queue
.Common
.LL
.Resume_Time
:= Max_Sensible_Delay
;
1757 -- Set the current task to regular GNU/Linux kernel task
1759 Current_Task
:= Environment_Task
;
1761 -- Set Timer_Wrapper to be the timer handler
1764 Rt_Request_Timer
(Timer_Wrapper
'Address);
1766 -- Initialize the lock used to synchronize chain of all ATCBs.
1768 Initialize_Lock
(Single_RTS_Lock
'Access, RTS_Lock_Level
);
1770 -- Single_Lock isn't supported in this configuration
1771 pragma Assert
(not Single_Lock
);
1773 Enter_Task
(Environment_Task
);
1776 end System
.Task_Primitives
.Operations
;