aboutsummaryrefslogtreecommitdiff
path: root/gcc/ada/libgnat/s-finpri.adb
blob: bc90fe23ac92625b6304412286cce65b2b05d0ba (plain)
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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
------------------------------------------------------------------------------
--                                                                          --
--                         GNAT COMPILER COMPONENTS                         --
--                                                                          --
--        S Y S T E M . F I N A L I Z A T I O N _ P R I M I T I V E S       --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--         Copyright (C) 2023-2024, Free Software Foundation, Inc.          --
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
--                                                                          --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception,   --
-- version 3.1, as published by the Free Software Foundation.               --
--                                                                          --
-- You should have received a copy of the GNU General Public License and    --
-- a copy of the GCC Runtime Library Exception along with this program;     --
-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
-- <http://www.gnu.org/licenses/>.                                          --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
--                                                                          --
------------------------------------------------------------------------------

with Ada.Exceptions;           use Ada.Exceptions;
with Ada.Unchecked_Conversion;

with System.Soft_Links; use System.Soft_Links;

package body System.Finalization_Primitives is

   procedure Raise_From_Controlled_Operation (X : Exception_Occurrence);
   pragma Import (Ada, Raise_From_Controlled_Operation,
                              "__gnat_raise_from_controlled_operation");

   function To_Collection_Node_Ptr is
     new Ada.Unchecked_Conversion (Address, Collection_Node_Ptr);

   procedure Detach_Node_From_Collection (Node : not null Collection_Node_Ptr);
   --  Remove a collection node from its associated finalization collection.
   --  Calls to the procedure with a Node that has already been detached have
   --  no effects.

   procedure Lock_Collection (Collection : in out Finalization_Collection);
   --  Lock the finalization collection. Upon return, the caller owns the lock
   --  to the collection and no other call with the same actual parameter will
   --  return until a corresponding call to Unlock_Collection has been made by
   --  the caller. This means that it is not possible to call Lock_Collection
   --  more than once on a collection without a call to Unlock_Collection in
   --  between.

   procedure Unlock_Collection (Collection : in out Finalization_Collection);
   --  Unlock the finalization collection, i.e. relinquish ownership of the
   --  lock to the collection.

   ---------------------------
   -- Add_Offset_To_Address --
   ---------------------------

   function Add_Offset_To_Address
     (Addr   : System.Address;
      Offset : System.Storage_Elements.Storage_Offset) return System.Address
   is
   begin
      return System.Storage_Elements."+" (Addr, Offset);
   end Add_Offset_To_Address;

   ---------------------------------
   -- Attach_Object_To_Collection --
   ---------------------------------

   procedure Attach_Object_To_Collection
     (Object_Address   : System.Address;
      Finalize_Address : not null Finalize_Address_Ptr;
      Collection       : in out Finalization_Collection)
   is
      Node : constant Collection_Node_Ptr :=
               To_Collection_Node_Ptr (Object_Address - Header_Size);

   begin
      Lock_Collection (Collection);

      --  Do not allow the attachment of controlled objects while the
      --  associated collection is being finalized.

      --  Synchronization:
      --    Read  - attachment, finalization
      --    Write - finalization

      if Collection.Finalization_Started then
         raise Program_Error with "attachment after finalization started";
      end if;

      --  Check whether primitive Finalize_Address is available. If it is
      --  not, then either the expansion of the designated type failed or
      --  the expansion of the allocator failed. This is a compiler bug.

      pragma Assert
        (Finalize_Address /= null, "primitive Finalize_Address not available");

      Node.Enclosing_Collection := Collection'Unrestricted_Access;
      Node.Finalize_Address     := Finalize_Address;
      Node.Prev                 := Collection.Head'Unchecked_Access;
      Node.Next                 := Collection.Head.Next;

      Collection.Head.Next.Prev := Node;
      Collection.Head.Next      := Node;

      Unlock_Collection (Collection);

   exception
      when others =>

         --  Unlock the collection in case the attachment failed and reraise
         --  the exception.

         Unlock_Collection (Collection);
         raise;
   end Attach_Object_To_Collection;

   -----------------------------
   -- Attach_Object_To_Master --
   -----------------------------

   procedure Attach_Object_To_Master
     (Object_Address   : System.Address;
      Finalize_Address : not null Finalize_Address_Ptr;
      Node             : not null Master_Node_Ptr;
      Master           : in out Finalization_Master)
   is
   begin
      Attach_Object_To_Node (Object_Address, Finalize_Address, Node.all);
      Chain_Node_To_Master (Node, Master);
   end Attach_Object_To_Master;

   ---------------------------
   -- Attach_Object_To_Node --
   ---------------------------

   procedure Attach_Object_To_Node
     (Object_Address   : System.Address;
      Finalize_Address : not null Finalize_Address_Ptr;
      Node             : in out Master_Node)
   is
   begin
      pragma Assert (Node.Object_Address = System.Null_Address
        and then Node.Finalize_Address = null);

      Node.Object_Address   := Object_Address;
      Node.Finalize_Address := Finalize_Address;
   end Attach_Object_To_Node;

   --------------------------
   -- Chain_Node_To_Master --
   --------------------------

   procedure Chain_Node_To_Master
     (Node   : not null Master_Node_Ptr;
      Master : in out Finalization_Master)
   is
   begin
      Node.Next   := Master.Head;
      Master.Head := Node;
   end Chain_Node_To_Master;

   ---------------------------------
   -- Detach_Node_From_Collection --
   ---------------------------------

   procedure Detach_Node_From_Collection
     (Node : not null Collection_Node_Ptr)
   is
   begin
      if Node.Prev /= null and then Node.Next /= null then
         Node.Prev.Next := Node.Next;
         Node.Next.Prev := Node.Prev;
         Node.Prev := null;
         Node.Next := null;
      end if;
   end Detach_Node_From_Collection;

   -----------------------------------
   -- Detach_Object_From_Collection --
   -----------------------------------

   procedure Detach_Object_From_Collection
     (Object_Address : System.Address)
   is
      Node : constant Collection_Node_Ptr :=
               To_Collection_Node_Ptr (Object_Address - Header_Size);

   begin
      Lock_Collection (Node.Enclosing_Collection.all);

      Detach_Node_From_Collection (Node);

      Unlock_Collection (Node.Enclosing_Collection.all);
   end Detach_Object_From_Collection;

   --------------
   -- Finalize --
   --------------

   overriding procedure Finalize
     (Collection : in out Finalization_Collection)
   is
      Curr_Ptr                      : Collection_Node_Ptr;
      Exc_Occur                     : Exception_Occurrence;
      Finalization_Exception_Raised : Boolean := False;
      Obj_Addr                      : Address;

      function Is_Empty_List (L : not null Collection_Node_Ptr) return Boolean;
      --  Determine whether a list contains only one element, the dummy head

      -------------------
      -- Is_Empty_List --
      -------------------

      function Is_Empty_List (L : not null Collection_Node_Ptr) return Boolean
      is
      begin
         return L.Next = L and then L.Prev = L;
      end Is_Empty_List;

   begin
      Lock_Collection (Collection);

      --  Synchronization:
      --    Read  - attachment, finalization
      --    Write - finalization

      if Collection.Finalization_Started then
         Unlock_Collection (Collection);

         --  Double finalization may occur during the handling of stand-alone
         --  libraries or the finalization of a pool with subpools.

         return;
      end if;

      --  Lock the collection to prevent any attachment while the objects are
      --  being finalized. The collection remains locked because either it is
      --  explicitly deallocated or the associated access type is about to go
      --  out of scope.

      --  Synchronization:
      --    Read  - attachment, finalization
      --    Write - finalization

      Collection.Finalization_Started := True;

      --  Note that we cannot walk the list while finalizing its elements
      --  because the finalization of one may call Unchecked_Deallocation
      --  on another and, therefore, detach it from anywhere on the list.
      --  Instead, we empty the list by repeatedly finalizing the first
      --  element (after the dummy head) and detaching it from the list.

      while not Is_Empty_List (Collection.Head'Unchecked_Access) loop
         Curr_Ptr := Collection.Head.Next;

         --  Synchronization:
         --    Write - attachment, detachment, finalization

         Detach_Node_From_Collection (Curr_Ptr);

         --  Skip the list header in order to offer proper object layout for
         --  finalization.

         Obj_Addr := Curr_Ptr.all'Address + Header_Size;

         --  Temporarily release the lock because the call to Finalize_Address
         --  may ultimately invoke Detach_Object_From_Collection.

         Unlock_Collection (Collection);

         begin
            Curr_Ptr.Finalize_Address (Obj_Addr);
         exception
            when Fin_Occur : others =>
               if not Finalization_Exception_Raised then
                  Finalization_Exception_Raised := True;
                  Save_Occurrence (Exc_Occur, Fin_Occur);
               end if;
         end;

         --  Retake the lock for the next iteration

         Lock_Collection (Collection);
      end loop;

      Unlock_Collection (Collection);

      --  If one of the finalization actions raised an exception, reraise it

      if Finalization_Exception_Raised then
         Raise_From_Controlled_Operation (Exc_Occur);
      end if;
   end Finalize;

   ---------------------
   -- Finalize_Master --
   ---------------------

   procedure Finalize_Master (Master : in out Finalization_Master) is
      Exc_Occur                     : Exception_Occurrence;
      Finalization_Exception_Raised : Boolean := False;
      Node                          : Master_Node_Ptr;

   begin
      Node := Master.Head;

      --  If exceptions are enabled, we catch them locally and reraise one
      --  once all the finalization actions have been completed.

      if Master.Exceptions_OK then
         while Node /= null loop
            begin
               Finalize_Object (Node.all);

            exception
               when Exc : others =>
                  if not Finalization_Exception_Raised then
                     Finalization_Exception_Raised := True;

                     if Master.Library_Level then
                        if Master.Extra_Info then
                           Save_Library_Occurrence (Exc'Unrestricted_Access);
                        else
                           Save_Library_Occurrence (null);
                        end if;

                     elsif Master.Extra_Info then
                        Save_Occurrence (Exc_Occur, Exc);
                     end if;
                  end if;
            end;

            Node := Node.Next;
         end loop;

      --  Otherwise we call finalization procedures without protection

      else
         while Node /= null loop
            Finalize_Object (Node.all);

            Node := Node.Next;
         end loop;
      end if;

      Master.Head := null;

      --  If one of the finalization actions raised an exception, and we are
      --  not at library level, then reraise the exception.

      if Finalization_Exception_Raised and then not Master.Library_Level then
         if Master.Extra_Info then
            Raise_From_Controlled_Operation (Exc_Occur);
         else
            raise Program_Error with "finalize/adjust raised exception";
         end if;
      end if;
   end Finalize_Master;

   ---------------------
   -- Finalize_Object --
   ---------------------

   procedure Finalize_Object (Node : in out Master_Node) is
      FA : constant Finalize_Address_Ptr := Node.Finalize_Address;

   begin
      if FA /= null then
         pragma Assert (Node.Object_Address /= System.Null_Address);

         Node.Finalize_Address := null;

         FA (Node.Object_Address);
      end if;
   end Finalize_Object;

   ----------------
   -- Initialize --
   ----------------

   overriding procedure Initialize
     (Collection : in out Finalization_Collection)
   is
   begin
      --  The dummy head must point to itself in both directions

      Collection.Head.Prev := Collection.Head'Unchecked_Access;
      Collection.Head.Next := Collection.Head'Unchecked_Access;

      Initialize_RTS_Lock (Collection.Lock'Address);

      Collection.Finalization_Started := False;
   end Initialize;

   ---------------------
   -- Lock_Collection --
   ---------------------

   procedure Lock_Collection (Collection : in out Finalization_Collection) is
   begin
      Acquire_RTS_Lock (Collection.Lock'Address);
   end Lock_Collection;

   -------------------------------------
   -- Suppress_Object_Finalize_At_End --
   -------------------------------------

   procedure Suppress_Object_Finalize_At_End (Node : in out Master_Node) is
   begin
      Node.Finalize_Address := null;
   end Suppress_Object_Finalize_At_End;

   -----------------------
   -- Unlock_Collection --
   -----------------------

   procedure Unlock_Collection (Collection : in out Finalization_Collection) is
   begin
      Release_RTS_Lock (Collection.Lock'Address);
   end Unlock_Collection;

end System.Finalization_Primitives;