| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS -- |
| -- -- |
| -- SYSTEM.TASK_PRIMITIVES.OPERATIONS.MONOTONIC -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1992-2022, Free Software Foundation, Inc. -- |
| -- -- |
| -- 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 3, or (at your option) any later ver- -- |
| -- sion. GNAT 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. -- |
| -- -- |
| -- As a special exception under Section 7 of GPL version 3, you are granted -- |
| -- additional permissions described in the GCC Runtime Library Exception, -- |
| -- version 3.1, as published by the Free Software Foundation. -- |
| -- -- |
| -- You should have received a copy of the GNU General Public License and -- |
| -- a copy of the GCC Runtime Library Exception along with this program; -- |
| -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- |
| -- <http://www.gnu.org/licenses/>. -- |
| -- -- |
| -- GNARL was developed by the GNARL team at Florida State University. -- |
| -- Extensive contributions were provided by Ada Core Technologies, Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| -- This is the Monotonic version of this package for Posix and Linux targets. |
| |
| separate (System.Task_Primitives.Operations) |
| package body Monotonic is |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| procedure Compute_Deadline |
| (Time : Duration; |
| Mode : ST.Delay_Modes; |
| Check_Time : out Duration; |
| Abs_Time : out Duration); |
| -- Helper for Timed_Sleep and Timed_Delay: given a deadline specified by |
| -- Time and Mode, compute the current clock reading (Check_Time), and the |
| -- target absolute and relative clock readings (Abs_Time). The |
| -- epoch for Time depends on Mode; the epoch for Check_Time and Abs_Time |
| -- is always that of CLOCK_RT_Ada. |
| |
| --------------------- |
| -- Monotonic_Clock -- |
| --------------------- |
| |
| function Monotonic_Clock return Duration is |
| TS : aliased timespec; |
| Result : Interfaces.C.int; |
| begin |
| Result := clock_gettime |
| (clock_id => OSC.CLOCK_RT_Ada, tp => TS'Unchecked_Access); |
| pragma Assert (Result = 0); |
| |
| return To_Duration (TS); |
| end Monotonic_Clock; |
| |
| ------------------- |
| -- RT_Resolution -- |
| ------------------- |
| |
| function RT_Resolution return Duration is |
| TS : aliased timespec; |
| Result : Interfaces.C.int; |
| |
| begin |
| Result := clock_getres (OSC.CLOCK_REALTIME, TS'Unchecked_Access); |
| pragma Assert (Result = 0); |
| |
| return To_Duration (TS); |
| end RT_Resolution; |
| |
| ---------------------- |
| -- Compute_Deadline -- |
| ---------------------- |
| |
| procedure Compute_Deadline |
| (Time : Duration; |
| Mode : ST.Delay_Modes; |
| Check_Time : out Duration; |
| Abs_Time : out Duration) |
| is |
| begin |
| Check_Time := Monotonic_Clock; |
| |
| -- Relative deadline |
| |
| if Mode = Relative then |
| Abs_Time := Duration'Min (Time, Max_Sensible_Delay) + Check_Time; |
| |
| pragma Warnings (Off); |
| -- Comparison "OSC.CLOCK_RT_Ada = OSC.CLOCK_REALTIME" is compile |
| -- time known. |
| |
| -- Absolute deadline specified using the tasking clock (CLOCK_RT_Ada) |
| |
| elsif Mode = Absolute_RT |
| or else OSC.CLOCK_RT_Ada = OSC.CLOCK_REALTIME |
| then |
| pragma Warnings (On); |
| Abs_Time := Duration'Min (Check_Time + Max_Sensible_Delay, Time); |
| |
| -- Absolute deadline specified using the calendar clock, in the |
| -- case where it is not the same as the tasking clock: compensate for |
| -- difference between clock epochs (Base_Time - Base_Cal_Time). |
| |
| else |
| declare |
| Cal_Check_Time : constant Duration := OS_Primitives.Clock; |
| RT_Time : constant Duration := |
| Time + Check_Time - Cal_Check_Time; |
| |
| begin |
| Abs_Time := |
| Duration'Min (Check_Time + Max_Sensible_Delay, RT_Time); |
| |
| end; |
| end if; |
| end Compute_Deadline; |
| |
| ----------------- |
| -- Timed_Sleep -- |
| ----------------- |
| |
| -- 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 : ST.Task_Id; |
| Time : Duration; |
| Mode : ST.Delay_Modes; |
| Reason : System.Tasking.Task_States; |
| Timedout : out Boolean; |
| Yielded : out Boolean) |
| is |
| pragma Unreferenced (Reason); |
| |
| Base_Time : Duration; |
| Check_Time : Duration; |
| Abs_Time : Duration; |
| P_Abs_Time : Duration; |
| |
| Request : aliased timespec; |
| Result : Interfaces.C.int; |
| Exit_Outer : Boolean := False; |
| |
| begin |
| Timedout := True; |
| Yielded := False; |
| |
| Compute_Deadline |
| (Time => Time, |
| Mode => Mode, |
| Check_Time => Check_Time, |
| Abs_Time => Abs_Time); |
| Base_Time := Check_Time; |
| |
| -- To keep a sensible Max_Sensible_Delay on a target whose system |
| -- maximum is less than sensible, we split the delay into manageable |
| -- chunks of time less than or equal to the Max_System_Delay. |
| |
| if Abs_Time > Check_Time then |
| |
| Outer : loop |
| |
| pragma Warnings (Off, "condition is always *"); |
| if Max_System_Delay < Max_Sensible_Delay and then |
| Abs_Time > Check_Time + Max_System_Delay |
| then |
| P_Abs_Time := Check_Time + Max_System_Delay; |
| else |
| P_Abs_Time := Abs_Time; |
| Exit_Outer := True; |
| end if; |
| pragma Warnings (On); |
| |
| Request := To_Timespec (P_Abs_Time); |
| |
| Inner : loop |
| exit Outer |
| when Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level; |
| |
| Result := |
| pthread_cond_timedwait |
| (cond => Self_ID.Common.LL.CV'Access, |
| mutex => Self_ID.Common.LL.L'Access, |
| abstime => Request'Access); |
| |
| case Result is |
| when 0 | EINTR => |
| -- Somebody may have called Wakeup for us |
| Timedout := False; |
| exit Outer; |
| |
| when ETIMEDOUT => |
| exit Outer when Exit_Outer; |
| Check_Time := Monotonic_Clock; |
| exit Inner; |
| |
| when others => |
| pragma Assert (Standard.False); |
| |
| end case; |
| |
| exit Outer |
| when Abs_Time <= Check_Time or else Check_Time < Base_Time; |
| |
| end loop Inner; |
| end loop Outer; |
| end if; |
| end Timed_Sleep; |
| |
| ----------------- |
| -- Timed_Delay -- |
| ----------------- |
| |
| -- This is for use in implementing delay statements, so we assume the |
| -- caller is abort-deferred but is holding no locks. |
| |
| procedure Timed_Delay |
| (Self_ID : ST.Task_Id; |
| Time : Duration; |
| Mode : ST.Delay_Modes) |
| is |
| Base_Time : Duration; |
| Check_Time : Duration; |
| Abs_Time : Duration; |
| P_Abs_Time : Duration; |
| Request : aliased timespec; |
| |
| Result : Interfaces.C.int; |
| Exit_Outer : Boolean := False; |
| |
| begin |
| Write_Lock (Self_ID); |
| |
| Compute_Deadline |
| (Time => Time, |
| Mode => Mode, |
| Check_Time => Check_Time, |
| Abs_Time => Abs_Time); |
| Base_Time := Check_Time; |
| |
| -- To keep a sensible Max_Sensible_Delay on a target whose system |
| -- maximum is less than sensible, we split the delay into manageable |
| -- chunks of time less than or equal to the Max_System_Delay. |
| |
| if Abs_Time > Check_Time then |
| Self_ID.Common.State := Delay_Sleep; |
| |
| Outer : loop |
| |
| pragma Warnings (Off, "condition is always *"); |
| if Max_System_Delay < Max_Sensible_Delay and then |
| Abs_Time > Check_Time + Max_System_Delay |
| then |
| P_Abs_Time := Check_Time + Max_System_Delay; |
| else |
| P_Abs_Time := Abs_Time; |
| Exit_Outer := True; |
| end if; |
| pragma Warnings (On); |
| |
| Request := To_Timespec (P_Abs_Time); |
| |
| Inner : loop |
| exit Outer |
| when Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level; |
| |
| Result := |
| pthread_cond_timedwait |
| (cond => Self_ID.Common.LL.CV'Access, |
| mutex => Self_ID.Common.LL.L'Access, |
| abstime => Request'Access); |
| |
| case Result is |
| when ETIMEDOUT => |
| exit Outer when Exit_Outer; |
| Check_Time := Monotonic_Clock; |
| exit Inner; |
| |
| when 0 | EINTR => null; |
| |
| when others => |
| pragma Assert (Standard.False); |
| |
| end case; |
| |
| exit Outer |
| when Abs_Time <= Check_Time or else Check_Time < Base_Time; |
| |
| end loop Inner; |
| end loop Outer; |
| |
| Self_ID.Common.State := Runnable; |
| end if; |
| |
| Unlock (Self_ID); |
| pragma Unreferenced (Result); |
| Result := sched_yield; |
| end Timed_Delay; |
| |
| end Monotonic; |