4.1.4. GNATCOLL.Atomic

package GNATCOLL.Atomic is

   subtype Atomic_Counter is System.Atomic_Counters.Atomic_Unsigned;

   Minus_One : constant Atomic_Counter :=
      System.Atomic_Counters."-" (0, 1);

   function Is_Lock_Free return Boolean;
   --  Whether the implementation uses the processor's atomic operations
   --  or falls back on using locks

   function Sync_Add_And_Fetch
     (Ptr   : access Atomic_Counter;
      Value : Atomic_Counter) return Atomic_Counter
     with Inline_Always;
   --  Increment Ptr by Value. This is task safe (either using a lock or
   --  intrinsic atomic operations). Returns the new value (as set, it
   --  might already have been changed by another task by the time this
   --  function returns.

   function Sync_Sub_And_Fetch
     (Ptr   : access Atomic_Counter;
      Value : Atomic_Counter) return Atomic_Counter
     with Inline_Always;
   --  Decrement Ptr by Value.

   procedure Sync_Add_And_Fetch
     (Ptr : access Atomic_Counter; Value : Atomic_Counter)
     with Inline_Always;
   procedure Sync_Sub_And_Fetch
     (Ptr : access Atomic_Counter; Value : Atomic_Counter)
     with Inline_Always;
   --  Same as above, but ignore the return value.

   procedure Increment
      (Value : aliased in out Atomic_Counter) with Inline_Always;
   procedure Decrement
      (Value : aliased in out Atomic_Counter) with Inline_Always;
   function Decrement
      (Value : aliased in out Atomic_Counter) return Boolean
      with Inline_Always;
   --  Similar to the Sync_Add_And_Fetch and Sync_Sub_And_And, but
   --  always increment or decrement by one.
   --  On some systems (x86) this uses faster assembly instructions.
   --  Decrement returns True if the value reaches 0.

   function "+"
      (Left, Right : Atomic_Counter) return Atomic_Counter is abstract;
   function "-"
      (Left, Right : Atomic_Counter) return Atomic_Counter is abstract;
   --  Prevent standard operations on these counters

   function Unsafe_Decrement (Value : in out Atomic_Counter) return Boolean
      with Inline_Always;
   procedure Unsafe_Increment (Value : in out Atomic_Counter)
      with Inline_Always;
   --  These are unsafe operations. If you have two threads, and they all try
   --  to do "Unsafe_Add (A, 2)" at the same time, when A was initially 0,
   --  you could end up with the following values in A:
   --      2 (both threads have read 0, then added 2)
   --      4 (thread 1 has read and incremented, then thread 2)
   --  If you use the other operations above, you always end up with 4.

   function ">" (Left, Right : Atomic_Counter) return Boolean
      is (System.Atomic_Counters.">" (Left, Right));
   --  Compare two counters.
   --  Note that by the time this function returns, and in a multi threaded
   --  application, either of the two counters might have changed.

   function "="
      (Left, Right : Atomic_Counter) return Boolean
      renames System.Atomic_Counters."=";
   --  Make the operator visible

   generic
      type Element_Type (<>) is limited private;
      type Element_Access is access Element_Type;
   function Sync_Bool_Compare_And_Swap
      (Ptr    : access Element_Access;
       Oldval : Element_Access;
       Newval : Element_Access) return Boolean;
   --  If Ptr is equal to Oldval, set it to Newval and return True.
   --  Otherwise, return False and do not modify the current value.
   --  This operation is task safe and atomic.

   function Sync_Bool_Compare_And_Swap_Counter
      (Ptr    : access Atomic_Counter;
       Oldval : Atomic_Counter;
       Newval : Atomic_Counter) return Boolean;
   function Sync_Val_Compare_And_Swap_Counter
      (Ptr    : access Atomic_Counter;
       Oldval : Atomic_Counter;
       Newval : Atomic_Counter) return Atomic_Counter;
   --  A version that works with Atomic_Counter.
   --  Ptr.all is set to Newval if and only if it is currently set to Oldval.
   --  Returns True if the value was changed.
   --  The second version returns the initial value of Ptr.all

end GNATCOLL.Atomic;