summaryrefslogtreecommitdiff
path: root/gcc/ada/5qtaprop.adb
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/ada/5qtaprop.adb')
-rw-r--r--gcc/ada/5qtaprop.adb1777
1 files changed, 1777 insertions, 0 deletions
diff --git a/gcc/ada/5qtaprop.adb b/gcc/ada/5qtaprop.adb
new file mode 100644
index 00000000000..00cfe90c07f
--- /dev/null
+++ b/gcc/ada/5qtaprop.adb
@@ -0,0 +1,1777 @@
+------------------------------------------------------------------------------
+-- --
+-- GNU ADA RUN-TIME LIBRARY (GNARL) COMPONENTS --
+-- --
+-- 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 --
+-- --
+-- B o d y --
+-- --
+-- $Revision: 1.10 $
+-- --
+-- Copyright (C) 1991-2001, Florida State University --
+-- --
+-- GNARL is free software; you can redistribute it and/or modify it under --
+-- terms of the GNU General Public License as published by the Free Soft- --
+-- ware Foundation; either version 2, or (at your option) any later ver- --
+-- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
+-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
+-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
+-- for more details. You should have received a copy of the GNU General --
+-- Public License distributed with GNARL; see file COPYING. If not, write --
+-- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
+-- MA 02111-1307, USA. --
+-- --
+-- As a special exception, if other files instantiate generics from this --
+-- unit, or you link this unit with other files to produce an executable, --
+-- this unit does not by itself cause the resulting executable to be --
+-- covered by the GNU General Public License. This exception does not --
+-- however invalidate any other reasons why the executable file might be --
+-- covered by the GNU Public License. --
+-- --
+-- GNARL was developed by the GNARL team at Florida State University. It is --
+-- now maintained by Ada Core Technologies Inc. in cooperation with Florida --
+-- State University (http://www.gnat.com). --
+-- --
+------------------------------------------------------------------------------
+
+-- RT Linux version
+
+-- ???? Later, look at what we might want to provide for interrupt
+-- management.
+
+pragma Suppress (All_Checks);
+
+pragma Polling (Off);
+-- Turn off polling, we do not want ATC polling to take place during
+-- tasking operations. It causes infinite loops and other problems.
+
+with System.Machine_Code;
+-- used for Asm
+
+with System.OS_Interface;
+-- used for various types, constants, and operations
+
+with System.OS_Primitives;
+-- used for Delay_Modes
+
+with System.Parameters;
+-- used for Size_Type
+
+with System.Storage_Elements;
+
+with System.Tasking;
+-- used for Ada_Task_Control_Block
+-- Task_ID
+
+with Ada.Unchecked_Conversion;
+
+package body System.Task_Primitives.Operations is
+
+ use System.Machine_Code,
+ System.OS_Interface,
+ System.OS_Primitives,
+ System.Parameters,
+ System.Tasking,
+ System.Storage_Elements;
+
+ ----------------------------
+ -- RT Linux specific Data --
+ ----------------------------
+
+ -- Define two important parameters necessary for a Linux kernel module.
+ -- Any module that is going to be loaded into the kernel space needs these
+ -- parameters.
+
+ Mod_Use_Count : Integer;
+ pragma Export (C, Mod_Use_Count, "mod_use_count_");
+ -- for module usage tracking by the kernel
+
+ type Aliased_String is array (Positive range <>) of aliased Character;
+ pragma Convention (C, Aliased_String);
+
+ Kernel_Version : constant Aliased_String := "2.0.33" & ASCII.Nul;
+ pragma Export (C, Kernel_Version, "kernel_version");
+ -- So that insmod can find the version number.
+
+ -- The following procedures have their name specified by the linux module
+ -- loader. Note that they simply correspond to adainit/adafinal.
+
+ function Init_Module return Integer;
+ pragma Export (C, Init_Module, "init_module");
+
+ procedure Cleanup_Module;
+ pragma Export (C, Cleanup_Module, "cleanup_module");
+
+ ----------------
+ -- Local Data --
+ ----------------
+
+ LF : constant String := ASCII.LF & ASCII.Nul;
+
+ LFHT : constant String := ASCII.LF & ASCII.HT;
+ -- used in inserted assembly code
+
+ Max_Tasks : constant := 10;
+ -- ??? Eventually, this should probably be in System.Parameters.
+
+ Known_Tasks : array (0 .. Max_Tasks) of Task_ID;
+ -- Global array of tasks read by gdb, and updated by Create_Task and
+ -- Finalize_TCB. It's from System.Tasking.Debug. We moved it here to
+ -- cut the dependence on that package. Consider moving it here or to
+ -- this package specification, permanently????
+
+ Max_Sensible_Delay : constant RTIME :=
+ 365 * 24 * 60 * 60 * RT_TICKS_PER_SEC;
+ -- Max of one year delay, needed to prevent exceptions for large
+ -- delay values. It seems unlikely that any test will notice this
+ -- restriction.
+ -- ??? This is really declared in System.OS_Primitives,
+ -- and the type is Duration, here its type is RTIME.
+
+ Tick_Count : constant := RT_TICKS_PER_SEC / 20;
+ Nano_Count : constant := 50_000_000;
+ -- two constants used in conversions between RTIME and Duration.
+
+ Addr_Bytes : constant Storage_Offset :=
+ System.Address'Max_Size_In_Storage_Elements;
+ -- number of bytes needed for storing an address.
+
+ Guess : constant RTIME := 10;
+ -- an approximate amount of RTIME used in scheduler to awake a task having
+ -- its resume time within 'current time + Guess'
+ -- The value of 10 is estimated here and may need further refinement
+
+ TCB_Array : array (0 .. Max_Tasks)
+ of aliased Restricted_Ada_Task_Control_Block (Entry_Num => 0);
+ pragma Volatile_Components (TCB_Array);
+
+ Available_TCBs : Task_ID;
+ pragma Atomic (Available_TCBs);
+ -- Head of linear linked list of available TCB's, linked using TCB's
+ -- LL.Next. This list is Initialized to contain a fixed number of tasks,
+ -- when the runtime system starts up.
+
+ Current_Task : Task_ID;
+ pragma Export (C, Current_Task, "current_task");
+ pragma Atomic (Current_Task);
+ -- This is the task currently running. We need the pragma here to specify
+ -- the link-name for Current_Task is "current_task", rather than the long
+ -- name (including the package name) that the Ada compiler would normally
+ -- generate. "current_task" is referenced in procedure Rt_Switch_To below
+
+ Idle_Task : aliased Restricted_Ada_Task_Control_Block (Entry_Num => 0);
+ -- Tail of the circular queue of ready to run tasks.
+
+ Scheduler_Idle : Boolean := False;
+ -- True when the scheduler is idle (no task other than the idle task
+ -- is on the ready queue).
+
+ In_Elab_Code : Boolean := True;
+ -- True when we are elaborating our application.
+ -- Init_Module will set this flag to false and never revert it.
+
+ Timer_Queue : aliased Restricted_Ada_Task_Control_Block (Entry_Num => 0);
+ -- Header of the queue of delayed real-time tasks.
+ -- Timer_Queue.LL has to be initialized properly before being used
+
+ Timer_Expired : Boolean := False;
+ -- flag to show whether the Timer_Queue needs to be checked
+ -- when it becomes true, it means there is a task in the
+ -- Timer_Queue having to be awakened and be moved to ready queue
+
+ Environment_Task_ID : Task_ID;
+ -- A variable to hold Task_ID for the environment task.
+ -- Once initialized, this behaves as a constant.
+ -- In the current implementation, this is the task assigned permanently
+ -- as the regular Linux kernel.
+
+ All_Tasks_L : aliased RTS_Lock;
+ -- See comments on locking rules in System.Tasking (spec).
+
+ -- The followings are internal configuration constants needed.
+ Next_Serial_Number : Task_Serial_Number := 100;
+ pragma Volatile (Next_Serial_Number);
+ -- We start at 100, to reserve some special values for
+ -- using in error checking.
+
+ Linux_Irq_State : Integer := 0;
+
+ type Duration_As_Integer is delta 1.0
+ range -2.0**(Duration'Size - 1) .. 2.0**(Duration'Size - 1) - 1.0;
+ -- used for output RTIME value during debugging
+
+ type Address_Ptr is access all System.Address;
+ pragma Convention (C, Address_Ptr);
+
+ --------------------------------
+ -- Local conversion functions --
+ --------------------------------
+
+ function To_Task_ID is new
+ Ada.Unchecked_Conversion (System.Address, Task_ID);
+
+ function To_Address is new
+ Ada.Unchecked_Conversion (Task_ID, System.Address);
+
+ function RTIME_To_D_Int is new
+ Ada.Unchecked_Conversion (RTIME, Duration_As_Integer);
+
+ function Raw_RTIME is new
+ Ada.Unchecked_Conversion (Duration, RTIME);
+
+ function Raw_Duration is new
+ Ada.Unchecked_Conversion (RTIME, Duration);
+
+ function To_Duration (T : RTIME) return Duration;
+ pragma Inline (To_Duration);
+
+ function To_RTIME (D : Duration) return RTIME;
+ pragma Inline (To_RTIME);
+
+ function To_Integer is new
+ Ada.Unchecked_Conversion (System.Parameters.Size_Type, Integer);
+
+ function To_Address_Ptr is
+ new Ada.Unchecked_Conversion (System.Address, Address_Ptr);
+
+ function To_RTS_Lock_Ptr is new
+ Ada.Unchecked_Conversion (Lock_Ptr, RTS_Lock_Ptr);
+
+ -----------------------------------
+ -- Local Subprogram Declarations --
+ -----------------------------------
+
+ procedure Rt_Switch_To (Tsk : Task_ID);
+ pragma Inline (Rt_Switch_To);
+ -- switch from the 'current_task' to 'Tsk'
+ -- and 'Tsk' then becomes 'current_task'
+
+ procedure R_Save_Flags (F : out Integer);
+ pragma Inline (R_Save_Flags);
+ -- save EFLAGS register to 'F'
+
+ procedure R_Restore_Flags (F : Integer);
+ pragma Inline (R_Restore_Flags);
+ -- restore EFLAGS register from 'F'
+
+ procedure R_Cli;
+ pragma Inline (R_Cli);
+ -- disable interrupts
+
+ procedure R_Sti;
+ pragma Inline (R_Sti);
+ -- enable interrupts
+
+ procedure Timer_Wrapper;
+ -- the timer handler. It sets Timer_Expired flag to True and
+ -- then calls Rt_Schedule
+
+ procedure Rt_Schedule;
+ -- the scheduler
+
+ procedure Insert_R (T : Task_ID);
+ pragma Inline (Insert_R);
+ -- insert 'T' into the tail of the ready queue for its active
+ -- priority
+ -- if original queue is 6 5 4 4 3 2 and T has priority of 4
+ -- then after T is inserted the queue becomes 6 5 4 4 T 3 2
+
+ procedure Insert_RF (T : Task_ID);
+ pragma Inline (Insert_RF);
+ -- insert 'T' into the front of the ready queue for its active
+ -- priority
+ -- if original queue is 6 5 4 4 3 2 and T has priority of 4
+ -- then after T is inserted the queue becomes 6 5 T 4 4 3 2
+
+ procedure Delete_R (T : Task_ID);
+ pragma Inline (Delete_R);
+ -- delete 'T' from the ready queue. If 'T' is not in any queue
+ -- the operation has no effect
+
+ procedure Insert_T (T : Task_ID);
+ pragma Inline (Insert_T);
+ -- insert 'T' into the waiting queue according to its Resume_Time.
+ -- If there are tasks in the waiting queue that have the same
+ -- Resume_Time as 'T', 'T' is then inserted into the queue for
+ -- its active priority
+
+ procedure Delete_T (T : Task_ID);
+ pragma Inline (Delete_T);
+ -- delete 'T' from the waiting queue.
+
+ procedure Move_Top_Task_From_Timer_Queue_To_Ready_Queue;
+ pragma Inline (Move_Top_Task_From_Timer_Queue_To_Ready_Queue);
+ -- remove the task in the front of the waiting queue and insert it
+ -- into the tail of the ready queue for its active priority
+
+ -------------------------
+ -- Local Subprograms --
+ -------------------------
+
+ procedure Rt_Switch_To (Tsk : Task_ID) is
+ begin
+ pragma Debug (Printk ("procedure Rt_Switch_To called" & LF));
+
+ Asm (
+ "pushl %%eax" & LFHT &
+ "pushl %%ebp" & LFHT &
+ "pushl %%edi" & LFHT &
+ "pushl %%esi" & LFHT &
+ "pushl %%edx" & LFHT &
+ "pushl %%ecx" & LFHT &
+ "pushl %%ebx" & LFHT &
+
+ "movl current_task, %%edx" & LFHT &
+ "cmpl $0, 36(%%edx)" & LFHT &
+ -- 36 is hard-coded, 36(%%edx) is actually
+ -- Current_Task.Common.LL.Uses_Fp
+
+ "jz 25f" & LFHT &
+ "sub $108,%%esp" & LFHT &
+ "fsave (%%esp)" & LFHT &
+ "25: pushl $1f" & LFHT &
+ "movl %%esp, 32(%%edx)" & LFHT &
+ -- 32 is hard-coded, 32(%%edx) is actually
+ -- Current_Task.Common.LL.Stack
+
+ "movl 32(%%ecx), %%esp" & LFHT &
+ -- 32 is hard-coded, 32(%%ecx) is actually Tsk.Common.LL.Stack.
+ -- Tsk is the task to be switched to
+
+ "movl %%ecx, current_task" & LFHT &
+ "ret" & LFHT &
+ "1: cmpl $0, 36(%%ecx)" & LFHT &
+ -- 36(%%exc) is Tsk.Common.LL.Stack (hard coded)
+ "jz 26f" & LFHT &
+ "frstor (%%esp)" & LFHT &
+ "add $108,%%esp" & LFHT &
+ "26: popl %%ebx" & LFHT &
+ "popl %%ecx" & LFHT &
+ "popl %%edx" & LFHT &
+ "popl %%esi" & LFHT &
+ "popl %%edi" & LFHT &
+ "popl %%ebp" & LFHT &
+ "popl %%eax",
+ Outputs => No_Output_Operands,
+ Inputs => Task_ID'Asm_Input ("c", Tsk),
+ Clobber => "cx",
+ Volatile => True);
+ end Rt_Switch_To;
+
+ procedure R_Save_Flags (F : out Integer) is
+ begin
+ Asm (
+ "pushfl" & LFHT &
+ "popl %0",
+ Outputs => Integer'Asm_Output ("=g", F),
+ Inputs => No_Input_Operands,
+ Clobber => "memory",
+ Volatile => True);
+ end R_Save_Flags;
+
+ procedure R_Restore_Flags (F : Integer) is
+ begin
+ Asm (
+ "pushl %0" & LFHT &
+ "popfl",
+ Outputs => No_Output_Operands,
+ Inputs => Integer'Asm_Input ("g", F),
+ Clobber => "memory",
+ Volatile => True);
+ end R_Restore_Flags;
+
+ procedure R_Sti is
+ begin
+ Asm (
+ "sti",
+ Outputs => No_Output_Operands,
+ Inputs => No_Input_Operands,
+ Clobber => "memory",
+ Volatile => True);
+ end R_Sti;
+
+ procedure R_Cli is
+ begin
+ Asm (
+ "cli",
+ Outputs => No_Output_Operands,
+ Inputs => No_Input_Operands,
+ Clobber => "memory",
+ Volatile => True);
+ end R_Cli;
+
+ -- A wrapper for Rt_Schedule, works as the timer handler
+
+ procedure Timer_Wrapper is
+ begin
+ pragma Debug (Printk ("procedure Timer_Wrapper called" & LF));
+
+ Timer_Expired := True;
+ Rt_Schedule;
+ end Timer_Wrapper;
+
+ procedure Rt_Schedule is
+ Now : RTIME;
+ Top_Task : Task_ID;
+ Flags : Integer;
+
+ procedure Debug_Timer_Queue;
+ -- Check the state of the Timer Queue.
+
+ procedure Debug_Timer_Queue is
+ begin
+ if Timer_Queue.Common.LL.Succ /= Timer_Queue'Address then
+ Printk ("Timer_Queue not empty" & LF);
+ end if;
+
+ if To_Task_ID (Timer_Queue.Common.LL.Succ).Common.LL.Resume_Time <
+ Now + Guess
+ then
+ Printk ("and need to move top task to ready queue" & LF);
+ end if;
+ end Debug_Timer_Queue;
+
+ begin
+ pragma Debug (Printk ("procedure Rt_Schedule called" & LF));
+
+ -- Scheduler_Idle means that this call comes from an interrupt
+ -- handler (e.g timer) that interrupted the idle loop below.
+
+ if Scheduler_Idle then
+ return;
+ end if;
+
+ <<Idle>>
+ R_Save_Flags (Flags);
+ R_Cli;
+
+ Scheduler_Idle := False;
+
+ if Timer_Expired then
+ pragma Debug (Printk ("Timer expired" & LF));
+ Timer_Expired := False;
+
+ -- Check for expired time delays.
+ Now := Rt_Get_Time;
+
+ -- Need another (circular) queue for delayed tasks, this one ordered
+ -- by wakeup time, so the one at the front has the earliest resume
+ -- time. Wake up all the tasks sleeping on time delays that should
+ -- be awakened at this time.
+
+ -- ??? This is not very good, since we may waste time here waking
+ -- up a bunch of lower priority tasks, adding to the blocking time
+ -- of higher priority ready tasks, but we don't see how to get
+ -- around this without adding more wasted time elsewhere.
+
+ pragma Debug (Debug_Timer_Queue);
+
+ while Timer_Queue.Common.LL.Succ /= Timer_Queue'Address and then
+ To_Task_ID
+ (Timer_Queue.Common.LL.Succ).Common.LL.Resume_Time < Now + Guess
+ loop
+ To_Task_ID (Timer_Queue.Common.LL.Succ).Common.LL.State :=
+ RT_TASK_READY;
+ Move_Top_Task_From_Timer_Queue_To_Ready_Queue;
+ end loop;
+
+ -- Arm the timer if necessary.
+ -- ??? This may be wasteful, if the tasks on the timer queue are
+ -- of lower priority than the current task's priority. The problem
+ -- is that we can't tell this without scanning the whole timer
+ -- queue. This scanning takes extra time.
+
+ if Timer_Queue.Common.LL.Succ /= Timer_Queue'Address then
+ -- Timer_Queue is not empty, so set the timer to interrupt at
+ -- the next resume time. The Wakeup procedure must also do this,
+ -- and must do it while interrupts are disabled so that there is
+ -- no danger of interleaving with this code.
+ Rt_Set_Timer
+ (To_Task_ID (Timer_Queue.Common.LL.Succ).Common.LL.Resume_Time);
+ else
+ Rt_No_Timer;
+ end if;
+ end if;
+
+ Top_Task := To_Task_ID (Idle_Task.Common.LL.Succ);
+
+ -- If the ready queue is empty, the kernel has to wait until the timer
+ -- or another interrupt makes a task ready.
+
+ if Top_Task = To_Task_ID (Idle_Task'Address) then
+ Scheduler_Idle := True;
+ R_Restore_Flags (Flags);
+ pragma Debug (Printk ("!!!kernel idle!!!" & LF));
+ goto Idle;
+ end if;
+
+ if Top_Task = Current_Task then
+ pragma Debug (Printk ("Rt_Schedule: Top_Task = Current_Task" & LF));
+ -- if current task continues, just return.
+
+ R_Restore_Flags (Flags);
+ return;
+ end if;
+
+ if Top_Task = Environment_Task_ID then
+ pragma Debug (Printk
+ ("Rt_Schedule: Top_Task = Environment_Task" & LF));
+ -- If there are no RT tasks ready, we execute the regular
+ -- Linux kernel, and allow the regular Linux interrupt
+ -- handlers to preempt the current task again.
+
+ if not In_Elab_Code then
+ SFIF := Linux_Irq_State;
+ end if;
+
+ elsif Current_Task = Environment_Task_ID then
+ pragma Debug (Printk
+ ("Rt_Schedule: Current_Task = Environment_Task" & LF));
+ -- We are going to preempt the regular Linux kernel to
+ -- execute an RT task, so don't allow the regular Linux
+ -- interrupt handlers to preempt the current task any more.
+
+ Linux_Irq_State := SFIF;
+ SFIF := 0;
+ end if;
+
+ Top_Task.Common.LL.State := RT_TASK_READY;
+ Rt_Switch_To (Top_Task);
+ R_Restore_Flags (Flags);
+ end Rt_Schedule;
+
+ procedure Insert_R (T : Task_ID) is
+ Q : Task_ID := To_Task_ID (Idle_Task.Common.LL.Succ);
+ begin
+ pragma Debug (Printk ("procedure Insert_R called" & LF));
+
+ pragma Assert (T.Common.LL.Succ = To_Address (T));
+ pragma Assert (T.Common.LL.Pred = To_Address (T));
+
+ -- T is inserted in the queue between a task that has higher
+ -- or the same Active_Priority as T and a task that has lower
+ -- Active_Priority than T
+
+ while Q /= To_Task_ID (Idle_Task'Address)
+ and then T.Common.LL.Active_Priority <= Q.Common.LL.Active_Priority
+ loop
+ Q := To_Task_ID (Q.Common.LL.Succ);
+ end loop;
+
+ -- Q is successor of T
+
+ T.Common.LL.Succ := To_Address (Q);
+ T.Common.LL.Pred := Q.Common.LL.Pred;
+ To_Task_ID (T.Common.LL.Pred).Common.LL.Succ := To_Address (T);
+ Q.Common.LL.Pred := To_Address (T);
+ end Insert_R;
+
+ procedure Insert_RF (T : Task_ID) is
+ Q : Task_ID := To_Task_ID (Idle_Task.Common.LL.Succ);
+ begin
+ pragma Debug (Printk ("procedure Insert_RF called" & LF));
+
+ pragma Assert (T.Common.LL.Succ = To_Address (T));
+ pragma Assert (T.Common.LL.Pred = To_Address (T));
+
+ -- T is inserted in the queue between a task that has higher
+ -- Active_Priority as T and a task that has lower or the same
+ -- Active_Priority as T
+
+ while Q /= To_Task_ID (Idle_Task'Address) and then
+ T.Common.LL.Active_Priority < Q.Common.LL.Active_Priority
+ loop
+ Q := To_Task_ID (Q.Common.LL.Succ);
+ end loop;
+
+ -- Q is successor of T
+
+ T.Common.LL.Succ := To_Address (Q);
+ T.Common.LL.Pred := Q.Common.LL.Pred;
+ To_Task_ID (T.Common.LL.Pred).Common.LL.Succ := To_Address (T);
+ Q.Common.LL.Pred := To_Address (T);
+ end Insert_RF;
+
+ procedure Delete_R (T : Task_ID) is
+ Tpred : constant Task_ID := To_Task_ID (T.Common.LL.Pred);
+ Tsucc : constant Task_ID := To_Task_ID (T.Common.LL.Succ);
+
+ begin
+ pragma Debug (Printk ("procedure Delete_R called" & LF));
+
+ -- checking whether T is in the queue is not necessary because
+ -- if T is not in the queue, following statements changes
+ -- nothing. But T cannot be in the Timer_Queue, otherwise
+ -- activate the check below, note that checking whether T is
+ -- in a queue is a relatively expensive operation
+
+ Tpred.Common.LL.Succ := To_Address (Tsucc);
+ Tsucc.Common.LL.Pred := To_Address (Tpred);
+ T.Common.LL.Succ := To_Address (T);
+ T.Common.LL.Pred := To_Address (T);
+ end Delete_R;
+
+ procedure Insert_T (T : Task_ID) is
+ Q : Task_ID := To_Task_ID (Timer_Queue.Common.LL.Succ);
+ begin
+ pragma Debug (Printk ("procedure Insert_T called" & LF));
+
+ pragma Assert (T.Common.LL.Succ = To_Address (T));
+
+ while Q /= To_Task_ID (Timer_Queue'Address) and then
+ T.Common.LL.Resume_Time > Q.Common.LL.Resume_Time
+ loop
+ Q := To_Task_ID (Q.Common.LL.Succ);
+ end loop;
+
+ -- Q is the task that has Resume_Time equal to or greater than that
+ -- of T. If they have the same Resume_Time, continue looking for the
+ -- location T is to be inserted using its Active_Priority
+
+ while Q /= To_Task_ID (Timer_Queue'Address) and then
+ T.Common.LL.Resume_Time = Q.Common.LL.Resume_Time
+ loop
+ exit when T.Common.LL.Active_Priority > Q.Common.LL.Active_Priority;
+ Q := To_Task_ID (Q.Common.LL.Succ);
+ end loop;
+
+ -- Q is successor of T
+
+ T.Common.LL.Succ := To_Address (Q);
+ T.Common.LL.Pred := Q.Common.LL.Pred;
+ To_Task_ID (T.Common.LL.Pred).Common.LL.Succ := To_Address (T);
+ Q.Common.LL.Pred := To_Address (T);
+ end Insert_T;
+
+ procedure Delete_T (T : Task_ID) is
+ Tpred : constant Task_ID := To_Task_ID (T.Common.LL.Pred);
+ Tsucc : constant Task_ID := To_Task_ID (T.Common.LL.Succ);
+
+ begin
+ pragma Debug (Printk ("procedure Delete_T called" & LF));
+
+ pragma Assert (T /= To_Task_ID (Timer_Queue'Address));
+
+ Tpred.Common.LL.Succ := To_Address (Tsucc);
+ Tsucc.Common.LL.Pred := To_Address (Tpred);
+ T.Common.LL.Succ := To_Address (T);
+ T.Common.LL.Pred := To_Address (T);
+ end Delete_T;
+
+ procedure Move_Top_Task_From_Timer_Queue_To_Ready_Queue is
+ Top_Task : Task_ID := To_Task_ID (Timer_Queue.Common.LL.Succ);
+ begin
+ pragma Debug (Printk ("procedure Move_Top_Task called" & LF));
+
+ if Top_Task /= To_Task_ID (Timer_Queue'Address) then
+ Delete_T (Top_Task);
+ Top_Task.Common.LL.State := RT_TASK_READY;
+ Insert_R (Top_Task);
+ end if;
+ end Move_Top_Task_From_Timer_Queue_To_Ready_Queue;
+
+ ----------
+ -- Self --
+ ----------
+
+ function Self return Task_ID is
+ begin
+ pragma Debug (Printk ("function Self called" & LF));
+
+ return Current_Task;
+ end Self;
+
+ ---------------------
+ -- Initialize_Lock --
+ ---------------------
+
+ procedure Initialize_Lock (Prio : System.Any_Priority; L : access Lock) is
+ begin
+ pragma Debug (Printk ("procedure Initialize_Lock called" & LF));
+
+ L.Ceiling_Priority := Prio;
+ L.Owner := System.Null_Address;
+ end Initialize_Lock;
+
+ procedure Initialize_Lock (L : access RTS_Lock; Level : Lock_Level) is
+ begin
+ pragma Debug (Printk ("procedure Initialize_Lock (RTS) called" & LF));
+
+ L.Ceiling_Priority := System.Any_Priority'Last;
+ L.Owner := System.Null_Address;
+ end Initialize_Lock;
+
+ -------------------
+ -- Finalize_Lock --
+ -------------------
+
+ procedure Finalize_Lock (L : access Lock) is
+ begin
+ pragma Debug (Printk ("procedure Finalize_Lock called" & LF));
+ null;
+ end Finalize_Lock;
+
+ procedure Finalize_Lock (L : access RTS_Lock) is
+ begin
+ pragma Debug (Printk ("procedure Finalize_Lock (RTS) called" & LF));
+ null;
+ end Finalize_Lock;
+
+ ----------------
+ -- Write_Lock --
+ ----------------
+
+ procedure Write_Lock
+ (L : access Lock;
+ Ceiling_Violation : out Boolean)
+ is
+ Prio : constant System.Any_Priority :=
+ Current_Task.Common.LL.Active_Priority;
+ begin
+ pragma Debug (Printk ("procedure Write_Lock called" & LF));
+
+ Ceiling_Violation := False;
+
+ if Prio > L.Ceiling_Priority then
+ -- Ceiling violation.
+ -- This should never happen, unless something is seriously
+ -- wrong with task T or the entire run-time system.
+ -- ???? extreme error recovery, e.g. shut down the system or task
+
+ Ceiling_Violation := True;
+ pragma Debug (Printk ("Ceiling Violation in Write_Lock" & LF));
+ return;
+ end if;
+
+ L.Pre_Locking_Priority := Prio;
+ L.Owner := To_Address (Current_Task);
+ Current_Task.Common.LL.Active_Priority := L.Ceiling_Priority;
+
+ if Current_Task.Common.LL.Outer_Lock = null then
+ -- If this lock is not nested, record a pointer to it.
+
+ Current_Task.Common.LL.Outer_Lock :=
+ To_RTS_Lock_Ptr (L.all'Unchecked_Access);
+ end if;
+ end Write_Lock;
+
+ procedure Write_Lock (L : access RTS_Lock) is
+ Prio : constant System.Any_Priority :=
+ Current_Task.Common.LL.Active_Priority;
+
+ begin
+ pragma Debug (Printk ("procedure Write_Lock (RTS) called" & LF));
+
+ if Prio > L.Ceiling_Priority then
+ -- Ceiling violation.
+ -- This should never happen, unless something is seriously
+ -- wrong with task T or the entire runtime system.
+ -- ???? extreme error recovery, e.g. shut down the system or task
+
+ Printk ("Ceiling Violation in Write_Lock (RTS)" & LF);
+ return;
+ end if;
+
+ L.Pre_Locking_Priority := Prio;
+ L.Owner := To_Address (Current_Task);
+ Current_Task.Common.LL.Active_Priority := L.Ceiling_Priority;
+
+ if Current_Task.Common.LL.Outer_Lock = null then
+ Current_Task.Common.LL.Outer_Lock := L.all'Unchecked_Access;
+ end if;
+ end Write_Lock;
+
+ procedure Write_Lock (T : Task_ID) is
+ Prio : constant System.Any_Priority :=
+ Current_Task.Common.LL.Active_Priority;
+
+ begin
+ pragma Debug (Printk ("procedure Write_Lock (Task_ID) called" & LF));
+
+ if Prio > T.Common.LL.L.Ceiling_Priority then
+ -- Ceiling violation.
+ -- This should never happen, unless something is seriously
+ -- wrong with task T or the entire runtime system.
+ -- ???? extreme error recovery, e.g. shut down the system or task
+
+ Printk ("Ceiling Violation in Write_Lock (Task)" & LF);
+ return;
+ end if;
+
+ T.Common.LL.L.Pre_Locking_Priority := Prio;
+ T.Common.LL.L.Owner := To_Address (Current_Task);
+ Current_Task.Common.LL.Active_Priority := T.Common.LL.L.Ceiling_Priority;
+
+ if Current_Task.Common.LL.Outer_Lock = null then
+ Current_Task.Common.LL.Outer_Lock := T.Common.LL.L'Access;
+ end if;
+ end Write_Lock;
+
+ ---------------
+ -- Read_Lock --
+ ---------------
+
+ procedure Read_Lock (L : access Lock; Ceiling_Violation : out Boolean) is
+ begin
+ pragma Debug (Printk ("procedure Read_Lock called" & LF));
+ Write_Lock (L, Ceiling_Violation);
+ end Read_Lock;
+
+ ------------
+ -- Unlock --
+ ------------
+
+ procedure Unlock (L : access Lock) is
+ Flags : Integer;
+ begin
+ pragma Debug (Printk ("procedure Unlock called" & LF));
+
+ if L.Owner /= To_Address (Current_Task) then
+ -- ...error recovery
+
+ null;
+ Printk ("The caller is not the owner of the lock" & LF);
+ return;
+ end if;
+
+ L.Owner := System.Null_Address;
+
+ -- Now that the lock is released, lower own priority,
+
+ if Current_Task.Common.LL.Outer_Lock =
+ To_RTS_Lock_Ptr (L.all'Unchecked_Access)
+ then
+ -- This lock is the outer-most one, reset own priority to
+ -- Current_Priority;
+
+ Current_Task.Common.LL.Active_Priority :=
+ Current_Task.Common.Current_Priority;
+ Current_Task.Common.LL.Outer_Lock := null;
+
+ else
+ -- If this lock is nested, pop the old active priority.
+
+ Current_Task.Common.LL.Active_Priority := L.Pre_Locking_Priority;
+ end if;
+
+ -- Reschedule the task if necessary. Note we only need to reschedule
+ -- the task if its Active_Priority becomes less than the one following
+ -- it. The check depends on the fact that Environment_Task (tail of
+ -- the ready queue) has the lowest Active_Priority
+
+ if Current_Task.Common.LL.Active_Priority
+ < To_Task_ID (Current_Task.Common.LL.Succ).Common.LL.Active_Priority
+ then
+ R_Save_Flags (Flags);
+ R_Cli;
+ Delete_R (Current_Task);
+ Insert_RF (Current_Task);
+ R_Restore_Flags (Flags);
+ Rt_Schedule;
+ end if;
+ end Unlock;
+
+ procedure Unlock (L : access RTS_Lock) is
+ Flags : Integer;
+ begin
+ pragma Debug (Printk ("procedure Unlock (RTS_Lock) called" & LF));
+
+ if L.Owner /= To_Address (Current_Task) then
+ null;
+ Printk ("The caller is not the owner of the lock" & LF);
+ return;
+ end if;
+
+ L.Owner := System.Null_Address;
+
+ if Current_Task.Common.LL.Outer_Lock = L.all'Unchecked_Access then
+ Current_Task.Common.LL.Active_Priority :=
+ Current_Task.Common.Current_Priority;
+ Current_Task.Common.LL.Outer_Lock := null;
+
+ else
+ Current_Task.Common.LL.Active_Priority := L.Pre_Locking_Priority;
+ end if;
+
+ -- Reschedule the task if necessary
+
+ if Current_Task.Common.LL.Active_Priority
+ < To_Task_ID (Current_Task.Common.LL.Succ).Common.LL.Active_Priority
+ then
+ R_Save_Flags (Flags);
+ R_Cli;
+ Delete_R (Current_Task);
+ Insert_RF (Current_Task);
+ R_Restore_Flags (Flags);
+ Rt_Schedule;
+ end if;
+ end Unlock;
+
+ procedure Unlock (T : Task_ID) is
+ begin
+ pragma Debug (Printk ("procedure Unlock (Task_ID) called" & LF));
+ Unlock (T.Common.LL.L'Access);
+ end Unlock;
+
+ -----------
+ -- Sleep --
+ -----------
+
+ -- Unlock Self_ID.Common.LL.L and suspend Self_ID, atomically.
+ -- Before return, lock Self_ID.Common.LL.L again
+ -- Self_ID can only be reactivated by calling Wakeup.
+ -- Unlock code is repeated intentionally.
+
+ procedure Sleep
+ (Self_ID : Task_ID;
+ Reason : ST.Task_States)
+ is
+ Flags : Integer;
+ begin
+ pragma Debug (Printk ("procedure Sleep called" & LF));
+
+ -- Note that Self_ID is actually Current_Task, that is, only the
+ -- task that is running can put itself into sleep. To preserve
+ -- consistency, we use Self_ID throughout the code here
+
+ Self_ID.Common.State := Reason;
+ Self_ID.Common.LL.State := RT_TASK_DORMANT;
+
+ R_Save_Flags (Flags);
+ R_Cli;
+
+ Delete_R (Self_ID);
+
+ -- Arrange to unlock Self_ID's ATCB lock. The following check
+ -- may be unnecessary because the specification of Sleep says
+ -- the caller shoud hold its own ATCB lock before calling Sleep
+
+ if Self_ID.Common.LL.L.Owner = To_Address (Self_ID) then
+ Self_ID.Common.LL.L.Owner := System.Null_Address;
+
+ if Self_ID.Common.LL.Outer_Lock = Self_ID.Common.LL.L'Access then
+ Self_ID.Common.LL.Active_Priority :=
+ Self_ID.Common.Current_Priority;
+ Self_ID.Common.LL.Outer_Lock := null;
+
+ else
+ Self_ID.Common.LL.Active_Priority :=
+ Self_ID.Common.LL.L.Pre_Locking_Priority;
+ end if;
+ end if;
+
+ R_Restore_Flags (Flags);
+ Rt_Schedule;
+
+ -- Before leave, regain the lock
+
+ Write_Lock (Self_ID);
+ end Sleep;
+
+ -----------------
+ -- Timed_Sleep --
+ -----------------
+
+ -- Arrange to be awakened after/at Time (depending on Mode) then Unlock
+ -- Self_ID.Common.LL.L and suspend self. If the timeout expires first,
+ -- that should awaken the task. If it's awakened (by some other task
+ -- calling Wakeup) before the timeout expires, the timeout should be
+ -- cancelled.
+
+ -- This is for use within the run-time system, so abort is
+ -- assumed to be already deferred, and the caller should be
+ -- holding its own ATCB lock.
+
+ procedure Timed_Sleep
+ (Self_ID : Task_ID;
+ Time : Duration;
+ Mode : ST.Delay_Modes;
+ Reason : Task_States;
+ Timedout : out Boolean;
+ Yielded : out Boolean)
+ is
+ Flags : Integer;
+ Abs_Time : RTIME;
+
+ begin
+ pragma Debug (Printk ("procedure Timed_Sleep called" & LF));
+
+ Timedout := True;
+ Yielded := False;
+ -- ??? These two boolean seems not relevant here
+
+ if Mode = Relative then
+ Abs_Time := To_RTIME (Time) + Rt_Get_Time;
+ else
+ Abs_Time := To_RTIME (Time);
+ end if;
+
+ Self_ID.Common.LL.Resume_Time := Abs_Time;
+ Self_ID.Common.LL.State := RT_TASK_DELAYED;
+
+ R_Save_Flags (Flags);
+ R_Cli;
+ Delete_R (Self_ID);
+ Insert_T (Self_ID);
+
+ -- Check if the timer needs to be set
+
+ if Timer_Queue.Common.LL.Succ = To_Address (Self_ID) then
+ Rt_Set_Timer (Abs_Time);
+ end if;
+
+ -- Another way to do it
+ --
+ -- if Abs_Time <
+ -- To_Task_ID (Timer_Queue.Common.LL.Succ).Common.LL.Resume_Time
+ -- then
+ -- Rt_Set_Timer (Abs_Time);
+ -- end if;
+
+ -- Arrange to unlock Self_ID's ATCB lock. see comments in Sleep
+
+ if Self_ID.Common.LL.L.Owner = To_Address (Self_ID) then
+ Self_ID.Common.LL.L.Owner := System.Null_Address;
+
+ if Self_ID.Common.LL.Outer_Lock = Self_ID.Common.LL.L'Access then
+ Self_ID.Common.LL.Active_Priority :=
+ Self_ID.Common.Current_Priority;
+ Self_ID.Common.LL.Outer_Lock := null;
+
+ else
+ Self_ID.Common.LL.Active_Priority :=
+ Self_ID.Common.LL.L.Pre_Locking_Priority;
+ end if;
+ end if;
+
+ R_Restore_Flags (Flags);
+ Rt_Schedule;
+
+ -- Before leaving, regain the lock
+
+ Write_Lock (Self_ID);
+ end Timed_Sleep;
+
+ -----------------
+ -- Timed_Delay --
+ -----------------
+
+ -- This is for use in implementing delay statements, so we assume
+ -- the caller is not abort-deferred and is holding no locks.
+ -- Self_ID can only be awakened after the timeout, no Wakeup on it.
+
+ procedure Timed_Delay
+ (Self_ID : Task_ID;
+ Time : Duration;
+ Mode : ST.Delay_Modes)
+ is
+ Flags : Integer;
+ Abs_Time : RTIME;
+
+ begin
+ pragma Debug (Printk ("procedure Timed_Delay called" & LF));
+
+ -- Only the little window between deferring abort and
+ -- locking Self_ID is the reason we need to
+ -- check for pending abort and priority change below! :(
+
+ Write_Lock (Self_ID);
+
+ -- Take the lock in case its ATCB needs to be modified
+
+ if Mode = Relative then
+ Abs_Time := To_RTIME (Time) + Rt_Get_Time;
+ else
+ Abs_Time := To_RTIME (Time);
+ end if;
+
+ Self_ID.Common.LL.Resume_Time := Abs_Time;
+ Self_ID.Common.LL.State := RT_TASK_DELAYED;
+
+ R_Save_Flags (Flags);
+ R_Cli;
+ Delete_R (Self_ID);
+ Insert_T (Self_ID);
+
+ -- Check if the timer needs to be set
+
+ if Timer_Queue.Common.LL.Succ = To_Address (Self_ID) then
+ Rt_Set_Timer (Abs_Time);
+ end if;
+
+ -- Arrange to unlock Self_ID's ATCB lock.
+ -- Note that the code below is slightly different from Unlock, so
+ -- it is more than inline it.
+
+ if To_Task_ID (Self_ID.Common.LL.L.Owner) = Self_ID then
+ Self_ID.Common.LL.L.Owner := System.Null_Address;
+
+ if Self_ID.Common.LL.Outer_Lock = Self_ID.Common.LL.L'Access then
+ Self_ID.Common.LL.Active_Priority :=
+ Self_ID.Common.Current_Priority;
+ Self_ID.Common.LL.Outer_Lock := null;
+
+ else
+ Self_ID.Common.LL.Active_Priority :=
+ Self_ID.Common.LL.L.Pre_Locking_Priority;
+ end if;
+ end if;
+
+ R_Restore_Flags (Flags);
+ Rt_Schedule;
+ end Timed_Delay;
+
+ ---------------------
+ -- Monotonic_Clock --
+ ---------------------
+
+ -- RTIME is represented as a 64-bit signed count of ticks,
+ -- where there are 1_193_180 ticks per second.
+
+ -- Let T be a count of ticks and N the corresponding count of nanoseconds.
+ -- From the following relationship
+ -- T / (ticks_per_second) = N / (ns_per_second)
+ -- where ns_per_second is 1_000_000_000 (number of nanoseconds in
+ -- a second), we get
+ -- T * (ns_per_second) = N * (ticks_per_second)
+ -- or
+ -- T * 1_000_000_000 = N * 1_193_180
+ -- which can be reduced to
+ -- T * 50_000_000 = N * 59_659
+ -- Let Nano_Count = 50_000_000 and Tick_Count = 59_659, we then have
+ -- T * Nano_Count = N * Tick_Count
+
+ -- IMPORTANT FACT:
+ -- These numbers are small enough that we can do arithmetic
+ -- on them without overflowing 64 bits. To see this, observe
+
+ -- 10**3 = 1000 < 1024 = 2**10
+ -- Tick_Count < 60 * 1000 < 64 * 1024 < 2**16
+ -- Nano_Count < 50 * 1000 * 1000 < 64 * 1024 * 1024 < 2**26
+
+ -- It follows that if 0 <= R < Tick_Count, we can compute
+ -- R * Nano_Count < 2**42 without overflow in 64 bits.
+ -- Similarly, if 0 <= R < Nano_Count, we can compute
+ -- R * Tick_Count < 2**42 without overflow in 64 bits.
+
+ -- GNAT represents Duration as a count of nanoseconds internally.
+
+ -- To convert T from RTIME to Duration, let
+ -- Q = T / Tick_Count, with truncation
+ -- R = T - Q * Tick_Count, the remainder 0 <= R < Tick_Count
+ -- so
+ -- N * Tick_Count
+ -- = T * Nano_Count - Q * Tick_Count * Nano_Count
+ -- + Q * Tick_Count * Nano_Count
+ -- = (T - Q * Tick_Count) * Nano_Count
+ -- + (Q * Nano_Count) * Tick_Count
+ -- = R * Nano_Count + (Q * Nano_Count) * Tick_Count
+
+ -- Now, let
+ -- Q1 = R * Nano_Count / Tick_Count, with truncation
+ -- R1 = R * Nano_Count - Q1 * Tick_Count, 0 <= R1 <Tick_Count
+ -- R * Nano_Count = Q1 * Tick_Count + R1
+ -- so
+ -- N * Tick_Count
+ -- = R * Nano_Count + (Q * Nano_Count) * Tick_Count
+ -- = Q1 * Tick_Count + R1 + (Q * Nano_Count) * Tick_Count
+ -- = R1 + (Q * Nano_Count + Q1) * Tick_Count
+ -- and
+ -- N = Q * Nano_Count + Q1 + R1 /Tick_Count,
+ -- where 0 <= R1 /Tick_Count < 1
+
+ function To_Duration (T : RTIME) return Duration is
+ Q, Q1, RN : RTIME;
+ begin
+ Q := T / Tick_Count;
+ RN := (T - Q * Tick_Count) * Nano_Count;
+ Q1 := RN / Tick_Count;
+ return Raw_Duration (Q * Nano_Count + Q1);
+ end To_Duration;
+
+ -- To convert D from Duration to RTIME,
+ -- Let D be a Duration value, and N be the representation of D as an
+ -- integer count of nanoseconds. Let
+ -- Q = N / Nano_Count, with truncation
+ -- R = N - Q * Nano_Count, the remainder 0 <= R < Nano_Count
+ -- so
+ -- T * Nano_Count
+ -- = N * Tick_Count - Q * Nano_Count * Tick_Count
+ -- + Q * Nano_Count * Tick_Count
+ -- = (N - Q * Nano_Count) * Tick_Count
+ -- + (Q * Tick_Count) * Nano_Count
+ -- = R * Tick_Count + (Q * Tick_Count) * Nano_Count
+ -- Now, let
+ -- Q1 = R * Tick_Count / Nano_Count, with truncation
+ -- R1 = R * Tick_Count - Q1 * Nano_Count, 0 <= R1 < Nano_Count
+ -- R * Tick_Count = Q1 * Nano_Count + R1
+ -- so
+ -- T * Nano_Count
+ -- = R * Tick_Count + (Q * Tick_Count) * Nano_Count
+ -- = Q1 * Nano_Count + R1 + (Q * Tick_Count) * Nano_Count
+ -- = (Q * Tick_Count + Q1) * Nano_Count + R1
+ -- and
+ -- T = Q * Tick_Count + Q1 + R1 / Nano_Count,
+ -- where 0 <= R1 / Nano_Count < 1
+
+ function To_RTIME (D : Duration) return RTIME is
+ N : RTIME := Raw_RTIME (D);
+ Q, Q1, RT : RTIME;
+
+ begin
+ Q := N / Nano_Count;
+ RT := (N - Q * Nano_Count) * Tick_Count;
+ Q1 := RT / Nano_Count;
+ return Q * Tick_Count + Q1;
+ end To_RTIME;
+
+ function Monotonic_Clock return Duration is
+ begin
+ pragma Debug (Printk ("procedure Clock called" & LF));
+
+ return To_Duration (Rt_Get_Time);
+ end Monotonic_Clock;
+
+ -------------------
+ -- RT_Resolution --
+ -------------------
+
+ function RT_Resolution return Duration is
+ begin
+ return 10#1.0#E-6;
+ end RT_Resolution;
+
+ ------------
+ -- Wakeup --
+ ------------
+
+ procedure Wakeup (T : Task_ID; Reason : ST.Task_States) is
+ Flags : Integer;
+ begin
+ pragma Debug (Printk ("procedure Wakeup called" & LF));
+
+ T.Common.State := Reason;
+ T.Common.LL.State := RT_TASK_READY;
+
+ R_Save_Flags (Flags);
+ R_Cli;
+
+ if Timer_Queue.Common.LL.Succ = To_Address (T) then
+ -- T is the first task in Timer_Queue, further check
+
+ if T.Common.LL.Succ = Timer_Queue'Address then
+ -- T is the only task in Timer_Queue, so deactivate timer
+
+ Rt_No_Timer;
+
+ else
+ -- T is the first task in Timer_Queue, so set timer to T's
+ -- successor's Resume_Time
+
+ Rt_Set_Timer (To_Task_ID (T.Common.LL.Succ).Common.LL.Resume_Time);
+ end if;
+ end if;
+
+ Delete_T (T);
+
+ -- If T is in Timer_Queue, T is removed. If not, nothing happened
+
+ Insert_R (T);
+ R_Restore_Flags (Flags);
+
+ Rt_Schedule;
+ end Wakeup;
+
+ -----------
+ -- Yield --
+ -----------
+
+ procedure Yield (Do_Yield : Boolean := True) is
+ Flags : Integer;
+ begin
+ pragma Debug (Printk ("procedure Yield called" & LF));
+
+ pragma Assert (Current_Task /= To_Task_ID (Idle_Task'Address));
+
+ R_Save_Flags (Flags);
+ R_Cli;
+ Delete_R (Current_Task);
+ Insert_R (Current_Task);
+
+ -- Remove Current_Task from the top of the Ready_Queue
+ -- and reinsert it back at proper position (the end of
+ -- tasks with the same active priority).
+
+ R_Restore_Flags (Flags);
+ Rt_Schedule;
+ end Yield;
+
+ ------------------
+ -- Set_Priority --
+ ------------------
+
+ -- This version implicitly assume that T is the Current_Task
+
+ procedure Set_Priority
+ (T : Task_ID;
+ Prio : System.Any_Priority;
+ Loss_Of_Inheritance : Boolean := False)
+ is
+ Flags : Integer;
+ begin
+ pragma Debug (Printk ("procedure Set_Priority called" & LF));
+ pragma Assert (T = Self);
+
+ T.Common.Current_Priority := Prio;
+
+ if T.Common.LL.Outer_Lock /= null then
+ -- If the task T is holding any lock, defer the priority change
+ -- until the lock is released. That is, T's Active_Priority will
+ -- be set to Prio after it unlocks the outer-most lock. See
+ -- Unlock for detail.
+ -- Nothing needs to be done here for this case
+
+ null;
+ else
+ -- If T is not holding any lock, change the priority right away.
+
+ R_Save_Flags (Flags);
+ R_Cli;
+ T.Common.LL.Active_Priority := Prio;
+ Delete_R (T);
+ Insert_RF (T);
+
+ -- Insert at the front of the queue for its new priority
+
+ R_Restore_Flags (Flags);
+ end if;
+
+ Rt_Schedule;
+ end Set_Priority;
+
+ ------------------
+ -- Get_Priority --
+ ------------------
+
+ function Get_Priority (T : Task_ID) return System.Any_Priority is
+ begin
+ pragma Debug (Printk ("procedure Get_Priority called" & LF));
+
+ return T.Common.Current_Priority;
+ end Get_Priority;
+
+ ----------------
+ -- Enter_Task --
+ ----------------
+
+ -- Do any target-specific initialization that is needed for a new task
+ -- that has to be done by the task itself. This is called from the task
+ -- wrapper, immediately after the task starts execution.
+
+ procedure Enter_Task (Self_ID : Task_ID) is
+ begin
+ -- Use this as "hook" to re-enable interrupts.
+ pragma Debug (Printk ("procedure Enter_Task called" & LF));
+
+ R_Sti;
+ end Enter_Task;
+
+ ----------------
+ -- New_ATCB --
+ ----------------
+
+ function New_ATCB (Entry_Num : Task_Entry_Index) return Task_ID is
+ T : constant Task_ID := Available_TCBs;
+ begin
+ pragma Debug (Printk ("function New_ATCB called" & LF));
+
+ if Entry_Num /= 0 then
+ -- We are preallocating all TCBs, so they must all have the
+ -- same number of entries, which means the value of
+ -- Entry_Num must be bounded. We probably could choose a
+ -- non-zero upper bound here, but the Ravenscar Profile
+ -- specifies that there be no task entries.
+ -- ???
+ -- Later, do something better for recovery from this error.
+
+ null;
+ end if;
+
+ if T /= null then
+ Available_TCBs := To_Task_ID (T.Common.LL.Next);
+ T.Common.LL.Next := System.Null_Address;
+ Known_Tasks (T.Known_Tasks_Index) := T;
+ end if;
+
+ return T;
+ end New_ATCB;
+
+ ----------------------
+ -- Initialize_TCB --
+ ----------------------
+
+ procedure Initialize_TCB (Self_ID : Task_ID; Succeeded : out Boolean) is
+ begin
+ pragma Debug (Printk ("procedure Initialize_TCB called" & LF));
+
+ -- Give the task a unique serial number.
+
+ Self_ID.Serial_Number := Next_Serial_Number;
+ Next_Serial_Number := Next_Serial_Number + 1;
+ pragma Assert (Next_Serial_Number /= 0);
+
+ Self_ID.Common.LL.L.Ceiling_Priority := System.Any_Priority'Last;
+ Self_ID.Common.LL.L.Owner := System.Null_Address;
+ Succeeded := True;
+ end Initialize_TCB;
+
+ -----------------
+ -- Create_Task --
+ -----------------
+
+ procedure Create_Task
+ (T : Task_ID;
+ Wrapper : System.Address;
+ Stack_Size : System.Parameters.Size_Type;
+ Priority : System.Any_Priority;
+ Succeeded : out Boolean)
+ is
+ Adjusted_Stack_Size : Integer;
+ Bottom : System.Address;
+ Flags : Integer;
+
+ begin
+ pragma Debug (Printk ("procedure Create_Task called" & LF));
+
+ Succeeded := True;
+
+ if T.Common.LL.Magic = RT_TASK_MAGIC then
+ Succeeded := False;
+ return;
+ end if;
+
+ if Stack_Size = Unspecified_Size then
+ Adjusted_Stack_Size := To_Integer (Default_Stack_Size);
+ elsif Stack_Size < Minimum_Stack_Size then
+ Adjusted_Stack_Size := To_Integer (Minimum_Stack_Size);
+ else
+ Adjusted_Stack_Size := To_Integer (Stack_Size);
+ end if;
+
+ Bottom := Kmalloc (Adjusted_Stack_Size, GFP_KERNEL);
+
+ if Bottom = System.Null_Address then
+ Succeeded := False;
+ return;
+ end if;
+
+ T.Common.LL.Uses_Fp := 1;
+
+ -- This field has to be reset to 1 if T uses FP unit. But, without
+ -- a library-level procedure provided by this package, it cannot
+ -- be set easily. So temporarily, set it to 1 (which means all the
+ -- tasks will use FP unit. ???
+
+ T.Common.LL.Magic := RT_TASK_MAGIC;
+ T.Common.LL.State := RT_TASK_READY;
+ T.Common.LL.Succ := To_Address (T);
+ T.Common.LL.Pred := To_Address (T);
+ T.Common.LL.Active_Priority := Priority;
+ T.Common.Current_Priority := Priority;
+
+ T.Common.LL.Stack_Bottom := Bottom;
+ T.Common.LL.Stack := Bottom + Storage_Offset (Adjusted_Stack_Size);
+
+ -- Store the value T into the stack, so that Task_wrapper (defined
+ -- in System.Tasking.Stages) will find that value for its parameter
+ -- Self_ID, when the scheduler eventually transfers control to the
+ -- new task.
+
+ T.Common.LL.Stack := T.Common.LL.Stack - Addr_Bytes;
+ To_Address_Ptr (T.Common.LL.Stack).all := To_Address (T);
+
+ -- Leave space for the return address, which will not be used,
+ -- since the task wrapper should never return.
+
+ T.Common.LL.Stack := T.Common.LL.Stack - Addr_Bytes;
+ To_Address_Ptr (T.Common.LL.Stack).all := System.Null_Address;
+
+ -- Put the entry point address of the task wrapper
+ -- procedure on the new top of the stack.
+
+ T.Common.LL.Stack := T.Common.LL.Stack - Addr_Bytes;
+ To_Address_Ptr (T.Common.LL.Stack).all := Wrapper;
+
+ R_Save_Flags (Flags);
+ R_Cli;
+ Insert_R (T);
+ R_Restore_Flags (Flags);
+ end Create_Task;
+
+ ------------------
+ -- Finalize_TCB --
+ ------------------
+
+ procedure Finalize_TCB (T : Task_ID) is
+ begin
+ pragma Debug (Printk ("procedure Finalize_TCB called" & LF));
+
+ pragma Assert (T.Common.LL.Succ = To_Address (T));
+
+ if T.Common.LL.State = RT_TASK_DORMANT then
+ Known_Tasks (T.Known_Tasks_Index) := null;
+ T.Common.LL.Next := To_Address (Available_TCBs);
+ Available_TCBs := T;
+ Kfree (T.Common.LL.Stack_Bottom);
+ end if;
+ end Finalize_TCB;
+
+ ---------------
+ -- Exit_Task --
+ ---------------
+
+ procedure Exit_Task is
+ Flags : Integer;
+ begin
+ pragma Debug (Printk ("procedure Exit_Task called" & LF));
+ pragma Assert (Current_Task /= To_Task_ID (Idle_Task'Address));
+ pragma Assert (Current_Task /= Environment_Task_ID);
+
+ R_Save_Flags (Flags);
+ R_Cli;
+ Current_Task.Common.LL.State := RT_TASK_DORMANT;
+ Current_Task.Common.LL.Magic := 0;
+ Delete_R (Current_Task);
+ R_Restore_Flags (Flags);
+ Rt_Schedule;
+ end Exit_Task;
+
+ ----------------
+ -- Abort_Task --
+ ----------------
+
+ -- ??? Not implemented for now
+
+ procedure Abort_Task (T : Task_ID) is
+ -- Should cause T to raise Abort_Signal the next time it
+ -- executes.
+ -- ??? Can this ever be called when T = Current_Task?
+ -- To be safe, do nothing in this case.
+ begin
+ pragma Debug (Printk ("procedure Abort_Task called" & LF));
+ null;
+ end Abort_Task;
+
+ ----------------
+ -- Check_Exit --
+ ----------------
+
+ -- Dummy versions. The only currently working versions is for solaris
+ -- (native).
+ -- We should probably copy the working versions over from the Solaris
+ -- version of this package, with any appropriate changes, since without
+ -- the checks on it will probably be nearly impossible to debug the
+ -- run-time system.
+
+ -- Not implemented for now
+
+ function Check_Exit (Self_ID : Task_ID) return Boolean is
+ begin
+ pragma Debug (Printk ("function Check_Exit called" & LF));
+
+ return True;
+ end Check_Exit;
+
+ --------------------
+ -- Check_No_Locks --
+ --------------------
+
+ function Check_No_Locks (Self_ID : Task_ID) return Boolean is
+ begin
+ pragma Debug (Printk ("function Check_No_Locks called" & LF));
+
+ if Self_ID.Common.LL.Outer_Lock = null then
+ return True;
+ else
+ return False;
+ end if;
+ end Check_No_Locks;
+
+ ----------------------
+ -- Environment_Task --
+ ----------------------
+
+ function Environment_Task return Task_ID is
+ begin
+ return Environment_Task_ID;
+ end Environment_Task;
+
+ -------------------------
+ -- Lock_All_Tasks_List --
+ -------------------------
+
+ procedure Lock_All_Tasks_List is
+ begin
+ pragma Debug (Printk ("procedure Lock_All_Tasks_List called" & LF));
+
+ Write_Lock (All_Tasks_L'Access);
+ end Lock_All_Tasks_List;
+
+ ---------------------------
+ -- Unlock_All_Tasks_List --
+ ---------------------------
+
+ procedure Unlock_All_Tasks_List is
+ begin
+ pragma Debug (Printk ("procedure Unlock_All_Tasks_List called" & LF));
+
+ Unlock (All_Tasks_L'Access);
+ end Unlock_All_Tasks_List;
+
+ -----------------
+ -- Stack_Guard --
+ -----------------
+
+ -- Not implemented for now
+
+ procedure Stack_Guard (T : Task_ID; On : Boolean) is
+ begin
+ null;
+ end Stack_Guard;
+
+ --------------------
+ -- Get_Thread_Id --
+ --------------------
+
+ function Get_Thread_Id (T : Task_ID) return OSI.Thread_Id is
+ begin
+ return To_Address (T);
+ end Get_Thread_Id;
+
+ ------------------
+ -- Suspend_Task --
+ ------------------
+
+ function Suspend_Task
+ (T : Task_ID;
+ Thread_Self : OSI.Thread_Id) return Boolean is
+ begin
+ return False;
+ end Suspend_Task;
+
+ -----------------
+ -- Resume_Task --
+ -----------------
+
+ function Resume_Task
+ (T : ST.Task_ID;
+ Thread_Self : OSI.Thread_Id) return Boolean is
+ begin
+ return False;
+ end Resume_Task;
+
+ -----------------
+ -- Init_Module --
+ -----------------
+
+ function Init_Module return Integer is
+ procedure adainit;
+ pragma Import (C, adainit);
+
+ begin
+ adainit;
+ In_Elab_Code := False;
+ Set_Priority (Environment_Task_ID, Any_Priority'First);
+ return 0;
+ end Init_Module;
+
+ --------------------
+ -- Cleanup_Module --
+ --------------------
+
+ procedure Cleanup_Module is
+ procedure adafinal;
+ pragma Import (C, adafinal);
+
+ begin
+ adafinal;
+ end Cleanup_Module;
+
+ ----------------
+ -- Initialize --
+ ----------------
+
+ -- The environment task is "special". The TCB of the environment task is
+ -- not in the TCB_Array above. Logically, all initialization code for the
+ -- runtime system is executed by the environment task, but until the
+ -- environment task has initialized its own TCB we dare not execute any
+ -- calls that try to access the TCB of Current_Task. It is allocated by
+ -- target-independent runtime system code, in System.Tasking.Initializa-
+ -- tion.Init_RTS, before the call to this procedure Initialize. The
+ -- target-independent runtime system initializes all the components that
+ -- are target-independent, but this package needs to be given a chance to
+ -- initialize the target-dependent data. We do that in this procedure.
+
+ -- In the present implementation, Environment_Task is set to be the
+ -- regular Linux kernel task.
+
+ procedure Initialize (Environment_Task : Task_ID) is
+ begin
+ pragma Debug (Printk ("procedure Initialize called" & LF));
+
+ Environment_Task_ID := Environment_Task;
+
+ -- Build the list of available ATCB's.
+
+ Available_TCBs := To_Task_ID (TCB_Array (1)'Address);
+
+ for J in TCB_Array'First + 1 .. TCB_Array'Last - 1 loop
+ -- Note that the zeroth element in TCB_Array is not used, see
+ -- comments following the declaration of TCB_Array
+
+ TCB_Array (J).Common.LL.Next := TCB_Array (J + 1)'Address;
+ end loop;
+
+ TCB_Array (TCB_Array'Last).Common.LL.Next := System.Null_Address;
+
+ -- Initialize the idle task, which is the head of Ready_Queue.
+
+ Idle_Task.Common.LL.Magic := RT_TASK_MAGIC;
+ Idle_Task.Common.LL.State := RT_TASK_READY;
+ Idle_Task.Common.Current_Priority := System.Any_Priority'First;
+ Idle_Task.Common.LL.Active_Priority := System.Any_Priority'First;
+ Idle_Task.Common.LL.Succ := Idle_Task'Address;
+ Idle_Task.Common.LL.Pred := Idle_Task'Address;
+
+ -- Initialize the regular Linux kernel task.
+
+ Environment_Task.Common.LL.Magic := RT_TASK_MAGIC;
+ Environment_Task.Common.LL.State := RT_TASK_READY;
+ Environment_Task.Common.Current_Priority := System.Any_Priority'First;
+ Environment_Task.Common.LL.Active_Priority := System.Any_Priority'First;
+ Environment_Task.Common.LL.Succ := To_Address (Environment_Task);
+ Environment_Task.Common.LL.Pred := To_Address (Environment_Task);
+
+ -- Initialize the head of Timer_Queue
+
+ Timer_Queue.Common.LL.Succ := Timer_Queue'Address;
+ Timer_Queue.Common.LL.Pred := Timer_Queue'Address;
+ Timer_Queue.Common.LL.Resume_Time := Max_Sensible_Delay;
+
+ -- Set the current task to regular Linux kernel task
+
+ Current_Task := Environment_Task;
+
+ -- Set Timer_Wrapper to be the timer handler
+
+ Rt_Free_Timer;
+ Rt_Request_Timer (Timer_Wrapper'Address);
+
+ -- Initialize the lock used to synchronize chain of all ATCBs.
+
+ Initialize_Lock (All_Tasks_L'Access, All_Tasks_Level);
+
+ Enter_Task (Environment_Task);
+ end Initialize;
+
+end System.Task_Primitives.Operations;