--- ai12s/ai12-0321-1.txt 2019/03/12 04:55:54 1.4 +++ ai12s/ai12-0321-1.txt 2019/03/15 05:39:29 1.5 @@ -85,7 +85,7 @@ An atomic flag object can either be considered to be set or cleared. Atomic_Test_And_Set performs an atomic test-and-set operation on Item. -Item is set to some implementation defined nonzero value. The function returns +Item is set to some implementation-defined nonzero value. The function returns True if the previous contents were nonzero, and otherwise returns False. Atomic_Clear performs an atomic clear operation on Item. After the @@ -94,13 +94,13 @@ C.6.4 The Package System.Atomic_Operations.Arithmetic -The language-defined package System.Atomic_Operations.Arithmetic +The language-defined generic package System.Atomic_Operations.Arithmetic provides operations to perform arithmetic atomically on objects of -modular types. +integer types. Static Semantics -The library package System.Atomic_Operations.Arithmetic has the +The generic library package System.Atomic_Operations.Arithmetic has the following declaration: generic @@ -131,32 +131,33 @@ end System.Atomic_Operations.Arithmetic; +The operations of this package are defined as follows: - procedure Atomic_Add (Item : aliased in out Atomic_Type; - Value : Atomic_Type) - with Intrinsic; +procedure Atomic_Add (Item : aliased in out Atomic_Type; + Value : Atomic_Type) + with Convention => Intrinsic; - Atomically performs: Item := Item + Value; + Atomically performs: Item := Item + Value; - procedure Atomic_Subtract (Item : aliased in out Atomic_Type; - Value : Atomic_Type) - with Intrinsic; +procedure Atomic_Subtract (Item : aliased in out Atomic_Type; + Value : Atomic_Type) + with Convention => Intrinsic; - Atomically performs: Item := Item - Value; + Atomically performs: Item := Item - Value; - function Atomic_Fetch_And_Add (Item : aliased in out Atomic_Type; - Value : Atomic_Type) return Atomic_Type - with Intrinsic; +function Atomic_Fetch_And_Add (Item : aliased in out Atomic_Type; + Value : Atomic_Type) return Atomic_Type + with Convention => Intrinsic; - Atomically performs: Tmp := Item; Item := Item + Value; return Tmp; + Atomically performs: Tmp := Item; Item := Item + Value; return Tmp; - function Atomic_Fetch_And_Subtract (Item : aliased in out Atomic_Type; - Value : Atomic_Type) return Atomic_Type - with Intrinsic; +function Atomic_Fetch_And_Subtract (Item : aliased in out Atomic_Type; + Value : Atomic_Type) return Atomic_Type + with Convention => Intrinsic; - Atomically performs: Tmp := Item; Item := Item - Value; return Tmp; + Atomically performs: Tmp := Item; Item := Item - Value; return Tmp; !discussion @@ -177,6 +178,118 @@ for overflow checks, so checking for overflow is needed. But if extra performance is needed, those checks can be suppressed. +!corrigendum C.6.3 + +@dinsc + +The language-defined package System.Atomic_Operations.Test_And_Set +provides an operation to atomically set and clear an atomic flag object. + +@s8<@i<Static Semantics>> + +The library package System.Atomic_Operations.Test_And_Set has the +following declaration: + +@xcode<@b<package> System.Atomic_Operations.Test_And_Set + @b<with> Pure, Nonblocking @b<is>> + +@xcode< @b<type> Test_And_Set_Flag @b<is mod> @ft<@i<implementation-defined>> + @b<with> Atomic, Default_Value =@> 0, Size =@> @ft<@i<implementation-defined>>;> + +@xcode< @b<function> Atomic_Test_And_Set + (Item : @b<aliased in out> Test_And_Set_Flag) @b<return> Boolean + @b<with> Convention =@> Intrinsic;> + +@xcode< @b<procedure> Atomic_Clear + (Item : @b<aliased in out> Test_And_Set_Flag) + @b<with> Convention =@> Intrinsic;> + +@xcode< @b<function> Is_Lock_Free + (Item : @b<aliased> Test_And_Set_Flag) @b<return> Boolean + @b<with> Convention =@> Intrinsic;> + +@xcode<@b<end> System.Atomic_Operations.Test_And_Set;> + +Test_And_Set_Flag represents the state of an atomic flag object. +An atomic flag object can either be considered to be set or cleared. + +Atomic_Test_And_Set performs an atomic test-and-set operation on Item. +Item is set to some implementation-defined nonzero value. The function returns +True if the previous contents were nonzero, and otherwise returns False. + +Atomic_Clear performs an atomic clear operation on Item. After the +operation, Item contains 0. This call should be used in conjunction with +Atomic_Test_And_Set. + +!corrigendum C.6.4 + +@dinsc + +The language-defined generic package System.Atomic_Operations.Arithmetic +provides operations to perform arithmetic atomically on objects of +integer types. + +@s8<@i<Static Semantics>> + +The generic library package System.Atomic_Operations.Arithmetic has the +following declaration: + +@xcode<@b<generic> + @b<type> Atomic_Type @b<is range> <@> @b<with> Atomic; +@b<package> System.Atomic_Operations.Arithmetic + @b<with> Pure, Nonblocking @b<is>> + +@xcode< @b<procedure> Atomic_Add (Item : @b<aliased in out> Atomic_Type; + Value : Atomic_Type) + @b<with> Convention =@> Intrinsic;> + +@xcode< @b<procedure> Atomic_Subtract (Item : @b<aliased in out> Atomic_Type; + Value : Atomic_Type) + @b<with> Convention =@> Intrinsic;> + +@xcode< @b<function> Atomic_Fetch_And_Add + (Item : @b<aliased in out> Atomic_Type; + Value : Atomic_Type) @b<return> Atomic_Type + @b<with> Convention =@> Intrinsic;> + +@xcode< @b<function> Atomic_Fetch_And_Subtract + (Item : @b<aliased in out> Atomic_Type; + Value : Atomic_Type) @b<return> Atomic_Type + @b<with> Convention =@> Intrinsic;> + +@xcode< @b<function> Is_Lock_Free (Item : @b<aliased> Atomic_Type) @b<return> Boolean + @b<with> Convention =@> Intrinsic;> + +@xcode<@b<end> System.Atomic_Operations.Arithmetic;> + +The operations of this package are defined as follows: + +@xcode<@b<procedure> Atomic_Add (Item : @b<aliased in out> Atomic_Type; + Value : Atomic_Type) + @b<with> Convention =@> Intrinsic;> + +@xindent<Atomically performs: @fc<Item := Item + Value;>> + +@xcode<@b<procedure> Atomic_Subtract (Item : @b<aliased in out> Atomic_Type; + Value : Atomic_Type) + @b<with> Convention =@> Intrinsic;> + +@xindent<Atomically performs: @fc<Item := Item - Value;>> + +@xcode<@b<function> Atomic_Fetch_And_Add + (Item : @b<aliased in out> Atomic_Type; + Value : Atomic_Type) @b<return> Atomic_Type + @b<with> Convention =@> Intrinsic;> + +@xindent<Atomically performs: @fc<Tmp := Item; Item := Item + Value; @b<return> Tmp;>> + +@xcode<@b<function> Atomic_Fetch_And_Subtract + (Item : @b<aliased in out> Atomic_Type; + Value : Atomic_Type) @b<return> Atomic_Type + @b<with> Convention =@> Intrinsic;> + +@xindent<Atomically performs: @fc<Tmp := Item; Item := Item - Value; @b<return> Tmp;>> + !ASIS No ASIS effect. @@ -276,6 +389,367 @@ Using signed arithmetic means there is a need to check for arithmetic overflow, but that can be done without looping, and if performance is an issue, the overflow checks can be suppressed. + +**************************************************************** + +From: Brad Moore +Sent: Wednesday, March 13, 2019 12:22 AM + +In the recent electronic ARG meeting, we were discussing the +Atomic_Operations.Arithmetic package. + +It was mentioned that the Bitwise operations that were defined in AI12-0321-1 +did not make sense if the generic formal type is a signed integer type, so we +decided to drop those subprograms from the AI. The thought was that Atomic +Addition and Subtraction is likely the most common need to support, +and that Bitwise operations seemed to be much less of a need. + +I mentioned that prior to Saturday, the generic formal of the Arithmetic +package was a modular type, but that I had changed it to signed integer, +due to perceived issues with dealing with addition and subtraction, when the +modulus of the generic formal type was not a multiple of the machines storage +element size. + +The decision to have the Arithmetic package only support the Add and Subtract, +still makes sense to me. + +But I am now thinking we were a bit too hasty in tossing out the bitwise +subprograms. + +After thinking about it, I think atomically modifying and testing bits +atomically is probably something that could be quite useful. + +Something I might have used for example, in my Paraffin libraries. + +If we wanted to support atomic bitwise operations, I think we'd want to limit +the support to modular types whose modulus is a power of two. + +That way, one cannot get into trouble setting or clearing bits of the generic +formal modular type. +All bit patterns (for values less than the 2**modulus) are valid bit patterns, +so there would be no need to add additional checks on the results of these +operations. + +Fortunately, that restriction could easily be enforced by placing the +following assert in the generic package. + + -- Only Modular types whose modulus is a power of 2 are allowed + pragma Assert ((Atomic_Type'Base (Atomic_Type'Modulus) and + Atomic_Type'Base (Atomic_Type'Modulus - 1)) = 0); + +Since adding these functions would be very similar to the wording for adding +the Atomic_Operations.Arithmetic package, I think we should consider adding +the following which supports three operations, "or", "and", and "xor"; + +generic + type Atomic_Type is mod <> with Atomic; +package System.Atomic_Operations.Bitwise with Pure, Nonblocking +is + + -- Only Modular types whose modulus is a power of 2 are allowed + pragma Assert ((Atomic_Type'Base (Atomic_Type'Modulus) and + Atomic_Type'Base (Atomic_Type'Modulus - 1)) = 0); + + + procedure Atomic_Bitwise_And (Item : aliased in out Atomic_Type; + Value : Atomic_Type) + with Convention => Intrinsic; + + procedure Atomic_Bitwise_Or (Item : aliased in out Atomic_Type; + Value : Atomic_Type) + with Convention => Intrinsic; + + procedure Atomic_Bitwise_Xor (Item : aliased in out Atomic_Type; + Value : Atomic_Type) + with Convention => Intrinsic; + + + function Atomic_Fetch_And_Bitwise_And + (Item : aliased in out Atomic_Type; + Value : Atomic_Type) return Atomic_Type + with Convention => Intrinsic; + + function Atomic_Fetch_And_Bitwise_Or + (Item : aliased in out Atomic_Type; + Value : Atomic_Type) return Atomic_Type + with Convention => Intrinsic; + + function Atomic_Fetch_And_Bitwise_Xor + (Item : aliased in out Atomic_Type; + Value : Atomic_Type) return Atomic_Type + with Convention => Intrinsic; + + function Is_Lock_Free + (Item : aliased Atomic_Type) return Boolean + with Convention => Intrinsic, Nonblocking + +end System.Atomic_Operations.Bitwise; + +I'd be happy to write this up (likely as a new AI if it cant be added to +AI12-0321-1. + +Thoughts? + +*************************************************************** + +From: Randy Brukardt +Sent: Wednesday, March 13, 2019 12:49 AM + +... +> Fortunately, that restriction could easily be enforced by placing the +> following assert in the generic package. +> +> -- Only Modular types whose modulus is a power of 2 are allowed +> pragma Assert ((Atomic_Type'Base (Atomic_Type'Modulus) and +> Atomic_Type'Base (Atomic_Type'Modulus - 1)) = 0); + +I think this Assertion always raises Constraint_Error (or is illegal). +Atomic_Type'Modulus is a universal_integer value, and those do not wrap. +For all T, T'Modulus > T'Last. Ergo, T'Modulus is out of range. If T'Modulus +was static (as it might be in an instance), then the type conversion is +illegal. + +You need to somehow use 'Mod. However, T'Mod(T'Modulus) is always 0. + +I've usually had to declare a Big_Mod type for this sort of thing: + type Big_Mod is mod System.Max_Binary_Modulus; +and then you can write your expression, so long as you pretest for modulii +that are too large. Which is a royal mess. :-) + +Anyway, you need to go back to the drawing board with this assertion. + +**************************************************************** + +From: Brad Moore +Sent: Wednesday, March 13, 2019 1:32 AM + +> I think this Assertion always raises Constraint_Error (or is illegal). +> Atomic_Type'Modulus is a universal_integer value, and those do not +> wrap. For all T, T'Modulus > T'Last. Ergo, T'Modulus is out of range. +> If T'Modulus was static (as it might be in an instance), then the type +> conversion is illegal. + +I knew that Atomic_Type'Modulus is a universal_integer value, which is why +I converted that result to the Atomic_Type'Base subtype, which allows me to +do the bitwise and operation. + +I tried this with GNAT, and it compiles, and works fine in my test program. + +When I try it with a type such as + +type Mod_5 is mod 5; + +It correctly tells me that an exception will be raised at runtime. +(since the modulus of that subtype is not a power of 2) + +**************************************************************** + +From: Tucker Taft +Sent: Wednesday, March 13, 2019 8:40 AM + +In any case, I think we should leave these out for now, as they are less +critical than add/subtract. + +It is easy enough to write such a package yourself if you know what you are +doing. Here we are looking for widely useful capabilities that need +standardization. + +**************************************************************** + +From: Randy Brukardt +Sent: Wednesday, March 13, 2019 3:41 PM + +> I knew that Atomic_Type'Modulus is a universal_integer value, which is +> why I converted that result to the Atomic_Type'Base subtype, which +> allows me to do the bitwise and operation. + +Yes, and that conversion always raises Constraint_Error (or is illegal if the +type is static). + +> I tried this with GNAT, and it compiles, and works fine in my test +> program. + +Either you didn't actually test what you think you did, or GNAT is wrong. +4.6(28) clearly states that Constraint_Error is raised for a value outside of +the base range of a modular type. T'Modulus is by definition outside of the +base range of T. And 4.9 states that a static expression is illegal if it +raises any exception other than an overflow. + +Now, I know that if you compiled this with Janus/Ada, it would in fact work +with at least some types, but that would be because Janus/Ada doesn't +properly handle these conversions when found in a generic. In particular, +T'Modulus of mod 2**32 has the special value zero at runtime so we can handle +such types correctly, but that would of course cause oddities if actually used +explicitly. But just because it would work doesn't make it right. + +So I wouldn't be surprised if it accidentally worked for Max_Binary_Modulus, +but I wouldn't take that as some sort of assumption that it should work. I'd +suggest trying it in a non-generic case to see. + +I'm pretty sure that there is an ACATS test that checks this rule, but perhaps +it could use some beefing up. + +> When I try it with a type such as +> +> type Mod_5 is mod 5; +> +> It correctly tells me that an exception will be raised at runtime. +> (since the modulus of that subtype is not a power of 2) + +Surely this is true, since it *always* raises Constraint_Error. + +**************************************************************** + +From: Tucker Taft +Sent: Wednesday, March 13, 2019 4:30 PM + +Another test that should work, I believe, is: + + pragma Assert (T'Modulus = 2 ** T'Size); + +unless the 'Size has been explicitly specified to be larger. + +Or if you want to go whole hog: + + pragma Assert((for some I in 1..T'Size => T'Modulus = 2**I)); + +though now you are getting into non-static expressions, so Constraint_Error +becomes more likely if you bump up against the Max_Integer limits of run-time +universal-int computations. + +;-) + +**************************************************************** + +From: Bob Duff +Sent: Wednesday, March 13, 2019 6:00 PM + +> pragma Assert((for some I in 1..T'Size => T'Modulus = 2**I)); + +For "type T is mod 1;", T'Size = 0, 2**T'Size = 1, and T'Modulus = 1. + +;-) + +Note to Brad: Unlike signed integers, the base range of a modular type is +widened beyond the first subtype. + +**************************************************************** + +From: Tucker Taft +Sent: Wednesday, March 13, 2019 7:27 PM + +Yes, I realized that, but I was somehow unable to allow myself to include the +"mod 1" option. ;-) + +**************************************************************** + +From: Tucker Taft +Sent: Wednesday, March 13, 2019 7:33 PM + +> Note to Brad: Unlike signed integers, the base range of a modular +> type is widened beyond the first subtype. + +What does this mean? Did you mean the base range is *not* widened? + +**************************************************************** + +From: Bob Duff +Sent: Wednesday, March 13, 2019 8:12 PM + +Yes, thanks for the correction. + +**************************************************************** + +From: Brad Moore +Sent: Wednesday, March 13, 2019 11:30 PM + +Thanks for the explanations. + +By the way, the reason I thought the following example was OK, is because +I didn't have the "enable assertions" checkbox checked, for the project file. + +Oddly, I do get a warning if I uncomment the assert below, which says that +an assertion would fail at run time. But I don't get any warnings otherwise, +and the code runs successfully. + +If I enable assertions, then the code does not compile, as Randy said. + + +with Ada.Text_IO; +use Ada.Text_IO; + +procedure Main is + type Mod_8 is mod 2**3; + type Mod_5 is mod 5; + + type Mod_16 is mod 2**4; + + type Mod_32 is mod 2**5; + + pragma Assert ((Mod_8'Base (Mod_8'Modulus) and + Mod_8'Base (Mod_8'Modulus - 1)) = 0); + + pragma Assert ((Mod_16'Base (Mod_16'Modulus) and + Mod_16'Base (Mod_16'Modulus - 1)) = 0); + + pragma Assert ((Mod_32'Base (Mod_32'Modulus) and + Mod_32'Base (Mod_32'Modulus - 1)) = 0); + +-- pragma Assert (((Mod_5'Base (Mod_5'Modulus) and +-- Mod_5'Base (Mod_5'Modulus - 1)) = 0)); + + X : Mod_8; + X2 : Mod_16; + X3 : Mod_32; +begin + + X := 4; + X := X + 7; + X2 := 4; + X2 := X2 + 14; + X3 := 5; + X3 := X3 + 30; + + Put_Line ("Mod8:" & Mod_8'Image (X)); + Put_Line ("Mod16:" & Mod_16'Image (X2)); + Put_Line ("Mod32:" & Mod_32'Image (X3)); + +end Main; + +**************************************************************** + +From: Brad Moore +Sent: Wednesday, March 13, 2019 11:39 PM + +Thanks for the suggestion, that works (the first simpler version, at least) +for my example. A nice concise way to express this property. + +**************************************************************** + +From: Randy Brukardt +Sent: Thursday, March 14, 2019 7:21 PM + +> > pragma Assert((for some I in 1..T'Size => T'Modulus = 2**I)); +> +> For "type T is mod 1;", T'Size = 0, 2**T'Size = 1, and T'Modulus = 1. +> +> ;-) + +Tucker noted that it isn't a very good assertion, since someone could +declare: + + type Biggie is mod 256 with Atomic, Size => 16; + +OTOH, we could decide to explicitly not care about such corner cases. + +> Note to Brad: Unlike signed integers, the base range of a modular +> type is widened beyond the first subtype. + +Every time I read this, I'm confused. I even went and looked it up in the RM +to be sure. I think there is a "not" missing somewhere. Perhaps you meant: + + Note to Brad: Unlike signed integers, the base range of a + modular type is NOT widened beyond the first subtype. ****************************************************************

Questions? Ask the ACAA Technical Agent