This patch adds the support for the CPU aspect to control the allocation of tasks to processors.
The following test case should compile quietly in Ada 2012 mode, and execute without any message (task T should execute on the first processor): pragma Ada_2012; procedure Test_CPU_Aspect is task T with CPU => 1; task body T is begin null; end T; begin null; end Test_CPU_Aspect; Tested on x86_64-pc-linux-gnu, committed on trunk 2011-09-01 Jose Ruiz <r...@adacore.com> * aspects.ads (Aspect_Id, Aspect_Argument, Aspect_Names): Add the CPU aspect. * aspects.adb (Canonical_Aspect): Add entry for the CPU aspect. * sem_ch13.adb (Analyze_Aspect_Specifications): Analyze the CPU aspect in a similar way as we do for the Priority or Dispatching_Domain aspect. * s-mudido-affinity.adb (Dispatching_Domain_Tasks, Dispatching_Domains_Frozen): Move this local data to package System.Tasking because with the CPU aspect we need to have access to this data when creating the task in System.Tasking.Stages.Create_Task * s-taskin.ads (Dispatching_Domain_Tasks, Dispatching_Domains_Frozen): Move these variables from the body of System.Multiprocessors.Dispatching_Domains because with the CPU aspect we need to have access to this data when creating the task in System.Tasking.Stages.Create_Task. * s-taskin.adb (Initialize): Signal the allocation of the environment task to a CPU, if any, so that we know whether the CPU can be transferred to a different dispatching domain. * s-tassta.adb (Create_Task): Check whether the CPU to which this task is being allocated belongs to the dispatching domain where the task lives. Signal the allocation of the task to a CPU, if any, so that we know whether the CPU can be transferred to a different dispatching domain.
Index: s-tassta.adb =================================================================== --- s-tassta.adb (revision 178381) +++ s-tassta.adb (working copy) @@ -493,6 +493,8 @@ Len : Natural; Base_CPU : System.Multiprocessors.CPU_Range; + use type System.Multiprocessors.CPU_Range; + pragma Unreferenced (Relative_Deadline); -- EDF scheduling is not supported by any of the target platforms so -- this parameter is not passed any further. @@ -540,10 +542,6 @@ else System.Multiprocessors.CPU_Range (CPU)); end if; - -- ??? If we want to handle the interaction between pragma CPU and - -- dispatching domains we would need to signal that this task is being - -- allocated to a processor. - -- Find parent P of new Task, via master level number P := Self_ID; @@ -658,6 +656,36 @@ Unlock (Self_ID); Unlock_RTS; + -- The CPU associated to the task (if any) must belong to the + -- dispatching domain. + + if Base_CPU /= System.Multiprocessors.Not_A_Specific_CPU and then + (Base_CPU not in T.Common.Domain'Range + or else not T.Common.Domain (Base_CPU)) + then + Initialization.Undefer_Abort_Nestable (Self_ID); + raise Tasking_Error with "CPU not in dispatching domain"; + end if; + + -- In order to handle the interaction between pragma CPU and + -- dispatching domains we need to signal that this task is being + -- allocated to a processor. This is needed only for tasks belonging to + -- the system domain (the creation of new dispatching domains can only + -- take processors from the system domain) and only before the + -- environment task calls the main procedure (dispatching domains cannot + -- be created after this). + + if Base_CPU /= System.Multiprocessors.Not_A_Specific_CPU + and then T.Common.Domain = System.Tasking.System_Domain + and then not System.Tasking.Dispatching_Domains_Frozen + then + -- Increase the number of tasks attached to the CPU to which this + -- task is being moved. + + Dispatching_Domain_Tasks (Base_CPU) := + Dispatching_Domain_Tasks (Base_CPU) + 1; + end if; + -- Note: we should not call 'new' while holding locks since new -- may use locks (e.g. RTS_Lock under Windows) itself and cause a -- deadlock. Index: s-mudido-affinity.adb =================================================================== --- s-mudido-affinity.adb (revision 178381) +++ s-mudido-affinity.adb (working copy) @@ -41,21 +41,6 @@ package ST renames System.Tasking; - ---------------- - -- Local data -- - ---------------- - - Dispatching_Domain_Tasks : array (CPU'First .. Number_Of_CPUs) of Natural := - (others => 0); - -- We need to store whether there are tasks allocated to concrete - -- processors in the default system dispatching domain because we need to - -- check it before creating a new dispatching domain. - -- ??? Tasks allocated with pragma CPU are not taken into account here. - - Dispatching_Domains_Frozen : Boolean := False; - -- True when the main procedure has been called. Hence, no new dispatching - -- domains can be created when this flag is True. - ----------------------- -- Local subprograms -- ----------------------- @@ -132,6 +117,7 @@ function Create (First, Last : CPU) return Dispatching_Domain is use type System.Tasking.Dispatching_Domain; use type System.Tasking.Dispatching_Domain_Access; + use type System.Tasking.Array_Allocated_Tasks; use type System.Tasking.Task_Id; Valid_System_Domain : constant Boolean := @@ -177,7 +163,7 @@ "CPU range not currently in System_Dispatching_Domain"; elsif - Dispatching_Domain_Tasks (First .. Last) /= (First .. Last => 0) + ST.Dispatching_Domain_Tasks (First .. Last) /= (First .. Last => 0) then raise Dispatching_Domain_Error with "CPU range has tasks assigned"; @@ -189,7 +175,7 @@ raise Dispatching_Domain_Error with "only the environment task can create dispatching domains"; - elsif Dispatching_Domains_Frozen then + elsif ST.Dispatching_Domains_Frozen then raise Dispatching_Domain_Error with "cannot create dispatching domain after call to main program"; end if; @@ -253,7 +239,7 @@ begin -- Signal the end of the elaboration code - Dispatching_Domains_Frozen := True; + ST.Dispatching_Domains_Frozen := True; end Freeze_Dispatching_Domains; ------------- @@ -370,23 +356,23 @@ -- Change the number of tasks attached to a given task in the system -- domain if needed. - if not Dispatching_Domains_Frozen + 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 - Dispatching_Domain_Tasks (Source_CPU) := - Dispatching_Domain_Tasks (Source_CPU) - 1; + 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 - Dispatching_Domain_Tasks (CPU) := - Dispatching_Domain_Tasks (CPU) + 1; + ST.Dispatching_Domain_Tasks (CPU) := + ST.Dispatching_Domain_Tasks (CPU) + 1; end if; end if; Index: aspects.adb =================================================================== --- aspects.adb (revision 178381) +++ aspects.adb (working copy) @@ -219,6 +219,7 @@ Aspect_Bit_Order => Aspect_Bit_Order, Aspect_Component_Size => Aspect_Component_Size, Aspect_Constant_Indexing => Aspect_Constant_Indexing, + Aspect_CPU => Aspect_CPU, Aspect_Default_Component_Value => Aspect_Default_Component_Value, Aspect_Default_Iterator => Aspect_Default_Iterator, Aspect_Default_Value => Aspect_Default_Value, Index: aspects.ads =================================================================== --- aspects.ads (revision 178381) +++ aspects.ads (working copy) @@ -50,6 +50,7 @@ Aspect_Bit_Order, Aspect_Component_Size, Aspect_Constant_Indexing, + Aspect_CPU, Aspect_Default_Component_Value, Aspect_Default_Iterator, Aspect_Default_Value, @@ -188,6 +189,7 @@ Aspect_Bit_Order => Expression, Aspect_Component_Size => Expression, Aspect_Constant_Indexing => Name, + Aspect_CPU => Expression, Aspect_Default_Component_Value => Expression, Aspect_Default_Iterator => Name, Aspect_Default_Value => Expression, @@ -248,6 +250,7 @@ Aspect_Compiler_Unit => Name_Compiler_Unit, Aspect_Component_Size => Name_Component_Size, Aspect_Constant_Indexing => Name_Constant_Indexing, + Aspect_CPU => Name_CPU, Aspect_Default_Iterator => Name_Default_Iterator, Aspect_Default_Value => Name_Default_Value, Aspect_Default_Component_Value => Name_Default_Component_Value, Index: sem_ch13.adb =================================================================== --- sem_ch13.adb (revision 178381) +++ sem_ch13.adb (working copy) @@ -1151,7 +1151,8 @@ when Aspect_Priority | Aspect_Interrupt_Priority | - Aspect_Dispatching_Domain => + Aspect_Dispatching_Domain | + Aspect_CPU => declare Pname : Name_Id; begin @@ -1161,6 +1162,9 @@ elsif A_Id = Aspect_Interrupt_Priority then Pname := Name_Interrupt_Priority; + elsif A_Id = Aspect_CPU then + Pname := Name_CPU; + else Pname := Name_Dispatching_Domain; end if; @@ -1495,11 +1499,13 @@ -- For Priority aspects, insert into the task or -- protected definition, which we need to create if it's - -- not there. + -- not there. The same applies to CPU and + -- Dispatching_Domain but only to tasks. when Aspect_Priority | Aspect_Interrupt_Priority | - Aspect_Dispatching_Domain => + Aspect_Dispatching_Domain | + Aspect_CPU => declare T : Node_Id; -- the type declaration L : List_Id; -- list of decls of task/protected @@ -1514,6 +1520,7 @@ if Nkind (T) = N_Protected_Type_Declaration and then A_Id /= Aspect_Dispatching_Domain + and then A_Id /= Aspect_CPU then pragma Assert (Present (Protected_Definition (T))); @@ -5890,6 +5897,9 @@ when Aspect_Bit_Order => T := RTE (RE_Bit_Order); + when Aspect_CPU => + T := RTE (RE_CPU_Range); + when Aspect_Dispatching_Domain => T := RTE (RE_Dispatching_Domain); Index: s-taskin.adb =================================================================== --- s-taskin.adb (revision 178381) +++ s-taskin.adb (working copy) @@ -189,6 +189,8 @@ Base_CPU : System.Multiprocessors.CPU_Range; Success : Boolean; + use type System.Multiprocessors.CPU_Range; + begin if Initialized then return; @@ -233,10 +235,21 @@ T.Common.Domain := System_Domain; - -- ??? If we want to handle the interaction between pragma CPU and - -- dispatching domains we would need to signal that this task is being - -- allocated to a processor. + Dispatching_Domain_Tasks := + new Array_Allocated_Tasks' + (Multiprocessors.CPU'First .. Multiprocessors.Number_Of_CPUs => 0); + -- Signal that this task is being allocated to a processor + + if Base_CPU /= System.Multiprocessors.Not_A_Specific_CPU then + + -- Increase the number of tasks attached to the CPU to which this + -- task is allocated. + + Dispatching_Domain_Tasks (Base_CPU) := + Dispatching_Domain_Tasks (Base_CPU) + 1; + end if; + -- Only initialize the first element since others are not relevant -- in ravenscar mode. Rest of the initialization is done in Init_RTS. Index: s-taskin.ads =================================================================== --- s-taskin.ads (revision 178381) +++ s-taskin.ads (working copy) @@ -394,8 +394,44 @@ type Dispatching_Domain_Access is access Dispatching_Domain; System_Domain : Dispatching_Domain_Access; - -- All processors belong to default system dispatching domain at start up + -- All processors belong to default system dispatching domain at start up. + -- We use a pointer which creates the actual variable for the reasons + -- explained bellow in Dispatching_Domain_Tasks. + Dispatching_Domains_Frozen : Boolean := False; + -- True when the main procedure has been called. Hence, no new dispatching + -- domains can be created when this flag is True. + + type Array_Allocated_Tasks is + array (System.Multiprocessors.CPU range <>) of Natural; + -- At start-up time, we need to store the number of tasks attached to + -- concrete processors within the system domain (we can only create + -- dispatching domains with processors belonging to the system domain and + -- without tasks allocated). + + type Array_Allocated_Tasks_Access is access Array_Allocated_Tasks; + + Dispatching_Domain_Tasks : Array_Allocated_Tasks_Access; + -- We need to store whether there are tasks allocated to concrete + -- processors in the default system dispatching domain because we need to + -- check it before creating a new dispatching domain. Two comments about + -- the reason why we use a pointer here and not in package + -- Dispatching_Domains. + -- 1) We use an array created dynamically in procedure Initialize which is + -- called at the beginning of the initialization of the run-time library. + -- Declaring a static array here in the spec would not work across + -- different installations because it would get the value of Number_Of_CPUs + -- from the machine where the run-time library is built, and not from the + -- machine where the application is executed. That is the reason why we + -- create the array (CPU'First .. Number_Of_CPUs) at execution time in the + -- procedure body, ensuring that the function Number_Of_CPUs is executed at + -- execution time (the same trick as we use for System_Domain). + -- 2) We have moved this declaration from package Dispatching_Domains + -- because when we use a pragma CPU, the affinity is passed through the + -- call to Create_Task. Hence, at this point, we may need to update the + -- number of tasks associated to the processor, but we do not want to force + -- a dependency from this package on Dispatching_Domains. + ------------------------------------ -- Task related other definitions -- ------------------------------------