1 /*******************************************************************************
2 
3     Base class for a bucket element allocator using malloc function bypassing
4     the GC.
5 
6     By default the class constructor checks the type of the Bucket elements
7     (i.e, the values stored in the Map). If the element contains reference
8     items (e.g class or an array) then on malloc allocations the class adds the
9     elements to the GC scan range and removes them from the GC scan range on
10     recycling.
11 
12     This tracking of the objects can be explicitly disabled through passing
13     the appropriate flag to the class constructor.
14 
15     Warning :
16 
17         Do not disable the tracking of the allocated elements except if the
18         element if there is another reference to the GC allocated items
19         elsewhere.
20 
21     The following is a more detailed explanation about what's safe to store
22     without GC tracking and what's unsafe.
23 
24         If the elements stored in the map (i.e the struct or class you are
25         storing) contains a GC managed memory item and the single reference to
26         this memory is only in this malloc-based map then the GC will collect
27         this data as no other GC object is referencing it. Once collected you
28         will end up with segmentation fault when you to access this non-used
29         memory address.
30 
31         For example, consider that what you are storing in the map is the
32         following :
33 
34         ---
35             struct S
36             {
37                 statuc class C
38                 {
39                 }
40 
41                 statuc struct S2
42                 {
43                     float x, y;
44                 }
45 
46                 int a; // Not a problem
47                 S2 s2; // Not a problem, S2 doesn't contain GC allocated memory.
48 
49                 int[] arr; // Problem: Arrays memory are managed by the GC
50                 Class c; // Problem: class C is tracked by the GC.
51 
52                 static S opCall()
53                 {
54                     S s;
55                     s.c = new C();
56                     return s;
57                 }
58              }
59         ---
60 
61         This reference items doesn't have to be added to the GC scan list if
62         it has another reference in the GC (e.g when another reference exists
63         in a pool).
64 
65         For example:
66 
67         ---
68             struct GCTrackedObject
69             {
70                 int[] arr;
71             }
72 
73             static StructPool!(GCTrackedObject) arr_pool; // Tracked by GC
74 
75             struct S
76             {
77                         .
78                 // Same code as above
79                         .
80 
81                 ObjectPool!(C) c_pool; // Tracked by GC
82                 static S opCall()
83                 {
84                     S s;
85                     s.c = c_pool.get();
86                     auto gc_tracked_object = arr_pool.get();
87                     s.arr = gc_tracked_object.arr;
88                     return s;
89                 }
90 
91                 // TODO: Recycle the struct and object again to their pools
92                 // again when this S struct item is removed from malloc map.
93             }
94         ---
95 
96     Copyright:
97         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
98         All rights reserved.
99 
100     License:
101         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
102         Alternatively, this file may be distributed under the terms of the Tango
103         3-Clause BSD License (see LICENSE_BSD.txt for details).
104 
105 *******************************************************************************/
106 
107 module ocean.util.container.map.model.BucketElementMallocAllocator;
108 
109 
110 import ocean.util.container.map.model.IAllocator;
111 
112 import core.memory;
113 import ocean.meta.types.Qualifiers;
114 
115 /*******************************************************************************
116 
117     Implements a malloc based BucketElement Allactor.
118 
119     By default the class constructor checks the type of the Bucket elements
120     (i.e, the values stored in the Map). If the element contains reference
121     items (e.g class or an array) then on malloc allocations the class adds the
122     elements to the GC scan range and removes them from the GC scan range on
123     recycling.
124 
125     This tracking of the objects can be explicitly disabled through passing
126     the appropriate flag to the class constructor.
127 
128     Params:
129         Bucket = the bucket-element type
130 
131 *******************************************************************************/
132 
133 public class BucketElementMallocAllocator (Bucket) : IAllocator
134 {
135     import core.stdc.stdio : fputs, stderr;
136     import core.stdc.stdlib : malloc, free, abort;
137 
138     /***************************************************************************
139 
140         Make sure we don't try to allocate 0 sized elements, otherwise
141         get() can return null.
142 
143     ***************************************************************************/
144 
145     static assert(Bucket.Element.sizeof);
146 
147     /***************************************************************************
148 
149         Flags whether the malloced items should be tracked by the GC.
150 
151     ***************************************************************************/
152 
153     private bool add_to_gc;
154 
155 
156     /***************************************************************************
157 
158         Constructor.
159 
160         Params:
161             attempt_gc_track = if set to true and if the Bucket element contains
162                 reference items (e.g class or arrays) then the allocated values
163                 are added to the GC scan range.
164                 If set to false or if set to true but the Bucket element doesn't
165                 contain reference type then the item won't be tracked by
166                 the GC.
167 
168     ***************************************************************************/
169 
170     public this(bool attempt_gc_track = true)
171     {
172         super(Bucket.Element.sizeof);
173         if (attempt_gc_track)
174         {
175             // TypeInfo.flags & 2 is set if the type cannot have references to
176             // GC allocated objects.
177             // In D2 this can be checked at compile time.
178             this.add_to_gc = !(typeid(Bucket).flags & 2);
179         }
180         else
181             this.add_to_gc = false;
182     }
183 
184     /***************************************************************************
185 
186         Get new element
187 
188         Returns:
189             pointer to the allocated item
190 
191     ***************************************************************************/
192 
193     protected override void* allocate ( )
194     {
195         auto inited = cast(Bucket.Element*) malloc(Bucket.Element.sizeof);
196         if (inited is null)
197         {
198             istring msg = "malloc failed to allocate @" ~ __FILE__ ~ ":" ~
199                           __LINE__.stringof ~ "\n\0";
200             fputs(msg.ptr, stderr);
201             abort();
202         }
203         *inited = Bucket.Element.init;
204 
205         if (this.add_to_gc)
206             GC.addRange(inited, Bucket.Element.sizeof);
207 
208         return inited;
209     }
210 
211     /***************************************************************************
212 
213         delete element
214 
215         Params:
216             element = pointer to the element to be deleted
217 
218     ***************************************************************************/
219 
220     protected override void deallocate ( void* element )
221     {
222         if (this.add_to_gc)
223             GC.removeRange(element);
224         free(element);
225     }
226 
227     /***************************************************************************
228 
229         Class for parking elements
230 
231     ***************************************************************************/
232 
233     static /* scope */ class ParkingStack : IParkingStack
234     {
235         /***********************************************************************
236 
237             Parking stack
238 
239         ***********************************************************************/
240 
241         private void*[] elements;
242 
243         /***********************************************************************
244 
245             Create new instance of class with size n
246 
247             Params:
248                 n = the number of elements that will be parked
249 
250         ***********************************************************************/
251 
252         public this ( size_t n )
253         {
254             super(n);
255 
256             auto allocated_mem = malloc((void*).sizeof * n);
257             if (allocated_mem is null)
258             {
259                 istring msg = "malloc failed to allocate @" ~ __FILE__ ~ ":" ~
260                               __LINE__.stringof ~ "\n\0";
261                 fputs(msg.ptr, stderr);
262                 abort();
263             }
264             this.elements = (cast(void**)allocated_mem)[0 .. n];
265         }
266 
267         /***********************************************************************
268 
269             Dispose class.
270 
271         ***********************************************************************/
272 
273         ~this ( )
274         {
275             free(this.elements.ptr);
276         }
277 
278         /***********************************************************************
279 
280             Park element.
281 
282             Params:
283                 element = the element to park
284                 n = the index of the element
285 
286         ***********************************************************************/
287 
288         protected override void push_ ( void* element, size_t n )
289         {
290             this.elements[n] = element;
291         }
292 
293         /***********************************************************************
294 
295             Pop an element from parking.
296 
297             Params:
298                 n = the index of the element to retrieve
299 
300             Returns:
301                 the element parked at index n
302 
303         ***********************************************************************/
304 
305         protected override void* pop_ ( size_t n )
306         {
307             return this.elements[n];
308         }
309     }
310 
311     /***************************************************************************
312 
313         Park elements
314 
315         Params:
316             n = the number of elements that will be parked
317             dg = the delegate that will receive the IParkingStack implementation
318 
319     ***************************************************************************/
320 
321     public override void parkElements (size_t n,
322                                        scope void delegate ( IParkingStack park ) dg)
323     {
324         scope park = new ParkingStack(n);
325         dg(park);
326     }
327 }
328 
329 
330 /*******************************************************************************
331 
332     Returns a new instance of type BucketElementMallocAllocator suitable to be
333     used with the Map passed as template parameter.
334 
335     Params:
336         Map = the map to create the allocator according to
337         attempt_gc_track = if set to true and if the Bucket element contains
338             reference items (e.g class or arrays) then the allocated values
339             are added to the GC scan range.
340             If set to false or if set to true but the Bucket element doesn't
341             contain reference type then the item won't be tracked by
342             the GC.
343 
344     Returns:
345         an instance of type BucketElementMallocAllocator
346 
347 *******************************************************************************/
348 
349 public BucketElementMallocAllocator!(Map.Bucket)
350        instantiateAllocator(Map)(bool attempt_gc_track = true)
351 {
352     return new BucketElementMallocAllocator!(Map.Bucket)(attempt_gc_track);
353 }
354 
355 
356 
357 version (unittest)
358 {
359     import ocean.util.container.map.model.Bucket;
360     unittest
361     {
362         // Test if creating a map with a bucket compiles.
363         auto allocator = new
364             BucketElementMallocAllocator!(Bucket!(hash_t.sizeof, hash_t));
365     }
366 }