1------------------------------------------------------------------------------
2--                                                                          --
3--                         GNAT RUN-TIME COMPONENTS                         --
4--                                                                          --
5--                     S Y S T E M . P O O L _ S I Z E                      --
6--                                                                          --
7--                                 B o d y                                  --
8--                                                                          --
9--          Copyright (C) 1992-2019, Free Software Foundation, Inc.         --
10--                                                                          --
11-- GNAT is free software;  you can  redistribute it  and/or modify it under --
12-- terms of the  GNU General Public License as published  by the Free Soft- --
13-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
14-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
15-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16-- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
17--                                                                          --
18-- As a special exception under Section 7 of GPL version 3, you are granted --
19-- additional permissions described in the GCC Runtime Library Exception,   --
20-- version 3.1, as published by the Free Software Foundation.               --
21--                                                                          --
22-- You should have received a copy of the GNU General Public License and    --
23-- a copy of the GCC Runtime Library Exception along with this program;     --
24-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
25-- <http://www.gnu.org/licenses/>.                                          --
26--                                                                          --
27-- GNAT was originally developed  by the GNAT team at  New York University. --
28-- Extensive contributions were provided by Ada Core Technologies Inc.      --
29--                                                                          --
30------------------------------------------------------------------------------
31
32with System.Soft_Links;
33
34with Ada.Unchecked_Conversion;
35
36package body System.Pool_Size is
37
38   package SSE renames System.Storage_Elements;
39   use type SSE.Storage_Offset;
40
41   --  Even though these storage pools are typically only used by a single
42   --  task, if multiple tasks are declared at the same or a more nested scope
43   --  as the storage pool, there still may be concurrent access. The current
44   --  implementation of Stack_Bounded_Pool always uses a global lock for
45   --  protecting access. This should eventually be replaced by an atomic
46   --  linked list implementation for efficiency reasons.
47
48   package SSL renames System.Soft_Links;
49
50   type Storage_Count_Access is access SSE.Storage_Count;
51   function To_Storage_Count_Access is
52     new Ada.Unchecked_Conversion (Address, Storage_Count_Access);
53
54   SC_Size : constant := SSE.Storage_Count'Object_Size / System.Storage_Unit;
55
56   package Variable_Size_Management is
57
58      --  Embedded pool that manages allocation of variable-size data
59
60      --  This pool is used as soon as the Elmt_Size of the pool object is 0
61
62      --  Allocation is done on the first chunk long enough for the request.
63      --  Deallocation just puts the freed chunk at the beginning of the list.
64
65      procedure Initialize  (Pool : in out Stack_Bounded_Pool);
66      procedure Allocate
67        (Pool         : in out Stack_Bounded_Pool;
68         Address      : out System.Address;
69         Storage_Size : SSE.Storage_Count;
70         Alignment    : SSE.Storage_Count);
71
72      procedure Deallocate
73        (Pool         : in out Stack_Bounded_Pool;
74         Address      : System.Address;
75         Storage_Size : SSE.Storage_Count;
76         Alignment    : SSE.Storage_Count);
77   end Variable_Size_Management;
78
79   package Vsize renames Variable_Size_Management;
80
81   --------------
82   -- Allocate --
83   --------------
84
85   procedure Allocate
86     (Pool         : in out Stack_Bounded_Pool;
87      Address      : out System.Address;
88      Storage_Size : SSE.Storage_Count;
89      Alignment    : SSE.Storage_Count)
90   is
91   begin
92      SSL.Lock_Task.all;
93
94      if Pool.Elmt_Size = 0 then
95         Vsize.Allocate (Pool, Address, Storage_Size, Alignment);
96
97      elsif Pool.First_Free /= 0 then
98         Address := Pool.The_Pool (Pool.First_Free)'Address;
99         Pool.First_Free := To_Storage_Count_Access (Address).all;
100
101      elsif
102        Pool.First_Empty <= (Pool.Pool_Size - Pool.Aligned_Elmt_Size + 1)
103      then
104         Address := Pool.The_Pool (Pool.First_Empty)'Address;
105         Pool.First_Empty := Pool.First_Empty + Pool.Aligned_Elmt_Size;
106
107      else
108         raise Storage_Error;
109      end if;
110
111      SSL.Unlock_Task.all;
112
113   exception
114      when others =>
115         SSL.Unlock_Task.all;
116         raise;
117   end Allocate;
118
119   ----------------
120   -- Deallocate --
121   ----------------
122
123   procedure Deallocate
124     (Pool         : in out Stack_Bounded_Pool;
125      Address      : System.Address;
126      Storage_Size : SSE.Storage_Count;
127      Alignment    : SSE.Storage_Count)
128   is
129   begin
130      SSL.Lock_Task.all;
131
132      if Pool.Elmt_Size = 0 then
133         Vsize.Deallocate (Pool, Address, Storage_Size, Alignment);
134
135      else
136         To_Storage_Count_Access (Address).all := Pool.First_Free;
137         Pool.First_Free := Address - Pool.The_Pool'Address + 1;
138      end if;
139
140      SSL.Unlock_Task.all;
141   exception
142      when others =>
143         SSL.Unlock_Task.all;
144         raise;
145   end Deallocate;
146
147   ----------------
148   -- Initialize --
149   ----------------
150
151   procedure Initialize (Pool : in out Stack_Bounded_Pool) is
152
153      --  Define the appropriate alignment for allocations. This is the
154      --  maximum of the requested alignment, and the alignment required
155      --  for Storage_Count values. The latter test is to ensure that we
156      --  can properly reference the linked list pointers for free lists.
157
158      Align : constant SSE.Storage_Count :=
159                SSE.Storage_Count'Max
160                  (SSE.Storage_Count'Alignment, Pool.Alignment);
161
162   begin
163      if Pool.Elmt_Size = 0 then
164         Vsize.Initialize (Pool);
165
166      else
167         Pool.First_Free := 0;
168         Pool.First_Empty := 1;
169
170         --  Compute the size to allocate given the size of the element and
171         --  the possible alignment requirement as defined above.
172
173         Pool.Aligned_Elmt_Size :=
174           SSE.Storage_Count'Max (SC_Size,
175             ((Pool.Elmt_Size + Align - 1) / Align) * Align);
176      end if;
177   end Initialize;
178
179   ------------------
180   -- Storage_Size --
181   ------------------
182
183   function Storage_Size
184     (Pool : Stack_Bounded_Pool) return SSE.Storage_Count
185   is
186   begin
187      return Pool.Pool_Size;
188   end Storage_Size;
189
190   ------------------------------
191   -- Variable_Size_Management --
192   ------------------------------
193
194   package body Variable_Size_Management is
195
196      Minimum_Size : constant := 2 * SC_Size;
197
198      procedure Set_Size
199        (Pool        : Stack_Bounded_Pool;
200         Chunk, Size : SSE.Storage_Count);
201      --  Update the field 'size' of a chunk of available storage
202
203      procedure Set_Next
204        (Pool        : Stack_Bounded_Pool;
205         Chunk, Next : SSE.Storage_Count);
206      --  Update the field 'next' of a chunk of available storage
207
208      function Size
209        (Pool  : Stack_Bounded_Pool;
210         Chunk : SSE.Storage_Count) return SSE.Storage_Count;
211      --  Fetch the field 'size' of a chunk of available storage
212
213      function Next
214        (Pool  : Stack_Bounded_Pool;
215         Chunk : SSE.Storage_Count) return SSE.Storage_Count;
216      --  Fetch the field 'next' of a chunk of available storage
217
218      function Chunk_Of
219        (Pool : Stack_Bounded_Pool;
220         Addr : System.Address) return SSE.Storage_Count;
221      --  Give the chunk number in the pool from its Address
222
223      --------------
224      -- Allocate --
225      --------------
226
227      procedure Allocate
228        (Pool         : in out Stack_Bounded_Pool;
229         Address      : out System.Address;
230         Storage_Size : SSE.Storage_Count;
231         Alignment    : SSE.Storage_Count)
232      is
233         Chunk      : SSE.Storage_Count;
234         New_Chunk  : SSE.Storage_Count;
235         Prev_Chunk : SSE.Storage_Count;
236         Our_Align  : constant SSE.Storage_Count :=
237                        SSE.Storage_Count'Max (SSE.Storage_Count'Alignment,
238                                               Alignment);
239         Align_Size : constant SSE.Storage_Count :=
240                        SSE.Storage_Count'Max (
241                          Minimum_Size,
242                          ((Storage_Size + Our_Align - 1) / Our_Align) *
243                                                                  Our_Align);
244
245      begin
246         --  Look for the first big enough chunk
247
248         Prev_Chunk := Pool.First_Free;
249         Chunk := Next (Pool, Prev_Chunk);
250
251         while Chunk /= 0 and then Size (Pool, Chunk) < Align_Size loop
252            Prev_Chunk := Chunk;
253            Chunk := Next (Pool, Chunk);
254         end loop;
255
256         --  Raise storage_error if no big enough chunk available
257
258         if Chunk = 0 then
259            raise Storage_Error;
260         end if;
261
262         --  When the chunk is bigger than what is needed, take appropriate
263         --  amount and build a new shrinked chunk with the remainder.
264
265         if Size (Pool, Chunk) - Align_Size  > Minimum_Size then
266            New_Chunk := Chunk + Align_Size;
267            Set_Size (Pool, New_Chunk, Size (Pool, Chunk) - Align_Size);
268            Set_Next (Pool, New_Chunk, Next (Pool, Chunk));
269            Set_Next (Pool, Prev_Chunk, New_Chunk);
270
271         --  If the chunk is the right size, just delete it from the chain
272
273         else
274            Set_Next (Pool, Prev_Chunk, Next (Pool, Chunk));
275         end if;
276
277         Address := Pool.The_Pool (Chunk)'Address;
278      end Allocate;
279
280      --------------
281      -- Chunk_Of --
282      --------------
283
284      function Chunk_Of
285        (Pool : Stack_Bounded_Pool;
286         Addr : System.Address) return SSE.Storage_Count
287      is
288      begin
289         return 1 + abs (Addr - Pool.The_Pool (1)'Address);
290      end Chunk_Of;
291
292      ----------------
293      -- Deallocate --
294      ----------------
295
296      procedure Deallocate
297        (Pool         : in out Stack_Bounded_Pool;
298         Address      : System.Address;
299         Storage_Size : SSE.Storage_Count;
300         Alignment    : SSE.Storage_Count)
301      is
302         pragma Warnings (Off, Pool);
303
304         Align_Size : constant SSE.Storage_Count :=
305                        ((Storage_Size + Alignment - 1) / Alignment) *
306                                                                 Alignment;
307         Chunk : constant SSE.Storage_Count := Chunk_Of (Pool, Address);
308
309      begin
310         --  Attach the freed chunk to the chain
311
312         Set_Size (Pool, Chunk,
313                         SSE.Storage_Count'Max (Align_Size, Minimum_Size));
314         Set_Next (Pool, Chunk, Next (Pool, Pool.First_Free));
315         Set_Next (Pool, Pool.First_Free,  Chunk);
316
317      end Deallocate;
318
319      ----------------
320      -- Initialize --
321      ----------------
322
323      procedure Initialize  (Pool : in out Stack_Bounded_Pool) is
324      begin
325         Pool.First_Free := 1;
326
327         if Pool.Pool_Size > Minimum_Size then
328            Set_Next (Pool, Pool.First_Free, Pool.First_Free + Minimum_Size);
329            Set_Size (Pool, Pool.First_Free, 0);
330            Set_Size (Pool, Pool.First_Free + Minimum_Size,
331                                              Pool.Pool_Size - Minimum_Size);
332            Set_Next (Pool, Pool.First_Free + Minimum_Size, 0);
333         end if;
334      end Initialize;
335
336      ----------
337      -- Next --
338      ----------
339
340      function Next
341        (Pool  : Stack_Bounded_Pool;
342         Chunk : SSE.Storage_Count) return SSE.Storage_Count
343      is
344      begin
345         pragma Warnings (Off);
346         --  Kill alignment warnings, we are careful to make sure
347         --  that the alignment is correct.
348
349         return To_Storage_Count_Access
350                  (Pool.The_Pool (Chunk + SC_Size)'Address).all;
351
352         pragma Warnings (On);
353      end Next;
354
355      --------------
356      -- Set_Next --
357      --------------
358
359      procedure Set_Next
360        (Pool        : Stack_Bounded_Pool;
361         Chunk, Next : SSE.Storage_Count)
362      is
363      begin
364         pragma Warnings (Off);
365         --  Kill alignment warnings, we are careful to make sure
366         --  that the alignment is correct.
367
368         To_Storage_Count_Access
369           (Pool.The_Pool (Chunk + SC_Size)'Address).all := Next;
370
371         pragma Warnings (On);
372      end Set_Next;
373
374      --------------
375      -- Set_Size --
376      --------------
377
378      procedure Set_Size
379        (Pool        : Stack_Bounded_Pool;
380         Chunk, Size : SSE.Storage_Count)
381      is
382      begin
383         pragma Warnings (Off);
384         --  Kill alignment warnings, we are careful to make sure
385         --  that the alignment is correct.
386
387         To_Storage_Count_Access
388           (Pool.The_Pool (Chunk)'Address).all := Size;
389
390         pragma Warnings (On);
391      end Set_Size;
392
393      ----------
394      -- Size --
395      ----------
396
397      function Size
398        (Pool  : Stack_Bounded_Pool;
399         Chunk : SSE.Storage_Count) return SSE.Storage_Count
400      is
401      begin
402         pragma Warnings (Off);
403         --  Kill alignment warnings, we are careful to make sure
404         --  that the alignment is correct.
405
406         return To_Storage_Count_Access (Pool.The_Pool (Chunk)'Address).all;
407
408         pragma Warnings (On);
409      end Size;
410
411   end  Variable_Size_Management;
412end System.Pool_Size;
413