Overview
Comment: | references-pools: new package that provides a task-safe pool of references |
---|---|
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
f5300980040c16d71c07b622085cb380 |
User & Date: | nat on 2014-08-26 20:50:16 |
Other Links: | manifest | tags |
Context
2014-08-27
| ||
19:43 | reference_tests-pools: new test suite for reference pools check-in: 2fa0bb02a5 user: nat tags: trunk | |
2014-08-26
| ||
20:50 | references-pools: new package that provides a task-safe pool of references check-in: f530098004 user: nat tags: trunk | |
2014-08-25
| ||
19:57 | reference_tests: also test the new Is_Last function check-in: 111a93ca40 user: nat tags: trunk | |
Changes
Added src/natools-references-pools.adb version [0b765e01db].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ------------------------------------------------------------------------------ -- Copyright (c) 2014, Natacha Porté -- -- -- -- Permission to use, copy, modify, and distribute this software for any -- -- purpose with or without fee is hereby granted, provided that the above -- -- copyright notice and this permission notice appear in all copies. -- -- -- -- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -- -- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -- -- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -- -- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -- -- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -- -- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -- -- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -- ------------------------------------------------------------------------------ package body Natools.References.Pools is ------------------------ -- Helper Subprograms -- ------------------------ overriding procedure Finalize (Object : in out Pool_Backend) is begin Unchecked_Free (Object.Refs); end Finalize; procedure Find (Container : in Pool_Backend; First_Available : out Extended_Index; First_Empty : out Extended_Index) is begin First_Available := 0; First_Empty := 0; if Container.Refs = null then return; end if; for I in Container.Refs'Range loop if Container.Refs (I).Is_Empty then if First_Empty = 0 then First_Empty := I; exit when First_Available /= 0; end if; elsif Container.Refs (I).Is_Last then if First_Available = 0 then First_Available := I; exit when First_Empty /= 0; end if; end if; end loop; end Find; not overriding procedure Preallocate (Container : in out Pool_Backend; New_Item_Count : in Pool_Size; Constructor : access function return Held_Data := null) is begin if New_Item_Count = 0 then return; end if; if Container.Refs = null then Container.Refs := new Reference_Array (1 .. New_Item_Count); if Constructor /= null then for I in Container.Refs'Range loop Container.Refs (I) := Create (Constructor); end loop; end if; else declare New_Data : Reference_Array_Access := new Reference_Array (1 .. Container.Refs'Length + New_Item_Count); begin New_Data (1 .. Container.Refs'Length) := Container.Refs.all; if Constructor /= null then for I in Container.Refs'Length + 1 .. New_Data'Last loop New_Data (I) := Create (Constructor); end loop; end if; Unchecked_Free (Container.Refs); Container.Refs := New_Data; exception when others => Unchecked_Free (New_Data); raise; end; end if; end Preallocate; ---------------------------------- -- Public Protected Subprograms -- ---------------------------------- protected body Pool is procedure Get (Ref : out Reference) is First_Available, First_Empty : Extended_Index; begin Backend.Find (First_Available, First_Empty); if First_Available in Reference_Index then Ref := Backend.Refs (First_Available); else raise Constraint_Error with "No non-empty unused reference in pool"; end if; end Get; procedure Get (Constructor : not null access function return Held_Data; Ref : out Reference) is First_Available, First_Empty : Extended_Index; begin Backend.Find (First_Available, First_Empty); if First_Available in Reference_Index then Ref := Backend.Refs (First_Available); elsif First_Empty in Reference_Index then Backend.Refs (First_Empty) := Create (Constructor); Ref := Backend.Refs (First_Empty); else raise Constraint_Error with "No unused reference in pool"; end if; end Get; procedure Create (Constructor : not null access function return Held_Data; Ref : out Reference; Expand_Count : in Pool_Size := 1) is First_Available, First_Empty : Extended_Index; begin Backend.Find (First_Available, First_Empty); if First_Available in Reference_Index then Ref := Backend.Refs (First_Available); elsif First_Empty in Reference_Index then Backend.Refs (First_Empty) := Create (Constructor); Ref := Backend.Refs (First_Empty); else First_Available := Backend.Length + 1; Backend.Preallocate (Expand_Count, Constructor); Ref := Backend.Refs (First_Available); end if; end Create; procedure Preallocate (New_Item_Count : in Pool_Size; Constructor : access function return Held_Data := null) is begin Backend.Preallocate (New_Item_Count, Constructor); end Preallocate; procedure Release_Unused is begin if Backend.Refs = null then return; end if; for I in Backend.Refs'Range loop if not Backend.Refs (I).Is_Empty and then Backend.Refs (I).Is_Last then Backend.Refs (I).Reset; end if; end loop; end Release_Unused; procedure Trim is Index : Extended_Index := 0; New_Count : constant Pool_Size := Initialized_Size; New_Data : Reference_Array_Access := null; begin if New_Count = Backend.Length then return; end if; New_Data := new Reference_Array (1 .. New_Count); for I in Backend.Refs'Range loop if not Backend.Refs (I).Is_Empty then Index := Index + 1; New_Data (Index) := Backend.Refs (I); end if; end loop; pragma Assert (Index = New_Count); Unchecked_Free (Backend.Refs); Backend.Refs := New_Data; exception when others => Unchecked_Free (New_Data); raise; end Trim; procedure Purge is begin Release_Unused; Trim; end Purge; function Capacity return Pool_Size is begin return Backend.Length; end Capacity; function Initialized_Size return Pool_Size is Result : Pool_Size := 0; begin if Backend.Refs /= null then for I in Backend.Refs'Range loop if not Backend.Refs (I).Is_Empty then Result := Result + 1; end if; end loop; end if; return Result; end Initialized_Size; function Active_Size return Pool_Size is Result : Pool_Size := 0; begin if Backend.Refs /= null then for I in Backend.Refs'Range loop if not Backend.Refs (I).Is_Empty and then not Backend.Refs (I).Is_Last then Result := Result + 1; end if; end loop; end if; return Result; end Active_Size; procedure Unchecked_Iterate (Process : not null access procedure (Ref : in Reference)) is begin for I in Backend.Refs'Range loop Process.all (Backend.Refs (I)); end loop; end Unchecked_Iterate; end Pool; end Natools.References.Pools; |
Added src/natools-references-pools.ads version [788ce7a04c].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ------------------------------------------------------------------------------ -- Copyright (c) 2014, Natacha Porté -- -- -- -- Permission to use, copy, modify, and distribute this software for any -- -- purpose with or without fee is hereby granted, provided that the above -- -- copyright notice and this permission notice appear in all copies. -- -- -- -- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -- -- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -- -- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -- -- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -- -- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -- -- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -- -- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -- ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ -- Natools.References.Pools provides a task-safe dynamic pool of -- -- homogeneous references. -- ------------------------------------------------------------------------------ private with Ada.Unchecked_Deallocation; generic package Natools.References.Pools is pragma Preelaborate; type Pool_Backend is limited private; pragma Preelaborable_Initialization (Pool_Backend); type Pool_Size is new Natural; protected type Pool is procedure Get (Ref : out Reference) with Post => not Ref.Is_Empty; -- Return an existing non-empty available reference from the pool, -- raising Constraint_Error when not possible. procedure Get (Constructor : not null access function return Held_Data; Ref : out Reference) with Post => not Ref.Is_Empty; -- Return an available reference from the pool, initializing it -- if needed, but without expanding the pool. -- Raise Constraint_Error when all references are in use. procedure Create (Constructor : not null access function return Held_Data; Ref : out Reference; Expand_Count : in Pool_Size := 1) with Pre => Expand_Count > 0, Post => not Ref.Is_Empty; -- Return a reference from the pool, creating it and/or initializing -- it if needed. procedure Preallocate (New_Item_Count : in Pool_Size; Constructor : access function return Held_Data := null); -- Add New_Item_Count references to the pool, using Constructor to -- initialize them if not null. procedure Release_Unused; -- Empty all references from the pool that are not used externally procedure Trim; -- Remove empty references from the pool, diminishing its capacity procedure Purge; -- Remove empty and available references from the pool. -- Equivalent to Release_Unused followed by Trim. function Capacity return Pool_Size; -- Return the number of references in the pool function Initialized_Size return Pool_Size; -- Return the number of non-empty references in the pool function Active_Size return Pool_Size; -- Return the number of externally-used references in the pool. -- WARNING: the result might be stale before it can be used by the -- client, do not take any sensitive decision from it. procedure Unchecked_Iterate (Process : not null access procedure (Ref : in Reference)); -- Iterate over all references held in the pool. -- WARNING: Process must not call any potentially blocking operations -- or any operation on the current pool, and safety of any tampering -- with Ref or its referred object must be ensured independently. private Backend : Pool_Backend; end Pool; private -- Basic types subtype Reference_Index is Pool_Size range 1 .. Pool_Size'Last; subtype Extended_Index is Pool_Size range 0 .. Pool_Size'Last; type Reference_Array is array (Reference_Index range <>) of Reference; type Reference_Array_Access is access Reference_Array; procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Reference_Array, Reference_Array_Access); -- Dynamic array backend type Pool_Backend is new Ada.Finalization.Limited_Controlled with record Refs : Reference_Array_Access := null; end record; overriding procedure Finalize (Object : in out Pool_Backend); not overriding procedure Find (Container : in Pool_Backend; First_Available : out Extended_Index; First_Empty : out Extended_Index) with Post => (First_Available = 0 or else (not Container.Refs (First_Available).Is_Empty and then Container.Refs (First_Available).Is_Last)) and then (First_Empty = 0 or else Container.Refs (First_Empty).Is_Empty); not overriding function Length (Container : Pool_Backend) return Pool_Size is (if Container.Refs = null then 0 else Container.Refs'Length); not overriding procedure Preallocate (Container : in out Pool_Backend; New_Item_Count : in Pool_Size; Constructor : access function return Held_Data := null) with Post => (Container.Length = Container.Length'Old + New_Item_Count); end Natools.References.Pools; |