1 /** 2 Numem Lifetime Handling. 3 4 This module implements wrappers around $(D numem.core.lifetime) to provide 5 an easy way to instantiate various D types and slices of D types. 6 7 Copyright: 8 Copyright © 2023-2025, Kitsunebi Games 9 Copyright © 2023-2025, Inochi2D Project 10 11 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 12 Authors: Luna Nielsen 13 14 See_Also: 15 $(D numem.core.memory.nu_resize) 16 $(D numem.core.memory.nu_dup) 17 $(D numem.core.memory.nu_idup) 18 */ 19 module numem.lifetime; 20 import numem.core.exception; 21 import numem.core.traits; 22 import numem.core.lifetime; 23 import numem.core.hooks; 24 import numem.core.memory; 25 import numem.core.math; 26 import numem.core.cpp; 27 import numem.casting; 28 import numem.heap; 29 30 // Utilities that are useful in the same regard from core. 31 public import numem.core.memory : nu_dup, nu_idup, nu_resize, nu_malloca, nu_freea, nu_terminate, nu_swap; 32 public import numem.core.lifetime : nu_destroywith, nu_autoreleasewith; 33 34 @nogc: 35 36 /** 37 Creates a new auto release pool spanning the current scope. 38 39 Returns: 40 A stack allocated object with copying and moving disabled. 41 */ 42 auto autoreleasepool_scope() { 43 static 44 struct nu_arpool_stackctx { 45 @nogc: 46 private: 47 void* ctx; 48 49 public: 50 ~this() { nu_autoreleasepool_pop(ctx); } 51 this(void* ctx) { this.ctx = ctx; } 52 53 @disable this(this); 54 } 55 56 return nu_arpool_stackctx(nu_autoreleasepool_push()); 57 } 58 59 /** 60 Creates a scoped auto release pool. 61 62 Params: 63 scope_ = The scope in which the auto release pool acts. 64 */ 65 void autoreleasepool(void delegate() scope @nogc scope_) @trusted { 66 void* ctx = nu_autoreleasepool_push(); 67 scope_(); 68 nu_autoreleasepool_pop(ctx); 69 } 70 71 /** 72 Constructs the given object. 73 74 Attempting to construct a non-initialized $(D object) is undefined behaviour. 75 */ 76 void nogc_construct(T, Args...)(ref T object, Args args) @trusted { 77 static if (isPointer!T) 78 emplace(*object, args); 79 else 80 emplace(object, args); 81 } 82 83 /** 84 Allocates a new instance of $(D T) using the DLang allocation 85 strategy. 86 87 See_Also: 88 $(D nogc_trynew), $(D cpp_new) 89 */ 90 Ref!T nogc_new(T, Args...)(auto ref Args args) @trusted { 91 if (Ref!T newobject = cast(Ref!T)nu_malloc(AllocSize!T)) { 92 try { 93 nogc_construct(newobject, args); 94 return newobject; 95 96 } catch(Exception ex) { 97 nu_free(cast(void*)newobject); 98 throw ex; 99 } 100 } 101 return null; 102 } 103 104 /** 105 Allocates a new instance of $(D T) using the DLang allocation 106 strategy on the specified heap. 107 108 Params: 109 heap = The heap to allocate the instance on. 110 args = The arguments to pass to the type's constructor. 111 112 See_Also: 113 $(D nogc_trynew), $(D cpp_new) 114 */ 115 Ref!T nogc_new(T, Args...)(NuHeap heap, auto ref Args args) @trusted { 116 if (Ref!T newobject = cast(Ref!T)heap.alloc(AllocSize!T)) { 117 try { 118 nogc_construct(newobject, args); 119 } catch(Exception ex) { 120 nu_free(cast(void*)newobject); 121 throw ex; 122 } 123 } 124 return null; 125 } 126 127 /** 128 Attempts to allocate a new instance of $(D T) using the DLang allocation 129 strategy. 130 131 Params: 132 args = The arguments to pass to the type's constructor. 133 134 Returns: 135 A reference to the instantiated object or $(D null) if allocation 136 failed. 137 138 See_Also: 139 $(D nogc_new), $(D cpp_new), $(D cpp_trynew) 140 */ 141 Ref!T nogc_trynew(T, Args...)(auto ref Args args) @trusted nothrow { 142 if (Ref!T newobject = cast(Ref!T)nu_malloc(AllocSize!T)) { 143 try { 144 nogc_construct(newobject, args); 145 return newobject; 146 147 } catch(Exception ex) { 148 149 nu_free(cast(void*)newobject); 150 if (ex) 151 assumeNoThrowNoGC((Exception ex) { nogc_delete(ex); }, ex); 152 return null; 153 } 154 } 155 return null; 156 } 157 158 /** 159 Attempts to allocate a new instance of $(D T) on the specified heap. 160 161 Params: 162 heap = The heap to allocate the instance on. 163 args = The arguments to pass to the type's constructor. 164 165 Returns: 166 A reference to the instantiated object or $(D null) if allocation 167 failed. 168 */ 169 Ref!T nogc_trynew(T, Args...)(NuHeap heap, auto ref Args args) @trusted nothrow { 170 if (Ref!T newobject = cast(Ref!T)heap.alloc(AllocSize!T)) { 171 try { 172 nogc_construct(newobject, args); 173 return newobject; 174 175 } catch(Exception ex) { 176 177 nu_free(cast(void*)newobject); 178 if (ex) 179 assumeNoThrowNoGC((Exception ex) { nogc_delete(ex); }, ex); 180 return null; 181 } 182 } 183 return null; 184 } 185 186 /** 187 Finalizes $(D obj_) by calling its destructor (if any). 188 189 Notes: 190 If $(D doFree) is $(D true), memory associated with obj_ will additionally be freed 191 after finalizers have run; otherwise the object is reset to its original state. 192 193 Params: 194 obj_ = Instance to destroy and deallocate. 195 */ 196 void nogc_delete(T, bool doFree=true)(ref T obj_) @trusted { 197 static if (isHeapAllocated!T) { 198 199 // Ensure type is not null. 200 if (reinterpret_cast!(void*)(obj_) !is null) { 201 202 destruct!(T, !doFree)(obj_); 203 204 // Free memory if need be. 205 static if (doFree) 206 nu_free(cast(void*)obj_); 207 208 obj_ = null; 209 } 210 } else { 211 destruct!(T, !doFree)(obj_); 212 } 213 } 214 215 /** 216 Deallocates the specified instance of $(D T) from the specified heap. 217 Finalizes $(D obj_) by calling its destructor (if any). 218 219 Notes: 220 If $(D doFree) is $(D true), memory associated with obj_ will additionally be freed 221 after finalizers have run; otherwise the object is reset to its original state. 222 223 Params: 224 heap = The heap to allocate the instance on. 225 obj_ = Instance to destroy and deallocate. 226 */ 227 void nogc_delete(T, bool doFree=true)(NuHeap heap, ref T obj_) @trusted { 228 static if (isHeapAllocated!T) { 229 if (reinterpret_cast!(void*)(obj_) !is null) { 230 231 destruct!(T, !doFree)(obj_); 232 233 // Free memory if need be. 234 static if (doFree) 235 heap.free(cast(void*)obj_); 236 237 obj_ = null; 238 } 239 } 240 } 241 242 /** 243 Finalizes the objects referenced by $(D objects) by calling the 244 destructors of its members (if any). 245 246 Notes: 247 If $(D doFree) is $(D true), memory associated with each object 248 will additionally be freed after finalizers have run; otherwise the object 249 is reset to its original state. 250 251 Params: 252 objects = The objects to delete. 253 */ 254 void nogc_delete(T, bool doFree=true)(T[] objects) @trusted { 255 foreach(i; 0..objects.length) 256 nogc_delete!(T, doFree)(objects[i]); 257 } 258 259 /** 260 Attempts to finalize $(D obj_) by calling its destructor (if any). 261 262 If $(D doFree) is $(D true), memory associated with obj_ will additionally be freed 263 after finalizers have run; otherwise the object is reset to its original state. 264 265 Params: 266 obj_ = Instance to destroy and deallocate. 267 268 Returns: 269 Whether the operation succeeded. 270 */ 271 bool nogc_trydelete(T, bool doFree=true)(ref T obj_) @trusted nothrow { 272 try { 273 nogc_delete!(T, doFree)(obj_); 274 return true; 275 276 } catch (Exception ex) { 277 if (ex) 278 assumeNoThrowNoGC((Exception ex) { nogc_delete(ex); }, ex); 279 280 return false; 281 } 282 } 283 284 /** 285 Attempts to deallocate the specified instance of $(D T) from the specified heap. 286 Finalizes $(D obj_) by calling its destructor (if any). 287 288 Notes: 289 If $(D doFree) is $(D true), memory associated with obj_ will additionally be freed 290 after finalizers have run; otherwise the object is reset to its original state. 291 292 Params: 293 heap = The heap to allocate the instance on. 294 obj_ = Instance to destroy and deallocate. 295 296 Returns: 297 Whether the operation succeeded. 298 */ 299 bool nogc_trydelete(T, bool doFree=true)(NuHeap heap, ref T obj_) @trusted nothrow { 300 static if (isHeapAllocated!T) { 301 try { 302 303 nogc_delete!(T, doFree)(heap, obj_); 304 return true; 305 } catch (Exception ex) { 306 307 if (ex) 308 assumeNoThrowNoGC((Exception ex) { nogc_delete(ex); }, ex); 309 return false; 310 } 311 } 312 } 313 314 /** 315 Attempts to finalize the objects referenced by $(D objects) by calling the 316 destructors of its members (if any). 317 318 Notes: 319 If $(D doFree) is $(D true), memory associated with each object 320 will additionally be freed after finalizers have run; otherwise the object 321 is reset to its original state. 322 323 Params: 324 objects = The objects to try to delete. 325 326 Returns: 327 Whether the operation succeeded. 328 */ 329 bool nogc_trydelete(T, bool doFree=true)(T[] objects) @trusted { 330 size_t failed = 0; 331 foreach(i; 0..objects.length) 332 failed += nogc_trydelete!(T, doFree)(objects[i]); 333 334 return failed != 0; 335 } 336 337 /** 338 Allocates a new instance of $(D T) using the C++ allocation 339 strategy. 340 341 Notes: 342 $(D doXCtor) is used to specify whether to also call any D mangled 343 constructors defined for the C++ type. 344 345 See_Also: 346 @(D nogc_new), $(D nogc_trynew), $(D cpp_trynew), 347 */ 348 Ref!T cpp_new(T, bool doXCtor=true, Args...)(auto ref Args args) @trusted { 349 return _nu_cpp_new!(T, doXCtor, Args)(forward!args); 350 } 351 352 /** 353 Attempts to allocate a new instance of $(D T) using the C++ allocation 354 strategy. 355 356 Notes: 357 $(D doXCtor) is used to specify whether to also call any D mangled 358 constructors defined for the C++ type. 359 360 See_Also: 361 @(D nogc_new), $(D nogc_trynew), $(D cpp_new) 362 */ 363 Ref!T cpp_trynew(T, bool doXCtor=true, Args...)(auto ref Args args) @trusted nothrow { 364 return assumeNoThrowNoGC((Args args) => _nu_cpp_new!(T, doXCtor, Args)(forward!args), args); 365 } 366 367 /** 368 Finalizes $(D obj_) by calling its destructor (if any). 369 370 Notes: 371 $(D doXDtor) is used to specify whether to also call any D mangled 372 destructors defined for the C++ type. 373 374 Params: 375 obj_ = Instance to destroy and deallocate. 376 377 See_Also: 378 @(D nogc_delete), $(D nogc_trydelete), $(D cpp_trydelete) 379 */ 380 void cpp_delete(T, bool doXDtor=true)(ref T obj_) @trusted if (isCPP!T) { 381 _nu_cpp_delete!(T, doXDtor)(obj_); 382 } 383 384 /** 385 Finalizes the objects referenced by $(D objects) by calling the 386 destructors of its members (if any). 387 388 Notes: 389 $(D doXDtor) is used to specify whether to also call any D mangled 390 destructors defined for the C++ type. 391 392 Params: 393 objects = The objects to delete. 394 395 See_Also: 396 @(D nogc_delete), $(D nogc_trydelete), $(D cpp_trydelete) 397 */ 398 void cpp_delete(T, bool doXDtor=true)(T[] objects) @trusted if (isCPP!T) { 399 foreach(i; 0..objects.length) 400 _nu_cpp_delete!(T, doXDtor)(objects[i]); 401 } 402 403 /** 404 Attempts to finalize the objects referenced by $(D objects) by calling the 405 destructors of its members (if any). 406 407 Notes: 408 $(D doXDtor) is used to specify whether to also call any D mangled 409 destructors defined for the C++ type. 410 411 Params: 412 objects = The objects to try to delete. 413 414 Returns: 415 Whether the operation succeeded. 416 417 See_Also: 418 @(D nogc_delete), $(D nogc_trydelete), $(D cpp_delete) 419 */ 420 bool cpp_trydelete(T, bool doFree=true)(T[] objects) @trusted { 421 size_t failed = 0; 422 foreach(i; 0..objects.length) 423 failed += cpp_trydelete!(T, doFree)(objects[i]); 424 425 return failed != 0; 426 } 427 428 /** 429 Attempts to finalize $(D obj_) by calling its C++ destructor. 430 431 Notes: 432 $(D doXDtor) is used to specify whether to also call any D mangled 433 destructors defined for the C++ type. 434 435 Params: 436 obj_ = Instance to destroy and deallocate. 437 438 Returns: 439 Whether the operation succeeded. 440 441 See_Also: 442 @(D nogc_delete), $(D nogc_trydelete), $(D cpp_delete) 443 */ 444 bool cpp_trydelete(T, bool doXDtor=true)(ref T obj_) @trusted nothrow if (isCPP!T) { 445 try { 446 447 _nu_cpp_delete!(T, doXDtor)(obj_); 448 return true; 449 } catch (Exception ex) { 450 if (ex) 451 assumeNoThrowNoGC((Exception ex) { nogc_delete(ex); }, ex); 452 453 return false; 454 } 455 } 456 457 /** 458 Initializes the object at the memory in $(D dst), filling it out with 459 its default state. 460 461 This variant will also initialize class instances on the stack which 462 can then be constructed with $(D nogc_construct). However you may 463 still need to $(D nogc_delete) these instances, with $(D doFree) set 464 to false. 465 466 Params: 467 dst = A memory range allocated, big enough to store $(D T) 468 469 Returns: 470 A reference to the initialized element, or $(D T.init) if it failed. 471 */ 472 T nogc_initialize(T)(void[] dst) @trusted { 473 if (dst.length < AllocSize!T) 474 return T.init; 475 476 T tmp = cast(T)dst.ptr; 477 initializeAt(tmp); 478 return tmp; 479 } 480 481 /** 482 Initializes the object at $(D element), filling it out with 483 its default state. 484 485 Params: 486 element = A reference to the allocated memory to initialize. 487 488 Returns: 489 A reference to the initialized element. 490 */ 491 ref T nogc_initialize(T)(ref T element) @trusted { 492 initializeAtNoCtx(element); 493 return element; 494 } 495 496 /** 497 Initializes the objects at $(D elements), filling them out with 498 their default state. 499 500 Params: 501 elements = A slice of the elements to initialize 502 503 Returns: 504 The slice with now initialized contents. 505 */ 506 T[] nogc_initialize(T)(T[] elements) @trusted { 507 foreach(i; 0..elements.length) 508 initializeAtNoCtx(elements[i]); 509 510 return elements; 511 } 512 513 /** 514 Zero-fills an object 515 */ 516 void nogc_zeroinit(T)(ref T element) @trusted nothrow pure { 517 nu_memset(&element, 0, element.sizeof); 518 } 519 520 /** 521 Zero-fills an object 522 */ 523 void nogc_zeroinit(T)(T[] elements) @trusted nothrow pure { 524 nu_memset(elements.ptr, 0, elements.length*T.sizeof); 525 } 526 527 /** 528 Allocates a new class on the heap. 529 Immediately exits the application if out of memory. 530 */ 531 void nogc_emplace(T, Args...)(auto ref T dest, Args args) @trusted { 532 emplace!(T, T, Args)(dest, args); 533 } 534 535 /** 536 Moves elements in $(D src) to $(D dst) via destructive copy. 537 538 If an element in $(D src) is not a valid object, 539 then accessing the moved element in $(D to) will be undefined behaviour. 540 541 After the move operation, the original memory locations in $(D from) will be 542 reset to their base initialized state before any constructors are run. 543 */ 544 void nogc_move(T)(T[] dst, T[] src) @trusted { 545 size_t toTx = nu_min(dst.length, src.length); 546 547 foreach(i; 0..toTx) 548 __move(src[i], dst[i]); 549 } 550 551 /** 552 Copies $(D src) to $(D dst) via a blit operation. 553 554 Postblits and copy constructors will be called subsequently. 555 */ 556 void nogc_copy(T)(T[] dst, T[] src) @trusted { 557 size_t toTx = nu_min(dst.length, src.length); 558 559 foreach(i; 0..toTx) 560 __copy(src[i], dst[i]); 561 } 562 563 /** 564 Moves $(D from) to $(D to) via a destructive copy. 565 566 If $(D from) is not a valid object, then accessing $(D to) will be undefined 567 behaviour. 568 569 After the move operation, the original memory location of $(D from) will be 570 reset to its base initialized state before any constructors are run. 571 572 Params: 573 from = The source of the move operation. 574 to = The destination of the move operation. 575 */ 576 void moveTo(T)(ref T from, ref T to) @trusted { 577 __move(from, to); 578 } 579 580 /** 581 Moves $(D from). 582 583 Useful for moving stack allocated structs. 584 585 Params: 586 from = The source of the move operation. 587 588 Returns: 589 The moved value, $(D from) will be reset to its initial state. 590 */ 591 T move(T)(scope ref return T from) @trusted { 592 return __move(from); 593 } 594 595 /** 596 Copies $(D from) to $(D to) via a blit operation. 597 598 Postblits and copy constructors will be called subsequently. 599 600 Params: 601 from = The source of the copy operation. 602 to = The destination of the copy operation. 603 */ 604 void copyTo(T)(ref T from, ref T to) @trusted { 605 __copy(from, to); 606 }