1 /** 2 Numem Core Hooks. 3 4 This file contains all the core hooks numem calls internally to handle memory. 5 Given that some platforms may not have a C standard library, these hooks allow you 6 to override how numem handles memory for such platforms from an external library. 7 8 In this case, most of the hooks presented here will need to be implemented to cover 9 all of the used internal hooks within numem. 10 11 Hooks which are prefixed $(D nuopt_) are optional and may be omitted, they are 12 usually for language-specific interoperability. 13 14 Copyright: 15 Copyright © 2023-2025, Kitsunebi Games 16 Copyright © 2023-2025, Inochi2D Project 17 18 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 19 Authors: Luna Nielsen 20 */ 21 module numem.core.hooks; 22 import core.attribute : weak; 23 24 /** 25 Allocates $(D bytes) worth of memory. 26 27 Params: 28 bytes = How many bytes to allocate. 29 30 Returns: 31 Newly allocated memory or $(D null) on failure. 32 To avoid a memory leak, free the memory with $(D nu_free). 33 34 Notes: 35 Given the implementation of $(D nu_malloc) and $(D nu_free) may be 36 independent of the libc allocator, memory allocated with 37 $(D nu_malloc) should $(B always) be freed with $(D nu_free)! 38 */ 39 export 40 extern(C) 41 void* nu_malloc(size_t bytes) @nogc nothrow @system pure @weak { 42 pragma(mangle, "malloc") 43 extern extern(C) void* malloc(size_t) @nogc nothrow @system pure; 44 45 return malloc(bytes); 46 } 47 48 /** 49 Reallocates memory prior allocated with $(D nu_malloc) or 50 $(D nu_alignedalloc). 51 52 This function may re-allocate the memory if resizing the allocation 53 to the new size is not possible. 54 55 Params: 56 data = Pointer to prior allocated memory. 57 newSize = New size of the allocation, in bytes. 58 59 Returns: 60 The address of the reallocated memory or $(D null) on failure. 61 To avoid a memory leak, free the memory with $(D nu_free). 62 63 Notes: 64 Given the implementation of $(D nu_realloc) and $(D nu_free) may be 65 independent of the libc allocator, memory allocated with 66 $(D nu_realloc) should $(B always) be freed with $(D nu_free)! 67 */ 68 export 69 extern(C) 70 void* nu_realloc(void* data, size_t newSize) @nogc nothrow @system pure @weak { 71 pragma(mangle, "realloc") 72 extern extern(C) void* realloc(void*, size_t) @nogc nothrow @system pure; 73 74 return realloc(data, newSize); 75 } 76 77 /** 78 Frees allocated memory. 79 80 Params: 81 data = Pointer to start of memory prior allocated. 82 83 Notes: 84 Given the implementation of the allocators and $(D nu_free) may be 85 independent of the libc allocator, memory allocated with 86 numem functions should $(B always) be freed with $(D nu_free)! 87 */ 88 export 89 extern(C) 90 void nu_free(void* data) @nogc nothrow @system pure @weak { 91 pragma(mangle, "free") 92 extern extern(C) void free(void*) @nogc nothrow @system pure; 93 94 return free(data); 95 } 96 97 /** 98 Copies $(D bytes) worth of data from $(D src) into $(D dst). 99 100 $(D src) and $(D dst) needs to be allocated and within range, 101 additionally the source and destination may not overlap. 102 103 Params: 104 dst = Destination of the memory copy operation. 105 src = Source of the memory copy operation 106 bytes = The amount of bytes to copy. 107 */ 108 export 109 extern(C) 110 void* nu_memcpy(return scope void* dst, scope const void* src, size_t bytes) @nogc nothrow @system pure @weak { 111 pragma(mangle, "memcpy") 112 extern extern(C) void* memcpy(return scope void*, scope const void*, size_t) @nogc nothrow @system pure; 113 114 return memcpy(dst, src, bytes); 115 } 116 117 /** 118 Moves $(D bytes) worth of data from $(D src) into $(D dst). 119 120 $(D src) and $(D dst) needs to be allocated and within range. 121 122 Params: 123 dst = Destination of the memory copy operation. 124 src = Source of the memory copy operation 125 bytes = The amount of bytes to copy. 126 127 Returns: 128 Pointer to $(D dst) 129 */ 130 export 131 extern(C) 132 void* nu_memmove(return scope void* dst, scope const void* src, size_t bytes) @nogc nothrow @system pure @weak { 133 pragma(mangle, "memmove") 134 extern extern(C) void* memmove(return scope void*, scope const void*, size_t) @nogc nothrow @system pure; 135 136 return memmove(dst, src, bytes); 137 } 138 139 /** 140 Fills $(D dst) with $(D bytes) amount of $(D value)s. 141 142 Params: 143 dst = Destination of the memory copy operation. 144 value = The byte to repeatedly copy to the memory starting at $(D dst) 145 bytes = The amount of bytes to write. 146 147 Returns: 148 Pointer to $(D dst) 149 */ 150 export 151 extern(C) 152 void* nu_memset(return scope void* dst, ubyte value, size_t bytes) @nogc nothrow @system pure @weak { 153 pragma(mangle, "memset") 154 extern extern(C) void* memset(return scope void*, int, size_t) @nogc nothrow @system pure; 155 156 return memset(dst, value, bytes); 157 } 158 159 /** 160 Called internally by numem if a fatal error occured. 161 162 This function $(B should) exit the application and if possible, 163 print an error. But it $(B may) not print an error. 164 165 Notes: 166 This function may trigger a trap to alert a debugger to 167 create a breakpoint. 168 169 Params: 170 errMsg = A D string containing the error in question. 171 172 Returns: 173 Never returns, the application should crash at this point. 174 */ 175 export 176 extern(C) 177 void nu_fatal(const(char)[] errMsg) @nogc nothrow @system pure @weak { 178 pragma(mangle, "printf") 179 extern extern(C) void printf(const(char)*, ...) @nogc nothrow @system pure; 180 pragma(mangle, "abort") 181 extern extern(C) void abort() @nogc nothrow @system pure; 182 183 // Print to stderr 184 printf("fatal error: %.*s\n", cast(int)errMsg.length, errMsg.ptr); 185 186 // Debugger trap 187 debug { 188 version(LDC) { 189 import ldc.intrinsics : llvm_debugtrap; 190 191 llvm_debugtrap(); 192 } else version(DigitalMars) { 193 version(X86) { 194 asm @nogc pure nothrow { 195 int 0x03; 196 } 197 } else version(X86_64) { 198 asm @nogc pure nothrow { 199 int 0x03; 200 } 201 } else version(AArch64) { 202 asm @nogc pure nothrow { 203 inst 0xd4200000; 204 } 205 } 206 } 207 } 208 209 // Final abort. 210 abort(); 211 } 212 213 /** 214 Gets the status of atomics support in the current 215 numem configuration. 216 217 Notes: 218 If atomics are not supported, these functions will 219 act as non-atomic alternatives. 220 221 Returns: 222 Whether atomics are supported by the loaded 223 hookset. 224 */ 225 export 226 extern(C) 227 bool nu_atomic_supported() @nogc nothrow @system pure @weak { 228 version(LDC) { 229 return true; 230 } else return false; 231 } 232 233 /** 234 Inserts a memory acquire barrier. 235 */ 236 export 237 extern(C) 238 void nu_atomic_barrier_acquire() @nogc nothrow @system pure @weak { 239 version(LDC) { 240 import ldc.intrinsics; 241 llvm_memory_fence(AtomicOrdering.Acquire, SynchronizationScope.CrossThread); 242 } 243 } 244 245 /** 246 Inserts a memory release barrier. 247 */ 248 export 249 extern(C) 250 void nu_atomic_barrier_release() @nogc nothrow @system pure @weak { 251 version(LDC) { 252 import ldc.intrinsics; 253 llvm_memory_fence(AtomicOrdering.Release, SynchronizationScope.CrossThread); 254 } 255 } 256 257 /** 258 Loads a 32 bit value atomically. 259 */ 260 export 261 extern(C) 262 inout(uint) nu_atomic_load_32(ref inout(uint) src) @nogc nothrow @system pure @weak { 263 version(LDC) { 264 import ldc.intrinsics; 265 return llvm_atomic_load!(inout(uint))(cast(shared(inout(uint)*))&src); 266 } else return src; 267 } 268 269 /** 270 Stores a 32 bit value atomically. 271 */ 272 export 273 extern(C) 274 void nu_atomic_store_32(ref uint dst, uint value) @nogc nothrow @system pure @weak { 275 version(LDC) { 276 import ldc.intrinsics; 277 llvm_atomic_store!(uint)(value, cast(shared(uint*))&dst); 278 } else { 279 dst = value; 280 } 281 } 282 283 /** 284 Adds a 32 bit value atomically. 285 */ 286 export 287 extern(C) 288 extern uint nu_atomic_add_32(ref uint dst, uint value) @nogc nothrow @system pure @weak { 289 version(LDC) { 290 291 import ldc.intrinsics; 292 return llvm_atomic_rmw_add!(uint)(cast(shared(uint*))&dst, value); 293 } else { 294 uint oldval = dst; 295 dst += value; 296 return oldval; 297 } 298 } 299 300 /** 301 Subtracts a 32 bit value atomically. 302 */ 303 export 304 extern(C) 305 extern uint nu_atomic_sub_32(ref uint dst, uint value) @nogc nothrow @system pure @weak { 306 version(LDC) { 307 308 import ldc.intrinsics; 309 return llvm_atomic_rmw_sub!(uint)(cast(shared(uint*))&dst, value); 310 } else { 311 uint oldval = dst; 312 dst -= value; 313 return oldval; 314 } 315 } 316 317 /** 318 Loads a pointer value atomically. 319 */ 320 export 321 extern(C) 322 extern inout(void)* nu_atomic_load_ptr(inout(void)** src) @nogc nothrow @system pure @weak { 323 version(LDC) { 324 325 import ldc.intrinsics; 326 return llvm_atomic_load!(inout(void)*)(cast(shared(inout(void)**))src); 327 } else { 328 return *src; 329 } 330 } 331 332 /** 333 Stores a pointer value atomically. 334 */ 335 export 336 extern(C) 337 extern void nu_atomic_store_ptr(void** dst, void* value) @nogc nothrow @system pure @weak { 338 version(LDC) { 339 340 import ldc.intrinsics; 341 llvm_atomic_store!(void*)(value, cast(shared(void**))dst); 342 } else { 343 *dst = value; 344 } 345 } 346 347 /** 348 Compares variable at $(D dst) and swaps it if it contains $(D oldvalue). 349 */ 350 export 351 extern(C) 352 extern bool nu_atomic_cmpxhg_ptr(void** dst, void* oldvalue, void* value) @nogc nothrow @system pure @weak { 353 version(LDC) { 354 355 import ldc.intrinsics; 356 return llvm_atomic_cmp_xchg!(void*)(cast(shared(void**))dst, oldvalue, value).exchanged; 357 } else { 358 if (*dst is oldvalue) { 359 *dst = value; 360 return true; 361 } 362 363 return false; 364 } 365 } 366 367 /** 368 Hooks for handling auto release pools. 369 370 These $(I optional) pool callback functions allows implementers of 371 numem to hook into the auto release pool system. 372 373 Push should push a new context onto an internal stack, while pop should 374 release an element from the stack. 375 376 These functions are mainly useful for Objective-C interoperability. 377 378 See_Also: 379 $(LINK2 https://clang.llvm.org/docs/AutomaticReferenceCounting.html#autoreleasepool, ARC Documentation) 380 */ 381 extern(C) 382 __gshared void* function() @nogc nothrow @system nuopt_autoreleasepool_push = null; 383 384 /// ditto 385 extern(C) 386 __gshared void function(void*) @nogc nothrow @system nuopt_autoreleasepool_pop = null;