1 /******************************************************************************* 2 3 Simple pool class template with the following features: 4 * Get and recycle items. Recycled items will be re-used before creating 5 new items. 6 * Fill the free list with a specific number of idle items. 7 * The number of idle items in the free list can be queried. 8 9 A free list does not manage busy items, it only stores a reference to any 10 idle / free items, making them available for re-use. 11 12 TODO: 13 - Free lists could support limiting, simply by counting the number of 14 allocated (i.e. busy) items. A count of allocated items would also allow a 15 length() method (with the same meaning as the method in IPool) to be 16 implemented. 17 - Add idle items iterators (safe & unsafe). These could probably share 18 code with the aggregate pool. 19 20 Copyright: 21 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 22 All rights reserved. 23 24 License: 25 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 26 Alternatively, this file may be distributed under the terms of the Tango 27 3-Clause BSD License (see LICENSE_BSD.txt for details). 28 29 *******************************************************************************/ 30 31 module ocean.util.container.pool.FreeList; 32 33 34 35 import ocean.util.container.pool.model.IFreeList; 36 37 import ocean.core.Array : pop; 38 39 import ocean.meta.traits.Basic; 40 import ocean.meta.types.Typedef; 41 42 import ocean.transition; 43 import ocean.core.Buffer; 44 import ocean.core.TypeConvert; 45 46 version (UnitTest) 47 import ocean.core.Test; 48 49 /******************************************************************************* 50 51 Template to determine the internal item type of a free list. 52 53 Params: 54 T = item type to be stored in free list 55 56 *******************************************************************************/ 57 58 private template ItemType_ ( T ) 59 { 60 static if (isTypedef!(T)) 61 { 62 alias ItemType_!(TypedefBaseType!(T)) ItemType_; 63 } 64 else static if ( 65 isReferenceType!(T) 66 || isArrayType!(T) == ArrayKind.Dynamic 67 || isArrayType!(T) == ArrayKind.Associative) 68 { 69 alias T ItemType_; 70 } 71 else 72 { 73 alias T* ItemType_; 74 } 75 } 76 77 78 /******************************************************************************* 79 80 Free list class template. 81 82 Params: 83 T = item type to be stored in free list 84 85 *******************************************************************************/ 86 87 public class FreeList ( T ) : IFreeList!(ItemType_!(T)) 88 { 89 import ocean.core.Verify; 90 91 /*************************************************************************** 92 93 Free lists of static arrays are not allowed. Likewise a free list of an 94 atomic type (int, float, etc) is not allowed -- this would be rather 95 pointless. 96 97 ***************************************************************************/ 98 99 static assert( 100 isArrayType!(T) != ArrayKind.Static, 101 "Cannot use static array type '" 102 ~ T.stringof ~ "' as base type for FreeList" 103 ); 104 105 static assert( 106 !isPrimitiveType!(T), 107 "Cannot use primitive type '" ~ T.stringof ~ 108 "' as base type for FreeList" 109 ); 110 111 112 /*************************************************************************** 113 114 Alias for item type to be stored in free list and accepted by public 115 methods. 116 117 Reference types (including dynamic and associative arrays) can be stored 118 directly in the free list. Pointers are stored for other types. 119 120 ***************************************************************************/ 121 122 public alias ItemType_!(T) ItemType; 123 124 125 /*************************************************************************** 126 127 List of free items. 128 129 ***************************************************************************/ 130 131 private ItemType[] free_list; 132 133 134 /*************************************************************************** 135 136 Gets an item from the free list. 137 138 Params: 139 new_item = new item, will only be evaluated in the case when no 140 items are available in the free list 141 142 Returns: 143 new item 144 145 ***************************************************************************/ 146 147 public ItemType get ( lazy ItemType new_item ) 148 { 149 if ( this.free_list.length ) 150 { 151 ItemType item; 152 auto popped = this.free_list.pop(item); 153 verify(popped, "Item failed to be popped from non-empty free list"); 154 return item; 155 } 156 else 157 { 158 return new_item; 159 } 160 } 161 162 163 /*************************************************************************** 164 165 Recycles an item into the free list. 166 167 Params: 168 item = item to be put into the free list 169 170 ***************************************************************************/ 171 172 public void recycle ( ItemType item ) 173 { 174 this.free_list ~= item; 175 } 176 177 178 /*************************************************************************** 179 180 Ensures that the free list contains at least the specified number of 181 (idle) items. Useful to pre-allocate a free list of a certain size. 182 183 Params: 184 num = minimum number of items desired in pool 185 new_item = expression that creates a new instance of T 186 187 Returns: 188 this 189 190 ***************************************************************************/ 191 192 public typeof(this) fill ( size_t num, lazy ItemType new_item ) 193 out 194 { 195 assert(this.free_list.length >= num); 196 } 197 body 198 { 199 if ( this.free_list.length < num ) 200 { 201 auto extra = num - this.free_list.length; 202 for ( size_t i; i < extra; i++ ) 203 { 204 this.free_list ~= new_item; 205 } 206 } 207 208 return this; 209 } 210 211 212 /*************************************************************************** 213 214 Ensures that the free list contains at most the specified number of 215 (idle) items. 216 217 Params: 218 num = maximum number of idle items desired in free list 219 220 Returns: 221 this 222 223 ***************************************************************************/ 224 225 public typeof(this) minimize ( size_t num = 0 ) 226 { 227 if ( this.free_list.length > num ) 228 { 229 this.free_list.length = num; 230 enableStomping(this.free_list); 231 } 232 233 return this; 234 } 235 236 237 /*************************************************************************** 238 239 Returns: 240 the number of idle (available) items in the free list 241 242 ***************************************************************************/ 243 244 public size_t num_idle ( ) 245 { 246 return this.free_list.length; 247 } 248 } 249 250 251 /******************************************************************************* 252 253 Unit test. 254 255 Tests: 256 * All methods of IFreeList. 257 * FreeList of strings, structs and classes. 258 259 *******************************************************************************/ 260 261 version ( UnitTest ) 262 { 263 import ocean.core.Test; 264 265 /*************************************************************************** 266 267 String free list tester. 268 269 ***************************************************************************/ 270 271 private alias FreeList!(char[]) StringFreeList; 272 private class StringFreeListTester : FreeListTester!(StringFreeList.ItemType) 273 { 274 public this ( ) { super(new StringFreeList); } 275 276 protected override Item newItem ( ) 277 { 278 return new char[10]; 279 } 280 281 protected override void setItem ( ref Item item, size_t i ) 282 { 283 item.length = 1; 284 item[0] = cast(char)(i + 32); 285 } 286 287 protected override void checkItem ( ref Item item, size_t i ) 288 { 289 .test!("==")(item.length, 1, "item length wrong"); 290 .test!("==")(item[0], cast(char)(i + 32), "item content wrong"); 291 } 292 } 293 294 295 /*************************************************************************** 296 297 Struct free list tester. 298 299 ***************************************************************************/ 300 301 private struct Struct 302 { 303 size_t i; 304 char[] s; 305 } 306 307 private alias FreeList!(Struct) StructFreeList; 308 private class StructFreeListTester : FreeListTester!(StructFreeList.ItemType) 309 { 310 public this ( ) { super(new StructFreeList); } 311 312 protected override Item newItem ( ) 313 { 314 return new Struct; 315 } 316 317 protected override void setItem ( ref Item item, size_t i ) 318 { 319 item.i = i; 320 item.s.length = 1; 321 item.s[0] = cast(char)(i + 32); 322 } 323 324 protected override void checkItem ( ref Item item, size_t i ) 325 { 326 .test!("==")(item.i, i, "item integer wrong"); 327 .test!("==")(item.s.length, 1, "item string length wrong"); 328 .test!("==")(item.s[0], cast(char)(i + 32), "item string content wrong"); 329 } 330 } 331 332 /*************************************************************************** 333 334 Buffer free list tester. 335 336 ***************************************************************************/ 337 338 private alias FreeList!(Buffer!(void)) BufferFreeList; 339 private class BufferFreeListTester : FreeListTester!(BufferFreeList.ItemType) 340 { 341 public this ( ) { super(new BufferFreeList); } 342 343 protected override Item newItem ( ) 344 { 345 return new Buffer!(void); 346 } 347 348 protected override void setItem ( ref Item item, size_t i ) 349 { 350 *item ~= 40; 351 *item ~= 41; 352 *item ~= 42; 353 } 354 355 protected override void checkItem ( ref Item item, size_t i ) 356 { 357 void[] a = (*item)[]; 358 void[] b = arrayOf!(ubyte)(40, 41, 42); 359 .test!("==")(a, b); 360 } 361 } 362 363 /*************************************************************************** 364 365 Class free list tester. 366 367 ***************************************************************************/ 368 369 private class Class 370 { 371 mixin(genOpEquals(` 372 { 373 auto crhs = cast(typeof(this)) rhs; 374 return this.i == crhs.i && this.s == crhs.s; 375 }`)); 376 377 size_t i; 378 char[] s; 379 } 380 381 private alias FreeList!(Class) ClassFreeList; 382 private class ClassFreeListTester : FreeListTester!(ClassFreeList.ItemType) 383 { 384 public this ( ) { super(new ClassFreeList); } 385 386 protected override Item newItem ( ) 387 { 388 return new Class; 389 } 390 391 protected override void setItem ( ref Item item, size_t i ) 392 { 393 item.i = i; 394 item.s.length = 1; 395 item.s[0] = cast(char)(i + 32); 396 } 397 398 protected override void checkItem ( ref Item item, size_t i ) 399 { 400 .test!("==")(item.i, i, "item integer wrong"); 401 .test!("==")(item.s.length, 1, "item string length wrong"); 402 .test!("==")(item.s[0], cast(char)(i + 32), "item string content wrong"); 403 } 404 } 405 } 406 407 unittest 408 { 409 // String (arrays) free list test 410 { 411 scope fl = new StringFreeListTester; 412 fl.test(); 413 } 414 415 // Struct free list test 416 { 417 scope fl = new StructFreeListTester; 418 fl.test(); 419 } 420 421 // Buffer free list test 422 { 423 scope fl = new BufferFreeListTester; 424 fl.test(); 425 } 426 427 // Class free list test 428 { 429 scope fl = new ClassFreeListTester; 430 fl.test(); 431 } 432 } 433