1-------------------------------------------------------------------------------
2--                       storage_pool_handler.adb                            --
3--                                                                           --
4-- Purposes: Manages memory resources. As a result memory leakage problem    --
5--           will be thing of the past.                                      --
6--                                                                           --
7-- Anh Vo - 01 March 2000                                                    --
8--                                                                           --
9-- C/C++ is for money. Java is for fame and money. Ada is for reliability.   --
10--                                                                           --
11-------------------------------------------------------------------------------
12
13with Ada.Exceptions;
14with Ada.Text_IO;
15with System.Address_To_Access_Conversions;
16with System.Storage_Elements;
17
18package body Storage_Pool_Handler is
19
20   use Ada;
21   use Text_IO;
22
23   use type System.Address;
24   use type System.Storage_Elements.Storage_Count;
25
26   Package_Name : constant String := "Storage_Pool_Handler.";
27
28
29   --  used by General Pool to link memory together
30   type Address_Linker is
31      record
32         Next_Address  : System.Address := System.Null_Address;
33         Size_Elements : System.Storage_Elements.Storage_Count;
34      end record;
35
36   --  storage elements needed by General Pool
37   Link_Storage : constant System.Storage_Elements.Storage_Count :=
38                             Address_Linker'Max_Size_In_Storage_Elements;
39
40   package Address_Access_Conversion is new
41                     System.Address_To_Access_Conversions (Address_Linker);
42
43
44   --  used by Detailed Pool to link the memory together
45   type Holder is
46      record
47         Next_Address : System.Address := System.Null_Address;
48      end record;
49
50   --  storage elements needed by Holder record.
51   Holder_Storage : constant System.Storage_Elements.Storage_Count :=
52                                      Holder'Max_Size_In_Storage_Elements;
53
54   package Address_Access_Conv is new
55               System.Address_To_Access_Conversions (Holder);
56
57
58   function Aligned (
59              Size      : System.Storage_Elements.Storage_Count;
60              Alignment : System.Storage_Elements.Storage_Count)
61                            return System.Storage_Elements.Storage_Count;
62   function Aligned (
63              Size      : System.Storage_Elements.Storage_Count;
64              Alignment : System.Storage_Elements.Storage_Count)
65                            return System.Storage_Elements.Storage_Count is
66   begin
67      if Size rem Alignment /= 0 then
68         return Size + Alignment - (Size mod Alignment);
69      else
70         return Size;
71      end if;
72   end Aligned;
73
74
75   procedure Allocate (
76         Pool            : in out General_Pool;
77         Storage_Address :    out System.Address;
78         Size_In_Storage_Elements : in System.Storage_Elements.Storage_Count;
79         Alignment       : in System.Storage_Elements.Storage_Count) is
80
81      Previous : System.Address := System.Null_Address;
82      Current  : System.Address := Pool.Addr_Head;
83      Desired_Size : System.Storage_Elements.Storage_Count :=
84                                               Size_In_Storage_Elements;
85
86   begin   -- Allocate
87
88      while Current /= System.Null_Address loop
89         if Address_Access_Conversion.To_Pointer (Current).Size_Elements =
90                                                      Desired_Size then
91
92            --  select the matched memory block
93            if Previous = System.Null_Address then
94               Pool.Addr_Head :=
95                 Address_Access_Conversion.To_Pointer (Current).Next_Address;
96            else
97               --  examine the next memory block
98               Address_Access_Conversion.To_Pointer (Previous).Next_Address :=
99                 Address_Access_Conversion.To_Pointer (Current).Next_Address;
100            end if;
101
102            Storage_Address := Current;
103            return;    -- mission complete
104
105         else   -- check the next address of the link
106
107            Previous := Current;
108            Current :=
109              Address_Access_Conversion.To_Pointer (Current).Next_Address;
110         end if;
111      end loop;
112
113      --  nothing found from storage reuse, grap storage elements from pool
114      if Pool.Size - Pool.Addr_Index > Desired_Size then
115         Storage_Address := Pool.Data (Pool.Addr_Index)'Address;
116         Pool.Addr_Index := Pool.Addr_Index + Desired_Size;
117      else
118         Exceptions.Raise_Exception (Storage_Pool_Error'Identity,
119           Message => Pool.User.all & "'s General Storage Pool is exhausted");
120      end if;
121
122   end Allocate;
123
124   procedure Deallocate (
125         Pool            : in out General_Pool;
126         Storage_Address : in     System.Address;
127         Size_In_Storage_Elements : in System.Storage_Elements.Storage_Count;
128         Alignment       : in System.Storage_Elements.Storage_Count) is
129
130      New_Object : Address_Access_Conversion.Object_Pointer;
131      Desired_Size : System.Storage_Elements.Storage_Count :=
132                                               Size_In_Storage_Elements;
133
134   begin   -- Deallocate
135
136      New_Object := Address_Access_Conversion.To_Pointer (Storage_Address);
137
138      New_Object.all := (Next_Address => Pool.Addr_Head,
139                         Size_Elements => Desired_Size);
140
141      Pool.Addr_Head := Storage_Address;
142
143   end Deallocate;
144
145
146   function Storage_Size (Pool : General_Pool)
147                            return System.Storage_Elements.Storage_Count is
148   begin
149      return Pool.Size;
150   end Storage_Size;
151
152
153   function Client_Id (Pool : General_Pool) return String is
154   begin
155      return Pool.User.all;
156   end Client_Id;
157
158   function Storage_Available (Pool : General_Pool)
159                      return System.Storage_Elements.Storage_Count is
160   begin
161      return Pool.Size - Pool.Addr_Index + 1;
162   end Storage_Available;
163
164   function Largest_Block (Pool : General_Pool)
165                            return System.Storage_Elements.Storage_Count is
166      Free_Block : System.Storage_Elements.Storage_Count := 0;
167      Addr_Index : System.Address := Pool.Addr_Head;
168      The_Object : Address_Access_Conversion.Object_Pointer;
169   begin
170      while Addr_Index /= System.Null_Address loop
171         The_Object := Address_Access_Conversion.To_Pointer (Addr_Index);
172
173         if The_Object.Size_Elements > Free_Block then
174            Free_Block := The_Object.Size_Elements;
175         else
176            Addr_Index :=
177              Address_Access_Conversion.To_Pointer (Addr_Index).Next_Address;
178         end if;
179      end loop;
180
181      if Free_Block >= Pool.Size - Pool.Addr_Index + 1 then
182         return Free_Block;
183      else
184         return Pool.Size - Pool.Addr_Index + 1;
185      end if;
186   end Largest_Block;
187
188   procedure Initialize (Pool : in out General_Pool) is
189   begin
190
191      if Pool.Size < Link_Storage then
192         --  fatal error - No go
193         Exceptions.Raise_Exception (Storage_Pool_Error'Identity,
194           Pool.User.all & "'s pool size specification is terribly wrong." &
195           " The requirements are: Size must be at least" &
196           System.Storage_Elements.Storage_Count'Image (Link_Storage) &
197           " elements long");
198      end if;
199
200      if Pool.User.all = ""  then
201         --  serious usage error
202         Exceptions.Raise_Exception (Unidentified_User'Identity,
203           "Pool User, you did not have a meaningful name");
204      end if;
205
206--      Put_Line (Pool.User.all & " user has the pool size of" &
207--        System.Storage_Elements.Storage_Count'Image (Pool.Size));
208   end Initialize;
209
210   procedure Finalize (Pool : in out General_Pool) is
211      Free_Count : System.Storage_Elements.Storage_Count := 0;
212      Addr_Index : System.Address := Pool.Addr_Head;
213      The_Object : Address_Access_Conversion.Object_Pointer;
214   begin
215      while Addr_Index /= System.Null_Address loop
216         The_Object := Address_Access_Conversion.To_Pointer (Addr_Index);
217         Free_Count := Free_Count + The_Object.Size_Elements;
218         Addr_Index :=
219           Address_Access_Conversion.To_Pointer (Addr_Index).Next_Address;
220      end loop;
221
222      if Free_Count /= Pool.Addr_Index - 1 then
223         Put_Line (Standard_Output, "Memory Leakage Detected!!! " &
224           Pool.User.all & "'s codes had memory leakage of" &
225           System.Storage_Elements.Storage_Count'Image (
226              Pool.Addr_Index - 1 - Free_Count) & " storage elements.");
227      end if;
228
229--      Put_Line (Pool.User.all & "'s " & "Free_Count = " &
230--        System.Storage_Elements.Storage_Count'Image (Free_Count));
231--      Put_Line (Pool.User.all & "'s " & "Addr_Index = " &
232--        System.Storage_Elements.Storage_Count'Image (Pool.Addr_Index));
233
234   end Finalize;
235
236
237   ----------------------------------------------------------------------------
238   -- Detailed memory blocks size are handled by this pool                   --
239   ----------------------------------------------------------------------------
240   procedure Allocate (
241         Pool            : in out Detailed_Pool;
242         Storage_Address :    out System.Address;
243         Size_In_Storage_Elements : in System.Storage_Elements.Storage_Count;
244         Alignment       : in System.Storage_Elements.Storage_Count) is
245   begin
246
247      if Pool.Head_Keeper /= System.Null_Address then
248         Storage_Address := Pool.Head_Keeper;
249         Pool.Head_Keeper :=
250           Address_Access_Conv.To_Pointer (Pool.Head_Keeper).Next_Address;
251      else
252         Exceptions.Raise_Exception (
253           E => Storage_Pool_Error'Identity,
254           Message => Pool.User.all & "'s Fixed Storage Pool is exhausted");
255      end if;
256
257   end Allocate;
258
259   procedure Deallocate (
260         Pool                     : in out Detailed_Pool;
261         Storage_Address          : in System.Address;
262         Size_In_Storage_Elements : in System.Storage_Elements.Storage_Count;
263         Alignment : in System.Storage_Elements.Storage_Count) is
264   begin
265      Address_Access_Conv.To_Pointer (Storage_Address).Next_Address :=
266                                                              Pool.Head_Keeper;
267      Pool.Head_Keeper := Storage_Address;
268   end Deallocate;
269
270   function Storage_Size (Pool : in Detailed_Pool)
271                            return System.Storage_Elements.Storage_Count is
272   begin
273      return Pool.Size;
274   end Storage_Size;
275
276   function User_Id (Pool : Detailed_Pool) return String is
277   begin
278      return Pool.User.all;
279   end User_Id;
280
281
282   function Memory_Left (Pool : Detailed_Pool)
283                               return System.Storage_Elements.Storage_Count is
284      Memory_Remain : constant Natural := Block_Remain (Pool);
285   begin
286      return System.Storage_Elements.Storage_Count (Memory_Remain);
287   end Memory_Left;
288
289   function Status (Pool : Detailed_Pool) return String is
290   begin
291      return (Pool.User.all & " has" &
292         Natural'Image (Block_Remain (Pool)) & " blocks free out of" &
293         Natural'Image (Pool.Block_Count) & "Block or" &
294         Natural'Image (Block_Remain (Pool) * Natural (Pool.Max)) &
295         " out of " & Natural'Image (Pool.Block_Count * Natural (Pool.Max)));
296   end Status;
297
298
299   function Block_Remain (Pool : Detailed_Pool) return Natural is
300      Count : Natural := 0;
301      Temp_Addr : System.Address := Pool.Head_Keeper;
302   begin
303      while Temp_Addr /= System.Null_Address loop
304         Count := Count + 1;
305         Temp_Addr := Address_Access_Conv.To_Pointer (Temp_Addr).Next_Address;
306      end loop;
307      return Count;
308   end Block_Remain;
309
310   procedure Initialize (Pool : in out Detailed_Pool) is
311      Temp_Addr : System.Address := System.Null_Address;
312      Addr_Index : System.Storage_Elements.Storage_Count := 1;
313
314   begin   -- Initialize
315
316      if Pool.Max < Holder_Storage or else Pool.Size < Pool.Max then
317         --  fatal error - No go
318         Exceptions.Raise_Exception (Storage_Pool_Error'Identity,
319           Pool.User.all & "'s pool size specification is terribly wrong." &
320           " The requirements are: Size must be at least" &
321           System.Storage_Elements.Storage_Count'Image (Holder_Storage) &
322           " elements and Max must be at least" &
323            System.Storage_Elements.Storage_Count'Image (Pool.Size) &
324           " elements long");
325      end if;
326
327      if Pool.User.all = ""  then
328         --  serious usage error
329         Exceptions.Raise_Exception (Unidentified_User'Identity,
330           "Pool user, you did not have a meaningful name");
331      end if;
332
333      while Pool.Size >= Addr_Index + Pool.Max - 1 loop
334         Temp_Addr := Pool.Data (Addr_Index)'Address;
335         Addr_Index := Addr_Index + Pool.Max;
336         Address_Access_Conv.To_Pointer (Temp_Addr).Next_Address :=
337                                                            Pool.Head_Keeper;
338         Pool.Head_Keeper := Temp_Addr;
339         Pool.Block_Count := Pool.Block_Count + 1;
340      end loop;
341
342--      Put_Line (Pool.User.all & " user has" &
343--        Integer'Image (Pool.Block_Count) & " blocks with block Size of" &
344--        System.Storage_Elements.Storage_Count'Image (Pool.Max) &
345--        " for the total size of" &
346--        System.Storage_Elements.Storage_Count'Image (Pool.Size));
347   end Initialize;
348
349   procedure Finalize (Pool : in out Detailed_Pool) is
350   begin
351      if Block_Remain (Pool) /= Pool.Block_Count then
352         Put_Line (Standard_Output, "Memory Leakage Detected!!! " &
353                     Pool.User.all & "'s codes had memory leakage of" &
354           Natural'Image ((Pool.Block_Count -
355                             Block_Remain (Pool)) * Integer (Pool.Max)) &
356                                                      " storage elements.");
357      end if;
358   end Finalize;
359
360end Storage_Pool_Handler;
361