1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
  
     | 
    
      ------------------------------------------------------------------------------
--                                                                          --
--                         GNAT RUN-TIME COMPONENTS                         --
--                                                                          --
--                SYSTEM.MULTIPROCESSORS.DISPATCHING_DOMAINS                --
--                                                                          --
--                                  B o d y                                 --
--                                                                          --
--          Copyright (C) 2011-2015, 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.     --
--                                                                          --
------------------------------------------------------------------------------
--  Body used on targets where the operating system supports setting task
--  affinities.
with System.Tasking.Initialization;
with System.Task_Primitives.Operations; use System.Task_Primitives.Operations;
with Ada.Unchecked_Conversion;
package body System.Multiprocessors.Dispatching_Domains is
   package ST renames System.Tasking;
   -----------------------
   -- Local subprograms --
   -----------------------
   function Convert_Ids is new
     Ada.Unchecked_Conversion (Ada.Task_Identification.Task_Id, ST.Task_Id);
   procedure Unchecked_Set_Affinity
     (Domain : ST.Dispatching_Domain_Access;
      CPU    : CPU_Range;
      T      : ST.Task_Id);
   --  Internal procedure to move a task to a target domain and CPU. No checks
   --  are performed about the validity of the domain and the CPU because they
   --  are done by the callers of this procedure (either Assign_Task or
   --  Set_CPU).
   procedure Freeze_Dispatching_Domains;
   pragma Export
     (Ada, Freeze_Dispatching_Domains, "__gnat_freeze_dispatching_domains");
   --  Signal the time when no new dispatching domains can be created. It
   --  should be called before the environment task calls the main procedure
   --  (and after the elaboration code), so the binder-generated file needs to
   --  import and call this procedure.
   -----------------
   -- Assign_Task --
   -----------------
   procedure Assign_Task
     (Domain : in out Dispatching_Domain;
      CPU    : CPU_Range := Not_A_Specific_CPU;
      T      : Ada.Task_Identification.Task_Id :=
                 Ada.Task_Identification.Current_Task)
   is
      Target : constant ST.Task_Id := Convert_Ids (T);
      use type ST.Dispatching_Domain_Access;
   begin
      --  The exception Dispatching_Domain_Error is propagated if T is already
      --  assigned to a Dispatching_Domain other than
      --  System_Dispatching_Domain, or if CPU is not one of the processors of
      --  Domain (and is not Not_A_Specific_CPU).
      if Dispatching_Domain (Target.Common.Domain) /= System_Dispatching_Domain
      then
         raise Dispatching_Domain_Error with
           "task already in user-defined dispatching domain";
      elsif CPU /= Not_A_Specific_CPU and then CPU not in Domain'Range then
         raise Dispatching_Domain_Error with
           "processor does not belong to dispatching domain";
      end if;
      --  Assigning a task to System_Dispatching_Domain that is already
      --  assigned to that domain has no effect.
      if Domain = System_Dispatching_Domain then
         return;
      else
         --  Set the task affinity once we know it is possible
         Unchecked_Set_Affinity
           (ST.Dispatching_Domain_Access (Domain), CPU, Target);
      end if;
   end Assign_Task;
   ------------
   -- Create --
   ------------
   function Create (First : CPU; Last : CPU_Range) return Dispatching_Domain is
   begin
      return Create ((First .. Last => True));
   end Create;
   function Create (Set : CPU_Set) return Dispatching_Domain is
      ST_DD : aliased constant ST.Dispatching_Domain :=
        ST.Dispatching_Domain (Set);
      First : constant CPU       := Get_First_CPU (ST_DD'Unrestricted_Access);
      Last  : constant CPU_Range := Get_Last_CPU (ST_DD'Unrestricted_Access);
      subtype Rng is CPU_Range range First .. Last;
      use type ST.Dispatching_Domain;
      use type ST.Dispatching_Domain_Access;
      use type ST.Array_Allocated_Tasks;
      use type ST.Task_Id;
      T : ST.Task_Id;
      New_System_Domain : ST.Dispatching_Domain := ST.System_Domain.all;
      ST_DD_Slice : constant ST.Dispatching_Domain := ST_DD (Rng);
   begin
      --  The set of processors for creating a dispatching domain must
      --  comply with the following restrictions:
      --    - Not exceeding the range of available processors.
      --    - CPUs from the System_Dispatching_Domain.
      --    - The calling task must be the environment task.
      --    - The call to Create must take place before the call to the main
      --      subprogram.
      --    - Set does not contain a processor with a task assigned to it.
      --    - The allocation cannot leave System_Dispatching_Domain empty.
      --  Note that a previous version of the language forbade empty domains.
      if Rng'Last > Number_Of_CPUs then
         raise Dispatching_Domain_Error with
           "CPU not supported by the target";
      end if;
      declare
         System_Domain_Slice : constant ST.Dispatching_Domain :=
           ST.System_Domain (Rng);
         Actual : constant ST.Dispatching_Domain :=
           ST_DD_Slice and not System_Domain_Slice;
         Expected : constant ST.Dispatching_Domain := (Rng => False);
      begin
         if Actual /= Expected then
            raise Dispatching_Domain_Error with
              "CPU not currently in System_Dispatching_Domain";
         end if;
      end;
      if Self /= Environment_Task then
         raise Dispatching_Domain_Error with
           "only the environment task can create dispatching domains";
      end if;
      if ST.Dispatching_Domains_Frozen then
         raise Dispatching_Domain_Error with
           "cannot create dispatching domain after call to main procedure";
      end if;
      for Proc in Rng loop
         if ST_DD (Proc) and then
           ST.Dispatching_Domain_Tasks (Proc) /= 0
         then
            raise Dispatching_Domain_Error with "CPU has tasks assigned";
         end if;
      end loop;
      New_System_Domain (Rng) := New_System_Domain (Rng) and not ST_DD_Slice;
      if New_System_Domain = (New_System_Domain'Range => False) then
         raise Dispatching_Domain_Error with
           "would leave System_Dispatching_Domain empty";
      end if;
      return Result : constant Dispatching_Domain :=
        new ST.Dispatching_Domain'(ST_DD_Slice)
      do
         --  At this point we need to fix the processors belonging to the
         --  system domain, and change the affinity of every task that has
         --  been created and assigned to the system domain.
         ST.Initialization.Defer_Abort (Self);
         Lock_RTS;
         ST.System_Domain (Rng) := New_System_Domain (Rng);
         pragma Assert (ST.System_Domain.all = New_System_Domain);
         --  Iterate the list of tasks belonging to the default system
         --  dispatching domain and set the appropriate affinity.
         T := ST.All_Tasks_List;
         while T /= null loop
            if T.Common.Domain = ST.System_Domain then
               Set_Task_Affinity (T);
            end if;
            T := T.Common.All_Tasks_Link;
         end loop;
         Unlock_RTS;
         ST.Initialization.Undefer_Abort (Self);
      end return;
   end Create;
   -----------------------------
   -- Delay_Until_And_Set_CPU --
   -----------------------------
   procedure Delay_Until_And_Set_CPU
     (Delay_Until_Time : Ada.Real_Time.Time;
      CPU              : CPU_Range)
   is
   begin
      --  Not supported atomically by the underlying operating systems.
      --  Operating systems use to migrate the task immediately after the call
      --  to set the affinity.
      delay until Delay_Until_Time;
      Set_CPU (CPU);
   end Delay_Until_And_Set_CPU;
   --------------------------------
   -- Freeze_Dispatching_Domains --
   --------------------------------
   procedure Freeze_Dispatching_Domains is
   begin
      --  Signal the end of the elaboration code
      ST.Dispatching_Domains_Frozen := True;
   end Freeze_Dispatching_Domains;
   -------------
   -- Get_CPU --
   -------------
   function Get_CPU
     (T : Ada.Task_Identification.Task_Id :=
            Ada.Task_Identification.Current_Task) return CPU_Range
   is
   begin
      return Convert_Ids (T).Common.Base_CPU;
   end Get_CPU;
   -----------------
   -- Get_CPU_Set --
   -----------------
   function Get_CPU_Set (Domain : Dispatching_Domain) return CPU_Set is
   begin
      return CPU_Set (Domain.all);
   end Get_CPU_Set;
   ----------------------------
   -- Get_Dispatching_Domain --
   ----------------------------
   function Get_Dispatching_Domain
     (T : Ada.Task_Identification.Task_Id :=
            Ada.Task_Identification.Current_Task) return Dispatching_Domain
   is
   begin
      return Result : constant Dispatching_Domain :=
        Dispatching_Domain (Convert_Ids (T).Common.Domain)
      do
         pragma Assert (Result /= null);
      end return;
   end Get_Dispatching_Domain;
   -------------------
   -- Get_First_CPU --
   -------------------
   function Get_First_CPU (Domain : Dispatching_Domain) return CPU is
   begin
      for Proc in Domain'Range loop
         if Domain (Proc) then
            return Proc;
         end if;
      end loop;
      return CPU'First;
   end Get_First_CPU;
   ------------------
   -- Get_Last_CPU --
   ------------------
   function Get_Last_CPU (Domain : Dispatching_Domain) return CPU_Range is
   begin
      for Proc in reverse Domain'Range loop
         if Domain (Proc) then
            return Proc;
         end if;
      end loop;
      return CPU_Range'First;
   end Get_Last_CPU;
   -------------
   -- Set_CPU --
   -------------
   procedure Set_CPU
     (CPU : CPU_Range;
      T   : Ada.Task_Identification.Task_Id :=
              Ada.Task_Identification.Current_Task)
   is
      Target : constant ST.Task_Id := Convert_Ids (T);
      use type ST.Dispatching_Domain_Access;
   begin
      --  The exception Dispatching_Domain_Error is propagated if CPU is not
      --  one of the processors of the Dispatching_Domain on which T is
      --  assigned (and is not Not_A_Specific_CPU).
      if CPU /= Not_A_Specific_CPU and then
        (CPU not in Target.Common.Domain'Range or else
         not Target.Common.Domain (CPU))
      then
         raise Dispatching_Domain_Error with
           "processor does not belong to the task's dispatching domain";
      end if;
      Unchecked_Set_Affinity (Target.Common.Domain, CPU, Target);
   end Set_CPU;
   ----------------------------
   -- Unchecked_Set_Affinity --
   ----------------------------
   procedure Unchecked_Set_Affinity
     (Domain : ST.Dispatching_Domain_Access;
      CPU    : CPU_Range;
      T      : ST.Task_Id)
   is
      Source_CPU : constant CPU_Range := T.Common.Base_CPU;
      use type ST.Dispatching_Domain_Access;
   begin
      Write_Lock (T);
      --  Move to the new domain
      T.Common.Domain := Domain;
      --  Attach the CPU to the task
      T.Common.Base_CPU := CPU;
      --  Change the number of tasks attached to a given task in the system
      --  domain if needed.
      if not ST.Dispatching_Domains_Frozen
        and then (Domain = null or else Domain = ST.System_Domain)
      then
         --  Reduce the number of tasks attached to the CPU from which this
         --  task is being moved, if needed.
         if Source_CPU /= Not_A_Specific_CPU then
            ST.Dispatching_Domain_Tasks (Source_CPU) :=
              ST.Dispatching_Domain_Tasks (Source_CPU) - 1;
         end if;
         --  Increase the number of tasks attached to the CPU to which this
         --  task is being moved, if needed.
         if CPU /= Not_A_Specific_CPU then
            ST.Dispatching_Domain_Tasks (CPU) :=
              ST.Dispatching_Domain_Tasks (CPU) + 1;
         end if;
      end if;
      --  Change the actual affinity calling the operating system level
      Set_Task_Affinity (T);
      Unlock (T);
   end Unchecked_Set_Affinity;
end System.Multiprocessors.Dispatching_Domains;
 
     |