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 }