----------------------------------------------------------------------- -- util-concurrent-arrays -- Concurrent Arrays -- Copyright (C) 2012, 2017, 2022 Stephane Carrez -- Written by Stephane Carrez (Stephane.Carrez@gmail.com) -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- -- http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. ----------------------------------------------------------------------- with Ada.Finalization; with Util.Concurrent.Counters; -- == Introduction == -- The Util.Concurrent.Arrays generic package defines an array which provides a -- concurrent read only access and a protected exclusive write access. This implementation -- is intended to be used in applications that have to frequently iterate over the array -- content. Adding or removing elements in the array is assumed to be a not so frequent -- operation. Based on these assumptions, updating the array is implemented by -- using the copy on write design pattern. Read access is provided through a -- reference object that can be shared by multiple readers. -- -- == Declaration == -- The package must be instantiated using the element type representing the array element. -- -- package My_Array is new Util.Concurrent.Arrays (Element_Type => Integer); -- -- == Adding Elements == -- The vector instance is declared and elements are added as follows: -- -- C : My_Array.Vector; -- -- C.Append (E1); -- -- == Iterating over the array == -- To read and iterate over the vector, a task will get a reference to the vector array -- and it will iterate over it. The reference will be held until the reference object is -- finalized. While doing so, if another task updates the vector, a new vector array will -- be associated with the vector instance (but this will not change the reader's references). -- -- R : My_Array.Ref := C.Get; -- -- R.Iterate (Process'Access); -- ... -- R.Iterate (Process'Access); -- -- In the above example, the two `Iterate` operations will iterate over the same list of -- elements, even if another task appends an element in the middle. -- -- Notes: -- * This package is close to the Java class `java.util.concurrent.CopyOnWriteArrayList`. -- -- * The package implements voluntarily a very small subset of `Ada.Containers.Vectors`. -- -- * The implementation does not use the Ada container for performance and size reasons. -- -- * The `Iterate` and `Reverse_Iterate` operation give a direct access to the element. generic type Element_Type is private; with function "=" (Left, Right : in Element_Type) return Boolean is <>; package Util.Concurrent.Arrays is -- The reference to the read-only vector elements. type Ref is tagged private; -- Returns True if the container is empty. function Is_Empty (Container : in Ref) return Boolean; -- Iterate over the vector elements and execute the Process procedure -- with the element as parameter. procedure Iterate (Container : in Ref; Process : not null access procedure (Item : in Element_Type)); -- Iterate over the vector elements in reverse order and execute the Process procedure -- with the element as parameter. procedure Reverse_Iterate (Container : in Ref; Process : not null access procedure (Item : in Element_Type)); -- Vector of elements. type Vector is new Ada.Finalization.Limited_Controlled with private; -- Get a read-only reference to the vector elements. The referenced vector will never -- be modified. function Get (Container : in Vector'Class) return Ref; -- Append the element to the vector. The modification will not be visible to readers -- until they call the Get function. procedure Append (Container : in out Vector; Item : in Element_Type); -- Remove the element represented by Item from the vector. The modification will -- not be visible to readers until they call the Get function. procedure Remove (Container : in out Vector; Item : in Element_Type); -- Release the vector elements. overriding procedure Finalize (Object : in out Vector); private -- To store the vector elements, we use an array which is allocated dynamically. -- The generated code is smaller compared to the use of Ada vectors container. type Element_Array is array (Positive range <>) of Element_Type; type Element_Array_Access is access all Element_Array; Null_Element_Array : constant Element_Array_Access := null; type Vector_Record (Len : Positive) is record Ref_Counter : Util.Concurrent.Counters.Counter; List : Element_Array (1 .. Len); end record; type Vector_Record_Access is access all Vector_Record; type Ref is new Ada.Finalization.Controlled with record Target : Vector_Record_Access := null; end record; -- Release the reference. Invoke Finalize and free the storage if it was -- the last reference. overriding procedure Finalize (Obj : in out Ref); -- Update the reference counter after an assignment. overriding procedure Adjust (Obj : in out Ref); -- Vector of objects protected type Protected_Vector is -- Get a readonly reference to the vector. function Get return Ref; -- Append the element to the vector. procedure Append (Item : in Element_Type); -- Remove the element from the vector. procedure Remove (Item : in Element_Type); private Elements : Ref; end Protected_Vector; type Vector is new Ada.Finalization.Limited_Controlled with record List : Protected_Vector; end record; end Util.Concurrent.Arrays;