2022-07-15 14:33:44 +02:00
|
|
|
/**
|
|
|
|
* This module provides an interface to the garbage collector used by
|
|
|
|
* applications written in the D programming language. It allows the
|
|
|
|
* garbage collector in the runtime to be swapped without affecting
|
|
|
|
* binary compatibility of applications.
|
|
|
|
*
|
|
|
|
* Using this module is not necessary in typical D code. It is mostly
|
|
|
|
* useful when doing low-level _memory management.
|
|
|
|
*
|
|
|
|
* Notes_to_users:
|
|
|
|
*
|
|
|
|
$(OL
|
|
|
|
$(LI The GC is a conservative mark-and-sweep collector. It only runs a
|
|
|
|
collection cycle when an allocation is requested of it, never
|
|
|
|
otherwise. Hence, if the program is not doing allocations,
|
|
|
|
there will be no GC collection pauses. The pauses occur because
|
|
|
|
all threads the GC knows about are halted so the threads' stacks
|
|
|
|
and registers can be scanned for references to GC allocated data.
|
|
|
|
)
|
|
|
|
|
|
|
|
$(LI The GC does not know about threads that were created by directly calling
|
|
|
|
the OS/C runtime thread creation APIs and D threads that were detached
|
|
|
|
from the D runtime after creation.
|
|
|
|
Such threads will not be paused for a GC collection, and the GC might not detect
|
|
|
|
references to GC allocated data held by them. This can cause memory corruption.
|
|
|
|
There are several ways to resolve this issue:
|
|
|
|
$(OL
|
|
|
|
$(LI Do not hold references to GC allocated data in such threads.)
|
|
|
|
$(LI Register/unregister such data with calls to $(LREF addRoot)/$(LREF removeRoot) and
|
|
|
|
$(LREF addRange)/$(LREF removeRange).)
|
|
|
|
$(LI Maintain another reference to that same data in another thread that the
|
|
|
|
GC does know about.)
|
|
|
|
$(LI Disable GC collection cycles while that thread is active with $(LREF disable)/$(LREF enable).)
|
2023-06-18 01:43:18 +01:00
|
|
|
$(LI Register the thread with the GC using $(REF thread_attachThis, core,thread,osthread)/$(REF thread_detachThis, core,thread,threadbase).)
|
2022-07-15 14:33:44 +02:00
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
*
|
|
|
|
* Notes_to_implementors:
|
|
|
|
* $(UL
|
2023-06-18 01:43:18 +01:00
|
|
|
* $(LI On POSIX systems, the signals `SIGRTMIN` and `SIGRTMIN + 1` are reserved
|
2022-07-15 14:33:44 +02:00
|
|
|
* by this module for use in the garbage collector implementation.
|
|
|
|
* Typically, they will be used to stop and resume other threads
|
|
|
|
* when performing a collection, but an implementation may choose
|
|
|
|
* not to use this mechanism (or not stop the world at all, in the
|
|
|
|
* case of concurrent garbage collectors).)
|
|
|
|
*
|
|
|
|
* $(LI Registers, the stack, and any other _memory locations added through
|
|
|
|
* the $(D GC.$(LREF addRange)) function are always scanned conservatively.
|
|
|
|
* This means that even if a variable is e.g. of type $(D float),
|
|
|
|
* it will still be scanned for possible GC pointers. And, if the
|
|
|
|
* word-interpreted representation of the variable matches a GC-managed
|
|
|
|
* _memory block's address, that _memory block is considered live.)
|
|
|
|
*
|
|
|
|
* $(LI Implementations are free to scan the non-root heap in a precise
|
|
|
|
* manner, so that fields of types like $(D float) will not be considered
|
|
|
|
* relevant when scanning the heap. Thus, casting a GC pointer to an
|
|
|
|
* integral type (e.g. $(D size_t)) and storing it in a field of that
|
|
|
|
* type inside the GC heap may mean that it will not be recognized
|
|
|
|
* if the _memory block was allocated with precise type info or with
|
|
|
|
* the $(D GC.BlkAttr.$(LREF NO_SCAN)) attribute.)
|
|
|
|
*
|
|
|
|
* $(LI Destructors will always be executed while other threads are
|
|
|
|
* active; that is, an implementation that stops the world must not
|
|
|
|
* execute destructors until the world has been resumed.)
|
|
|
|
*
|
|
|
|
* $(LI A destructor of an object must not access object references
|
|
|
|
* within the object. This means that an implementation is free to
|
|
|
|
* optimize based on this rule.)
|
|
|
|
*
|
|
|
|
* $(LI An implementation is free to perform heap compaction and copying
|
|
|
|
* so long as no valid GC pointers are invalidated in the process.
|
|
|
|
* However, _memory allocated with $(D GC.BlkAttr.$(LREF NO_MOVE)) must
|
|
|
|
* not be moved/copied.)
|
|
|
|
*
|
|
|
|
* $(LI Implementations must support interior pointers. That is, if the
|
|
|
|
* only reference to a GC-managed _memory block points into the
|
|
|
|
* middle of the block rather than the beginning (for example), the
|
|
|
|
* GC must consider the _memory block live. The exception to this
|
|
|
|
* rule is when a _memory block is allocated with the
|
|
|
|
* $(D GC.BlkAttr.$(LREF NO_INTERIOR)) attribute; it is the user's
|
|
|
|
* responsibility to make sure such _memory blocks have a proper pointer
|
|
|
|
* to them when they should be considered live.)
|
|
|
|
*
|
|
|
|
* $(LI It is acceptable for an implementation to store bit flags into
|
|
|
|
* pointer values and GC-managed _memory blocks, so long as such a
|
|
|
|
* trick is not visible to the application. In practice, this means
|
|
|
|
* that only a stop-the-world collector can do this.)
|
|
|
|
*
|
|
|
|
* $(LI Implementations are free to assume that GC pointers are only
|
|
|
|
* stored on word boundaries. Unaligned pointers may be ignored
|
|
|
|
* entirely.)
|
|
|
|
*
|
|
|
|
* $(LI Implementations are free to run collections at any point. It is,
|
|
|
|
* however, recommendable to only do so when an allocation attempt
|
|
|
|
* happens and there is insufficient _memory available.)
|
|
|
|
* )
|
|
|
|
*
|
|
|
|
* Copyright: Copyright Sean Kelly 2005 - 2015.
|
|
|
|
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
|
|
|
* Authors: Sean Kelly, Alex Rønne Petersen
|
|
|
|
* Source: $(DRUNTIMESRC core/_memory.d)
|
|
|
|
*/
|
|
|
|
|
|
|
|
module core.memory;
|
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
version (ARM)
|
|
|
|
version = AnyARM;
|
|
|
|
else version (AArch64)
|
|
|
|
version = AnyARM;
|
|
|
|
|
|
|
|
version (iOS)
|
|
|
|
version = iOSDerived;
|
|
|
|
else version (TVOS)
|
|
|
|
version = iOSDerived;
|
|
|
|
else version (WatchOS)
|
|
|
|
version = iOSDerived;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
private
|
|
|
|
{
|
|
|
|
extern (C) uint gc_getAttr( void* p ) pure nothrow;
|
|
|
|
extern (C) uint gc_setAttr( void* p, uint a ) pure nothrow;
|
|
|
|
extern (C) uint gc_clrAttr( void* p, uint a ) pure nothrow;
|
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
extern (C) void* gc_addrOf( void* p ) pure nothrow @nogc;
|
|
|
|
extern (C) size_t gc_sizeOf( void* p ) pure nothrow @nogc;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
struct BlkInfo_
|
|
|
|
{
|
|
|
|
void* base;
|
|
|
|
size_t size;
|
|
|
|
uint attr;
|
|
|
|
}
|
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
extern (C) BlkInfo_ gc_query(return scope void* p) pure nothrow;
|
|
|
|
extern (C) GC.Stats gc_stats ( ) @safe nothrow @nogc;
|
|
|
|
extern (C) GC.ProfileStats gc_profileStats ( ) nothrow @nogc @safe;
|
|
|
|
}
|
2022-07-15 14:33:44 +02:00
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
version (CoreDoc)
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* The minimum size of a system page in bytes.
|
|
|
|
*
|
|
|
|
* This is a compile time, platform specific value. This value might not
|
|
|
|
* be accurate, since it might be possible to change this value. Whenever
|
|
|
|
* possible, please use $(LREF pageSize) instead, which is initialized
|
|
|
|
* during runtime.
|
|
|
|
*
|
|
|
|
* The minimum size is useful when the context requires a compile time known
|
|
|
|
* value, like the size of a static array: `ubyte[minimumPageSize] buffer`.
|
|
|
|
*/
|
|
|
|
enum minimumPageSize : size_t;
|
|
|
|
}
|
|
|
|
else version (AnyARM)
|
|
|
|
{
|
|
|
|
version (iOSDerived)
|
|
|
|
enum size_t minimumPageSize = 16384;
|
|
|
|
else
|
|
|
|
enum size_t minimumPageSize = 4096;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
enum size_t minimumPageSize = 4096;
|
|
|
|
|
|
|
|
///
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
ubyte[minimumPageSize] buffer;
|
|
|
|
}
|
2022-07-15 14:33:44 +02:00
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
/**
|
|
|
|
* The size of a system page in bytes.
|
|
|
|
*
|
|
|
|
* This value is set at startup time of the application. It's safe to use
|
|
|
|
* early in the start process, like in shared module constructors and
|
|
|
|
* initialization of the D runtime itself.
|
|
|
|
*/
|
|
|
|
immutable size_t pageSize;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
///
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
ubyte[] buffer = new ubyte[pageSize];
|
2022-07-15 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
// The reason for this elaborated way of declaring a function is:
|
|
|
|
//
|
|
|
|
// * `pragma(crt_constructor)` is used to declare a constructor that is called by
|
|
|
|
// the C runtime, before C main. This allows the `pageSize` value to be used
|
|
|
|
// during initialization of the D runtime. This also avoids any issues with
|
|
|
|
// static module constructors and circular references.
|
|
|
|
//
|
|
|
|
// * `pragma(mangle)` is used because `pragma(crt_constructor)` requires a
|
|
|
|
// function with C linkage. To avoid any name conflict with other C symbols,
|
|
|
|
// standard D mangling is used.
|
|
|
|
//
|
|
|
|
// * The extra function declaration, without the body, is to be able to get the
|
|
|
|
// D mangling of the function without the need to hardcode the value.
|
|
|
|
//
|
|
|
|
// * The extern function declaration also has the side effect of making it
|
|
|
|
// impossible to manually call the function with standard syntax. This is to
|
|
|
|
// make it more difficult to call the function again, manually.
|
|
|
|
private void initialize();
|
|
|
|
pragma(crt_constructor)
|
|
|
|
pragma(mangle, initialize.mangleof)
|
|
|
|
private extern (C) void _initialize() @system
|
|
|
|
{
|
|
|
|
version (Posix)
|
|
|
|
{
|
|
|
|
import core.sys.posix.unistd : sysconf, _SC_PAGESIZE;
|
|
|
|
|
|
|
|
(cast() pageSize) = cast(size_t) sysconf(_SC_PAGESIZE);
|
|
|
|
}
|
|
|
|
else version (Windows)
|
|
|
|
{
|
|
|
|
import core.sys.windows.winbase : GetSystemInfo, SYSTEM_INFO;
|
|
|
|
|
|
|
|
SYSTEM_INFO si;
|
|
|
|
GetSystemInfo(&si);
|
|
|
|
(cast() pageSize) = cast(size_t) si.dwPageSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
static assert(false, __FUNCTION__ ~ " is not implemented on this platform");
|
|
|
|
}
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This struct encapsulates all garbage collection functionality for the D
|
|
|
|
* programming language.
|
|
|
|
*/
|
|
|
|
struct GC
|
|
|
|
{
|
|
|
|
@disable this();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Aggregation of GC stats to be exposed via public API
|
|
|
|
*/
|
|
|
|
static struct Stats
|
|
|
|
{
|
|
|
|
/// number of used bytes on the GC heap (might only get updated after a collection)
|
|
|
|
size_t usedSize;
|
|
|
|
/// number of free bytes on the GC heap (might only get updated after a collection)
|
|
|
|
size_t freeSize;
|
2023-06-18 01:43:18 +01:00
|
|
|
/// number of bytes allocated for current thread since program start
|
|
|
|
ulong allocatedInCurrentThread;
|
2022-07-15 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
/**
|
|
|
|
* Aggregation of current profile information
|
|
|
|
*/
|
|
|
|
static struct ProfileStats
|
|
|
|
{
|
|
|
|
import core.time : Duration;
|
|
|
|
/// total number of GC cycles
|
|
|
|
size_t numCollections;
|
|
|
|
/// total time spent doing GC
|
|
|
|
Duration totalCollectionTime;
|
|
|
|
/// total time threads were paused doing GC
|
|
|
|
Duration totalPauseTime;
|
|
|
|
/// largest time threads were paused during one GC cycle
|
|
|
|
Duration maxPauseTime;
|
|
|
|
/// largest time spent doing one GC cycle
|
|
|
|
Duration maxCollectionTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern(C):
|
|
|
|
|
2022-07-15 14:33:44 +02:00
|
|
|
/**
|
|
|
|
* Enables automatic garbage collection behavior if collections have
|
|
|
|
* previously been suspended by a call to disable. This function is
|
|
|
|
* reentrant, and must be called once for every call to disable before
|
|
|
|
* automatic collections are enabled.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
pragma(mangle, "gc_enable") static void enable() nothrow pure;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Disables automatic garbage collections performed to minimize the
|
|
|
|
* process footprint. Collections may continue to occur in instances
|
|
|
|
* where the implementation deems necessary for correct program behavior,
|
|
|
|
* such as during an out of memory condition. This function is reentrant,
|
|
|
|
* but enable must be called once for each call to disable.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
pragma(mangle, "gc_disable") static void disable() nothrow pure;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Begins a full collection. While the meaning of this may change based
|
|
|
|
* on the garbage collector implementation, typical behavior is to scan
|
|
|
|
* all stack segments for roots, mark accessible memory blocks as alive,
|
|
|
|
* and then to reclaim free space. This action may need to suspend all
|
|
|
|
* running threads for at least part of the collection process.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
pragma(mangle, "gc_collect") static void collect() nothrow pure;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Indicates that the managed memory space be minimized by returning free
|
|
|
|
* physical memory to the operating system. The amount of free memory
|
|
|
|
* returned depends on the allocator design and on program behavior.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
pragma(mangle, "gc_minimize") static void minimize() nothrow pure;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
extern(D):
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Elements for a bit field representing memory block attributes. These
|
|
|
|
* are manipulated via the getAttr, setAttr, clrAttr functions.
|
|
|
|
*/
|
|
|
|
enum BlkAttr : uint
|
|
|
|
{
|
|
|
|
NONE = 0b0000_0000, /// No attributes set.
|
|
|
|
FINALIZE = 0b0000_0001, /// Finalize the data in this block on collect.
|
|
|
|
NO_SCAN = 0b0000_0010, /// Do not scan through this block on collect.
|
|
|
|
NO_MOVE = 0b0000_0100, /// Do not move this memory block on collect.
|
|
|
|
/**
|
|
|
|
This block contains the info to allow appending.
|
|
|
|
|
|
|
|
This can be used to manually allocate arrays. Initial slice size is 0.
|
|
|
|
|
|
|
|
Note: The slice's usable size will not match the block size. Use
|
|
|
|
$(LREF capacity) to retrieve actual usable capacity.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
----
|
|
|
|
// Allocate the underlying array.
|
|
|
|
int* pToArray = cast(int*)GC.malloc(10 * int.sizeof, GC.BlkAttr.NO_SCAN | GC.BlkAttr.APPENDABLE);
|
|
|
|
// Bind a slice. Check the slice has capacity information.
|
|
|
|
int[] slice = pToArray[0 .. 0];
|
|
|
|
assert(capacity(slice) > 0);
|
|
|
|
// Appending to the slice will not relocate it.
|
|
|
|
slice.length = 5;
|
|
|
|
slice ~= 1;
|
|
|
|
assert(slice.ptr == p);
|
|
|
|
----
|
|
|
|
*/
|
|
|
|
APPENDABLE = 0b0000_1000,
|
|
|
|
|
|
|
|
/**
|
|
|
|
This block is guaranteed to have a pointer to its base while it is
|
|
|
|
alive. Interior pointers can be safely ignored. This attribute is
|
|
|
|
useful for eliminating false pointers in very large data structures
|
|
|
|
and is only implemented for data structures at least a page in size.
|
|
|
|
*/
|
|
|
|
NO_INTERIOR = 0b0001_0000,
|
|
|
|
|
|
|
|
STRUCTFINAL = 0b0010_0000, // the block has a finalizer for (an array of) structs
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contains aggregate information about a block of managed memory. The
|
|
|
|
* purpose of this struct is to support a more efficient query style in
|
|
|
|
* instances where detailed information is needed.
|
|
|
|
*
|
|
|
|
* base = A pointer to the base of the block in question.
|
|
|
|
* size = The size of the block, calculated from base.
|
|
|
|
* attr = Attribute bits set on the memory block.
|
|
|
|
*/
|
|
|
|
alias BlkInfo = BlkInfo_;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a bit field representing all block attributes set for the memory
|
|
|
|
* referenced by p. If p references memory not originally allocated by
|
|
|
|
* this garbage collector, points to the interior of a memory block, or if
|
|
|
|
* p is null, zero will be returned.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* p = A pointer to the root of a valid memory block or to null.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* A bit field containing any bits set for the memory block referenced by
|
|
|
|
* p or zero on error.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
static uint getAttr( const scope void* p ) nothrow
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
2023-06-18 01:43:18 +01:00
|
|
|
return gc_getAttr(cast(void*) p);
|
2022-07-15 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// ditto
|
|
|
|
static uint getAttr(void* p) pure nothrow
|
|
|
|
{
|
|
|
|
return gc_getAttr( p );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the specified bits for the memory references by p. If p references
|
|
|
|
* memory not originally allocated by this garbage collector, points to the
|
|
|
|
* interior of a memory block, or if p is null, no action will be
|
|
|
|
* performed.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* p = A pointer to the root of a valid memory block or to null.
|
|
|
|
* a = A bit field containing any bits to set for this memory block.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* The result of a call to getAttr after the specified bits have been
|
|
|
|
* set.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
static uint setAttr( const scope void* p, uint a ) nothrow
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
2023-06-18 01:43:18 +01:00
|
|
|
return gc_setAttr(cast(void*) p, a);
|
2022-07-15 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// ditto
|
|
|
|
static uint setAttr(void* p, uint a) pure nothrow
|
|
|
|
{
|
|
|
|
return gc_setAttr( p, a );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clears the specified bits for the memory references by p. If p
|
|
|
|
* references memory not originally allocated by this garbage collector,
|
|
|
|
* points to the interior of a memory block, or if p is null, no action
|
|
|
|
* will be performed.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* p = A pointer to the root of a valid memory block or to null.
|
|
|
|
* a = A bit field containing any bits to clear for this memory block.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* The result of a call to getAttr after the specified bits have been
|
|
|
|
* cleared.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
static uint clrAttr( const scope void* p, uint a ) nothrow
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
2023-06-18 01:43:18 +01:00
|
|
|
return gc_clrAttr(cast(void*) p, a);
|
2022-07-15 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// ditto
|
|
|
|
static uint clrAttr(void* p, uint a) pure nothrow
|
|
|
|
{
|
|
|
|
return gc_clrAttr( p, a );
|
|
|
|
}
|
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
extern(C):
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Requests an aligned block of managed memory from the garbage collector.
|
|
|
|
* This memory may be deleted at will with a call to free, or it may be
|
|
|
|
* discarded and cleaned up automatically during a collection run. If
|
|
|
|
* allocation fails, this function will call onOutOfMemory which is
|
|
|
|
* expected to throw an OutOfMemoryError.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* sz = The desired allocation size in bytes.
|
|
|
|
* ba = A bitmask of the attributes to set on this block.
|
|
|
|
* ti = TypeInfo to describe the memory. The GC might use this information
|
|
|
|
* to improve scanning for pointers or to call finalizers.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* A reference to the allocated memory or null if insufficient memory
|
|
|
|
* is available.
|
|
|
|
*
|
|
|
|
* Throws:
|
|
|
|
* OutOfMemoryError on allocation failure.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
version (D_ProfileGC)
|
|
|
|
pragma(mangle, "gc_mallocTrace") static void* malloc(size_t sz, uint ba = 0, const scope TypeInfo ti = null,
|
|
|
|
string file = __FILE__, int line = __LINE__, string func = __FUNCTION__) pure nothrow;
|
|
|
|
else
|
|
|
|
pragma(mangle, "gc_malloc") static void* malloc(size_t sz, uint ba = 0, const scope TypeInfo ti = null) pure nothrow;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Requests an aligned block of managed memory from the garbage collector.
|
|
|
|
* This memory may be deleted at will with a call to free, or it may be
|
|
|
|
* discarded and cleaned up automatically during a collection run. If
|
|
|
|
* allocation fails, this function will call onOutOfMemory which is
|
|
|
|
* expected to throw an OutOfMemoryError.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* sz = The desired allocation size in bytes.
|
|
|
|
* ba = A bitmask of the attributes to set on this block.
|
|
|
|
* ti = TypeInfo to describe the memory. The GC might use this information
|
|
|
|
* to improve scanning for pointers or to call finalizers.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* Information regarding the allocated memory block or BlkInfo.init on
|
|
|
|
* error.
|
|
|
|
*
|
|
|
|
* Throws:
|
|
|
|
* OutOfMemoryError on allocation failure.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
version (D_ProfileGC)
|
|
|
|
pragma(mangle, "gc_qallocTrace") static BlkInfo qalloc(size_t sz, uint ba = 0, const scope TypeInfo ti = null,
|
|
|
|
string file = __FILE__, int line = __LINE__, string func = __FUNCTION__) pure nothrow;
|
|
|
|
else
|
|
|
|
pragma(mangle, "gc_qalloc") static BlkInfo qalloc(size_t sz, uint ba = 0, const scope TypeInfo ti = null) pure nothrow;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Requests an aligned block of managed memory from the garbage collector,
|
|
|
|
* which is initialized with all bits set to zero. This memory may be
|
|
|
|
* deleted at will with a call to free, or it may be discarded and cleaned
|
|
|
|
* up automatically during a collection run. If allocation fails, this
|
|
|
|
* function will call onOutOfMemory which is expected to throw an
|
|
|
|
* OutOfMemoryError.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* sz = The desired allocation size in bytes.
|
|
|
|
* ba = A bitmask of the attributes to set on this block.
|
|
|
|
* ti = TypeInfo to describe the memory. The GC might use this information
|
|
|
|
* to improve scanning for pointers or to call finalizers.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* A reference to the allocated memory or null if insufficient memory
|
|
|
|
* is available.
|
|
|
|
*
|
|
|
|
* Throws:
|
|
|
|
* OutOfMemoryError on allocation failure.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
version (D_ProfileGC)
|
|
|
|
pragma(mangle, "gc_callocTrace") static void* calloc(size_t sz, uint ba = 0, const TypeInfo ti = null,
|
|
|
|
string file = __FILE__, int line = __LINE__, string func = __FUNCTION__) pure nothrow;
|
|
|
|
else
|
|
|
|
pragma(mangle, "gc_calloc") static void* calloc(size_t sz, uint ba = 0, const TypeInfo ti = null) pure nothrow;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-06-18 01:43:18 +01:00
|
|
|
* Extend, shrink or allocate a new block of memory keeping the contents of
|
|
|
|
* an existing block
|
|
|
|
*
|
|
|
|
* If `sz` is zero, the memory referenced by p will be deallocated as if
|
|
|
|
* by a call to `free`.
|
|
|
|
* If `p` is `null`, new memory will be allocated via `malloc`.
|
|
|
|
* If `p` is pointing to memory not allocated from the GC or to the interior
|
|
|
|
* of an allocated memory block, no operation is performed and null is returned.
|
|
|
|
*
|
|
|
|
* Otherwise, a new memory block of size `sz` will be allocated as if by a
|
|
|
|
* call to `malloc`, or the implementation may instead resize or shrink the memory
|
|
|
|
* block in place.
|
|
|
|
* The contents of the new memory block will be the same as the contents
|
|
|
|
* of the old memory block, up to the lesser of the new and old sizes.
|
|
|
|
*
|
|
|
|
* The caller guarantees that there are no other live pointers to the
|
|
|
|
* passed memory block, still it might not be freed immediately by `realloc`.
|
|
|
|
* The garbage collector can reclaim the memory block in a later
|
|
|
|
* collection if it is unused.
|
|
|
|
* If allocation fails, this function will throw an `OutOfMemoryError`.
|
|
|
|
*
|
|
|
|
* If `ba` is zero (the default) the attributes of the existing memory
|
|
|
|
* will be used for an allocation.
|
|
|
|
* If `ba` is not zero and no new memory is allocated, the bits in ba will
|
|
|
|
* replace those of the current memory block.
|
2022-07-15 14:33:44 +02:00
|
|
|
*
|
|
|
|
* Params:
|
2023-06-18 01:43:18 +01:00
|
|
|
* p = A pointer to the base of a valid memory block or to `null`.
|
2022-07-15 14:33:44 +02:00
|
|
|
* sz = The desired allocation size in bytes.
|
2023-06-18 01:43:18 +01:00
|
|
|
* ba = A bitmask of the BlkAttr attributes to set on this block.
|
2022-07-15 14:33:44 +02:00
|
|
|
* ti = TypeInfo to describe the memory. The GC might use this information
|
|
|
|
* to improve scanning for pointers or to call finalizers.
|
|
|
|
*
|
|
|
|
* Returns:
|
2023-06-18 01:43:18 +01:00
|
|
|
* A reference to the allocated memory on success or `null` if `sz` is
|
|
|
|
* zero or the pointer does not point to the base of an GC allocated
|
|
|
|
* memory block.
|
2022-07-15 14:33:44 +02:00
|
|
|
*
|
|
|
|
* Throws:
|
2023-06-18 01:43:18 +01:00
|
|
|
* `OutOfMemoryError` on allocation failure.
|
2022-07-15 14:33:44 +02:00
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
version (D_ProfileGC)
|
|
|
|
pragma(mangle, "gc_reallocTrace") static void* realloc(return scope void* p, size_t sz, uint ba = 0, const TypeInfo ti = null,
|
|
|
|
string file = __FILE__, int line = __LINE__, string func = __FUNCTION__) pure nothrow;
|
|
|
|
else
|
|
|
|
pragma(mangle, "gc_realloc") static void* realloc(return scope void* p, size_t sz, uint ba = 0, const TypeInfo ti = null) pure nothrow;
|
|
|
|
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=13111
|
|
|
|
///
|
2022-07-15 14:33:44 +02:00
|
|
|
unittest
|
|
|
|
{
|
|
|
|
enum size1 = 1 << 11 + 1; // page in large object pool
|
|
|
|
enum size2 = 1 << 22 + 1; // larger than large object pool size
|
|
|
|
|
|
|
|
auto data1 = cast(ubyte*)GC.calloc(size1);
|
|
|
|
auto data2 = cast(ubyte*)GC.realloc(data1, size2);
|
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
GC.BlkInfo info = GC.query(data2);
|
2022-07-15 14:33:44 +02:00
|
|
|
assert(info.size >= size2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Requests that the managed memory block referenced by p be extended in
|
|
|
|
* place by at least mx bytes, with a desired extension of sz bytes. If an
|
|
|
|
* extension of the required size is not possible or if p references memory
|
|
|
|
* not originally allocated by this garbage collector, no action will be
|
|
|
|
* taken.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* p = A pointer to the root of a valid memory block or to null.
|
|
|
|
* mx = The minimum extension size in bytes.
|
|
|
|
* sz = The desired extension size in bytes.
|
|
|
|
* ti = TypeInfo to describe the full memory block. The GC might use
|
|
|
|
* this information to improve scanning for pointers or to
|
|
|
|
* call finalizers.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* The size in bytes of the extended memory block referenced by p or zero
|
|
|
|
* if no extension occurred.
|
|
|
|
*
|
|
|
|
* Note:
|
|
|
|
* Extend may also be used to extend slices (or memory blocks with
|
|
|
|
* $(LREF APPENDABLE) info). However, use the return value only
|
|
|
|
* as an indicator of success. $(LREF capacity) should be used to
|
|
|
|
* retrieve actual usable slice capacity.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
version (D_ProfileGC)
|
|
|
|
pragma(mangle, "gc_extendTrace") static size_t extend(void* p, size_t mx, size_t sz, const TypeInfo ti = null,
|
|
|
|
string file = __FILE__, int line = __LINE__, string func = __FUNCTION__) pure nothrow;
|
|
|
|
else
|
|
|
|
pragma(mangle, "gc_extend") static size_t extend(void* p, size_t mx, size_t sz, const TypeInfo ti = null) pure nothrow;
|
|
|
|
|
2022-07-15 14:33:44 +02:00
|
|
|
/// Standard extending
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
size_t size = 1000;
|
|
|
|
int* p = cast(int*)GC.malloc(size * int.sizeof, GC.BlkAttr.NO_SCAN);
|
|
|
|
|
|
|
|
//Try to extend the allocated data by 1000 elements, preferred 2000.
|
|
|
|
size_t u = GC.extend(p, 1000 * int.sizeof, 2000 * int.sizeof);
|
|
|
|
if (u != 0)
|
|
|
|
size = u / int.sizeof;
|
|
|
|
}
|
|
|
|
/// slice extending
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
int[] slice = new int[](1000);
|
|
|
|
int* p = slice.ptr;
|
|
|
|
|
|
|
|
//Check we have access to capacity before attempting the extend
|
|
|
|
if (slice.capacity)
|
|
|
|
{
|
|
|
|
//Try to extend slice by 1000 elements, preferred 2000.
|
|
|
|
size_t u = GC.extend(p, 1000 * int.sizeof, 2000 * int.sizeof);
|
|
|
|
if (u != 0)
|
|
|
|
{
|
|
|
|
slice.length = slice.capacity;
|
|
|
|
assert(slice.length >= 2000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Requests that at least sz bytes of memory be obtained from the operating
|
|
|
|
* system and marked as free.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* sz = The desired size in bytes.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* The actual number of bytes reserved or zero on error.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
pragma(mangle, "gc_reserve") static size_t reserve(size_t sz) nothrow pure;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deallocates the memory referenced by p. If p is null, no action occurs.
|
|
|
|
* If p references memory not originally allocated by this garbage
|
|
|
|
* collector, if p points to the interior of a memory block, or if this
|
|
|
|
* method is called from a finalizer, no action will be taken. The block
|
|
|
|
* will not be finalized regardless of whether the FINALIZE attribute is
|
2023-06-18 01:43:18 +01:00
|
|
|
* set. If finalization is desired, call $(REF1 destroy, object) prior to `GC.free`.
|
2022-07-15 14:33:44 +02:00
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* p = A pointer to the root of a valid memory block or to null.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
pragma(mangle, "gc_free") static void free(void* p) pure nothrow @nogc;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
extern(D):
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the base address of the memory block containing p. This value
|
|
|
|
* is useful to determine whether p is an interior pointer, and the result
|
|
|
|
* may be passed to routines such as sizeOf which may otherwise fail. If p
|
|
|
|
* references memory not originally allocated by this garbage collector, if
|
|
|
|
* p is null, or if the garbage collector does not support this operation,
|
|
|
|
* null will be returned.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* p = A pointer to the root or the interior of a valid memory block or to
|
|
|
|
* null.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* The base address of the memory block referenced by p or null on error.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
static inout(void)* addrOf( inout(void)* p ) nothrow @nogc pure @trusted
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
|
|
|
return cast(inout(void)*)gc_addrOf(cast(void*)p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// ditto
|
2023-06-18 01:43:18 +01:00
|
|
|
static void* addrOf(void* p) pure nothrow @nogc @trusted
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
|
|
|
return gc_addrOf(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the true size of the memory block referenced by p. This value
|
|
|
|
* represents the maximum number of bytes for which a call to realloc may
|
|
|
|
* resize the existing block in place. If p references memory not
|
|
|
|
* originally allocated by this garbage collector, points to the interior
|
|
|
|
* of a memory block, or if p is null, zero will be returned.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* p = A pointer to the root of a valid memory block or to null.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* The size in bytes of the memory block referenced by p or zero on error.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
static size_t sizeOf( const scope void* p ) nothrow @nogc /* FIXME pure */
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
|
|
|
return gc_sizeOf(cast(void*)p);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// ditto
|
2023-06-18 01:43:18 +01:00
|
|
|
static size_t sizeOf(void* p) pure nothrow @nogc
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
|
|
|
return gc_sizeOf( p );
|
|
|
|
}
|
|
|
|
|
|
|
|
// verify that the reallocation doesn't leave the size cache in a wrong state
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
auto data = cast(int*)realloc(null, 4096);
|
|
|
|
size_t size = GC.sizeOf(data);
|
|
|
|
assert(size >= 4096);
|
|
|
|
data = cast(int*)GC.realloc(data, 4100);
|
|
|
|
size = GC.sizeOf(data);
|
|
|
|
assert(size >= 4100);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns aggregate information about the memory block containing p. If p
|
|
|
|
* references memory not originally allocated by this garbage collector, if
|
|
|
|
* p is null, or if the garbage collector does not support this operation,
|
|
|
|
* BlkInfo.init will be returned. Typically, support for this operation
|
|
|
|
* is dependent on support for addrOf.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* p = A pointer to the root or the interior of a valid memory block or to
|
|
|
|
* null.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* Information regarding the memory block referenced by p or BlkInfo.init
|
|
|
|
* on error.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
static BlkInfo query(return scope const void* p) nothrow
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
|
|
|
return gc_query(cast(void*)p);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// ditto
|
2023-06-18 01:43:18 +01:00
|
|
|
static BlkInfo query(return scope void* p) pure nothrow
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
|
|
|
return gc_query( p );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns runtime stats for currently active GC implementation
|
|
|
|
* See `core.memory.GC.Stats` for list of available metrics.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
static Stats stats() @safe nothrow @nogc
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
|
|
|
return gc_stats();
|
|
|
|
}
|
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
/**
|
|
|
|
* Returns runtime profile stats for currently active GC implementation
|
|
|
|
* See `core.memory.GC.ProfileStats` for list of available metrics.
|
|
|
|
*/
|
|
|
|
static ProfileStats profileStats() nothrow @nogc @safe
|
|
|
|
{
|
|
|
|
return gc_profileStats();
|
|
|
|
}
|
|
|
|
|
|
|
|
extern(C):
|
|
|
|
|
2022-07-15 14:33:44 +02:00
|
|
|
/**
|
|
|
|
* Adds an internal root pointing to the GC memory block referenced by p.
|
|
|
|
* As a result, the block referenced by p itself and any blocks accessible
|
|
|
|
* via it will be considered live until the root is removed again.
|
|
|
|
*
|
|
|
|
* If p is null, no operation is performed.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* p = A pointer into a GC-managed memory block or null.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* ---
|
|
|
|
* // Typical C-style callback mechanism; the passed function
|
|
|
|
* // is invoked with the user-supplied context pointer at a
|
|
|
|
* // later point.
|
|
|
|
* extern(C) void addCallback(void function(void*), void*);
|
|
|
|
*
|
|
|
|
* // Allocate an object on the GC heap (this would usually be
|
|
|
|
* // some application-specific context data).
|
|
|
|
* auto context = new Object;
|
|
|
|
*
|
|
|
|
* // Make sure that it is not collected even if it is no
|
|
|
|
* // longer referenced from D code (stack, GC heap, …).
|
|
|
|
* GC.addRoot(cast(void*)context);
|
|
|
|
*
|
|
|
|
* // Also ensure that a moving collector does not relocate
|
|
|
|
* // the object.
|
|
|
|
* GC.setAttr(cast(void*)context, GC.BlkAttr.NO_MOVE);
|
|
|
|
*
|
|
|
|
* // Now context can be safely passed to the C library.
|
|
|
|
* addCallback(&myHandler, cast(void*)context);
|
|
|
|
*
|
|
|
|
* extern(C) void myHandler(void* ctx)
|
|
|
|
* {
|
|
|
|
* // Assuming that the callback is invoked only once, the
|
|
|
|
* // added root can be removed again now to allow the GC
|
|
|
|
* // to collect it later.
|
|
|
|
* GC.removeRoot(ctx);
|
|
|
|
* GC.clrAttr(ctx, GC.BlkAttr.NO_MOVE);
|
|
|
|
*
|
|
|
|
* auto context = cast(Object)ctx;
|
|
|
|
* // Use context here…
|
|
|
|
* }
|
|
|
|
* ---
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
pragma(mangle, "gc_addRoot") static void addRoot(const void* p) nothrow @nogc pure;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the memory block referenced by p from an internal list of roots
|
|
|
|
* to be scanned during a collection. If p is null or is not a value
|
|
|
|
* previously passed to addRoot() then no operation is performed.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* p = A pointer into a GC-managed memory block or null.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
pragma(mangle, "gc_removeRoot") static void removeRoot(const void* p) nothrow @nogc pure;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds $(D p[0 .. sz]) to the list of memory ranges to be scanned for
|
|
|
|
* pointers during a collection. If p is null, no operation is performed.
|
|
|
|
*
|
|
|
|
* Note that $(D p[0 .. sz]) is treated as an opaque range of memory assumed
|
|
|
|
* to be suitably managed by the caller. In particular, if p points into a
|
|
|
|
* GC-managed memory block, addRange does $(I not) mark this block as live.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* p = A pointer to a valid memory address or to null.
|
|
|
|
* sz = The size in bytes of the block to add. If sz is zero then the
|
|
|
|
* no operation will occur. If p is null then sz must be zero.
|
|
|
|
* ti = TypeInfo to describe the memory. The GC might use this information
|
|
|
|
* to improve scanning for pointers or to call finalizers
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
* ---
|
|
|
|
* // Allocate a piece of memory on the C heap.
|
|
|
|
* enum size = 1_000;
|
|
|
|
* auto rawMemory = core.stdc.stdlib.malloc(size);
|
|
|
|
*
|
|
|
|
* // Add it as a GC range.
|
|
|
|
* GC.addRange(rawMemory, size);
|
|
|
|
*
|
|
|
|
* // Now, pointers to GC-managed memory stored in
|
|
|
|
* // rawMemory will be recognized on collection.
|
|
|
|
* ---
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
pragma(mangle, "gc_addRange")
|
|
|
|
static void addRange(const void* p, size_t sz, const TypeInfo ti = null) @nogc nothrow pure;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the memory range starting at p from an internal list of ranges
|
|
|
|
* to be scanned during a collection. If p is null or does not represent
|
|
|
|
* a value previously passed to addRange() then no operation is
|
|
|
|
* performed.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* p = A pointer to a valid memory address or to null.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
pragma(mangle, "gc_removeRange") static void removeRange(const void* p) nothrow @nogc pure;
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Runs any finalizer that is located in address range of the
|
|
|
|
* given code segment. This is used before unloading shared
|
|
|
|
* libraries. All matching objects which have a finalizer in this
|
|
|
|
* code segment are assumed to be dead, using them while or after
|
|
|
|
* calling this method has undefined behavior.
|
|
|
|
*
|
|
|
|
* Params:
|
|
|
|
* segment = address range of a code segment.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
pragma(mangle, "gc_runFinalizers") static void runFinalizers(const scope void[] segment);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Queries the GC whether the current thread is running object finalization
|
|
|
|
* as part of a GC collection, or an explicit call to runFinalizers.
|
|
|
|
*
|
|
|
|
* As some GC implementations (such as the current conservative one) don't
|
|
|
|
* support GC memory allocation during object finalization, this function
|
|
|
|
* can be used to guard against such programming errors.
|
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* true if the current thread is in a finalizer, a destructor invoked by
|
|
|
|
* the GC.
|
|
|
|
*/
|
|
|
|
pragma(mangle, "gc_inFinalizer") static bool inFinalizer() nothrow @nogc @safe;
|
|
|
|
|
|
|
|
///
|
|
|
|
@safe nothrow @nogc unittest
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
2023-06-18 01:43:18 +01:00
|
|
|
// Only code called from a destructor is executed during finalization.
|
|
|
|
assert(!GC.inFinalizer);
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
enum Outcome
|
|
|
|
{
|
|
|
|
notCalled,
|
|
|
|
calledManually,
|
|
|
|
calledFromDruntime
|
|
|
|
}
|
|
|
|
|
|
|
|
static class Resource
|
|
|
|
{
|
|
|
|
static Outcome outcome;
|
|
|
|
|
|
|
|
this()
|
|
|
|
{
|
|
|
|
outcome = Outcome.notCalled;
|
|
|
|
}
|
|
|
|
|
|
|
|
~this()
|
|
|
|
{
|
|
|
|
if (GC.inFinalizer)
|
|
|
|
{
|
|
|
|
outcome = Outcome.calledFromDruntime;
|
|
|
|
|
|
|
|
import core.exception : InvalidMemoryOperationError;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Presently, allocating GC memory during finalization
|
|
|
|
* is forbidden and leads to
|
|
|
|
* `InvalidMemoryOperationError` being thrown.
|
|
|
|
*
|
|
|
|
* `GC.inFinalizer` can be used to guard against
|
|
|
|
* programming erros such as these and is also a more
|
|
|
|
* efficient way to verify whether a destructor was
|
|
|
|
* invoked by the GC.
|
|
|
|
*/
|
|
|
|
cast(void) GC.malloc(1);
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
catch (InvalidMemoryOperationError e)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
outcome = Outcome.calledManually;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void createGarbage()
|
|
|
|
{
|
|
|
|
auto r = new Resource;
|
|
|
|
r = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(Resource.outcome == Outcome.notCalled);
|
|
|
|
createGarbage();
|
|
|
|
GC.collect;
|
|
|
|
assert(
|
|
|
|
Resource.outcome == Outcome.notCalled ||
|
|
|
|
Resource.outcome == Outcome.calledFromDruntime);
|
|
|
|
|
|
|
|
auto r = new Resource;
|
|
|
|
GC.runFinalizers((cast(const void*)typeid(Resource).destructor)[0..1]);
|
|
|
|
assert(Resource.outcome == Outcome.calledFromDruntime);
|
|
|
|
Resource.outcome = Outcome.notCalled;
|
|
|
|
|
|
|
|
debug(MEMSTOMP) {} else
|
|
|
|
{
|
|
|
|
// assume Resource data is still available
|
|
|
|
r.destroy;
|
|
|
|
assert(Resource.outcome == Outcome.notCalled);
|
|
|
|
}
|
|
|
|
|
|
|
|
r = new Resource;
|
|
|
|
assert(Resource.outcome == Outcome.notCalled);
|
|
|
|
r.destroy;
|
|
|
|
assert(Resource.outcome == Outcome.calledManually);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the number of bytes allocated for the current thread
|
|
|
|
* since program start. It is the same as
|
|
|
|
* GC.stats().allocatedInCurrentThread, but faster.
|
|
|
|
*/
|
|
|
|
pragma(mangle, "gc_allocatedInCurrentThread") static ulong allocatedInCurrentThread() nothrow;
|
|
|
|
|
|
|
|
/// Using allocatedInCurrentThread
|
|
|
|
nothrow unittest
|
|
|
|
{
|
|
|
|
ulong currentlyAllocated = GC.allocatedInCurrentThread();
|
|
|
|
struct DataStruct
|
|
|
|
{
|
|
|
|
long l1;
|
|
|
|
long l2;
|
|
|
|
long l3;
|
|
|
|
long l4;
|
|
|
|
}
|
|
|
|
DataStruct* unused = new DataStruct;
|
|
|
|
assert(GC.allocatedInCurrentThread() == currentlyAllocated + 32);
|
|
|
|
assert(GC.stats().allocatedInCurrentThread == currentlyAllocated + 32);
|
2022-07-15 14:33:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pure variants of C's memory allocation functions `malloc`, `calloc`, and
|
|
|
|
* `realloc` and deallocation function `free`.
|
|
|
|
*
|
2023-06-18 01:43:18 +01:00
|
|
|
* UNIX 98 requires that errno be set to ENOMEM upon failure.
|
2022-07-15 14:33:44 +02:00
|
|
|
* Purity is achieved by saving and restoring the value of `errno`, thus
|
|
|
|
* behaving as if it were never changed.
|
|
|
|
*
|
|
|
|
* See_Also:
|
|
|
|
* $(LINK2 https://dlang.org/spec/function.html#pure-functions, D's rules for purity),
|
|
|
|
* which allow for memory allocation under specific circumstances.
|
|
|
|
*/
|
2023-06-18 01:43:18 +01:00
|
|
|
void* pureMalloc()(size_t size) @trusted pure @nogc nothrow
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
2023-06-18 01:43:18 +01:00
|
|
|
const errnosave = fakePureErrno;
|
2022-07-15 14:33:44 +02:00
|
|
|
void* ret = fakePureMalloc(size);
|
2023-06-18 01:43:18 +01:00
|
|
|
fakePureErrno = errnosave;
|
2022-07-15 14:33:44 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
/// ditto
|
2023-06-18 01:43:18 +01:00
|
|
|
void* pureCalloc()(size_t nmemb, size_t size) @trusted pure @nogc nothrow
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
2023-06-18 01:43:18 +01:00
|
|
|
const errnosave = fakePureErrno;
|
2022-07-15 14:33:44 +02:00
|
|
|
void* ret = fakePureCalloc(nmemb, size);
|
2023-06-18 01:43:18 +01:00
|
|
|
fakePureErrno = errnosave;
|
2022-07-15 14:33:44 +02:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
/// ditto
|
2023-06-18 01:43:18 +01:00
|
|
|
void* pureRealloc()(void* ptr, size_t size) @system pure @nogc nothrow
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
2023-06-18 01:43:18 +01:00
|
|
|
const errnosave = fakePureErrno;
|
2022-07-15 14:33:44 +02:00
|
|
|
void* ret = fakePureRealloc(ptr, size);
|
2023-06-18 01:43:18 +01:00
|
|
|
fakePureErrno = errnosave;
|
2022-07-15 14:33:44 +02:00
|
|
|
return ret;
|
|
|
|
}
|
2023-06-18 01:43:18 +01:00
|
|
|
|
2022-07-15 14:33:44 +02:00
|
|
|
/// ditto
|
2023-06-18 01:43:18 +01:00
|
|
|
void pureFree()(void* ptr) @system pure @nogc nothrow
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
2023-06-18 01:43:18 +01:00
|
|
|
version (Posix)
|
|
|
|
{
|
|
|
|
// POSIX free doesn't set errno
|
|
|
|
fakePureFree(ptr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const errnosave = fakePureErrno;
|
|
|
|
fakePureFree(ptr);
|
|
|
|
fakePureErrno = errnosave;
|
|
|
|
}
|
2022-07-15 14:33:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
@system pure nothrow @nogc unittest
|
|
|
|
{
|
|
|
|
ubyte[] fun(size_t n) pure
|
|
|
|
{
|
|
|
|
void* p = pureMalloc(n);
|
|
|
|
p !is null || n == 0 || assert(0);
|
|
|
|
scope(failure) p = pureRealloc(p, 0);
|
|
|
|
p = pureRealloc(p, n *= 2);
|
|
|
|
p !is null || n == 0 || assert(0);
|
|
|
|
return cast(ubyte[]) p[0 .. n];
|
|
|
|
}
|
|
|
|
|
|
|
|
auto buf = fun(100);
|
|
|
|
assert(buf.length == 200);
|
|
|
|
pureFree(buf.ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
@system pure nothrow @nogc unittest
|
|
|
|
{
|
2023-06-18 01:43:18 +01:00
|
|
|
const int errno = fakePureErrno();
|
2022-07-15 14:33:44 +02:00
|
|
|
|
|
|
|
void* x = pureMalloc(10); // normal allocation
|
2023-06-18 01:43:18 +01:00
|
|
|
assert(errno == fakePureErrno()); // errno shouldn't change
|
2022-07-15 14:33:44 +02:00
|
|
|
assert(x !is null); // allocation should succeed
|
|
|
|
|
|
|
|
x = pureRealloc(x, 10); // normal reallocation
|
2023-06-18 01:43:18 +01:00
|
|
|
assert(errno == fakePureErrno()); // errno shouldn't change
|
2022-07-15 14:33:44 +02:00
|
|
|
assert(x !is null); // allocation should succeed
|
|
|
|
|
|
|
|
fakePureFree(x);
|
|
|
|
|
|
|
|
void* y = pureCalloc(10, 1); // normal zeroed allocation
|
2023-06-18 01:43:18 +01:00
|
|
|
assert(errno == fakePureErrno()); // errno shouldn't change
|
2022-07-15 14:33:44 +02:00
|
|
|
assert(y !is null); // allocation should succeed
|
|
|
|
|
|
|
|
fakePureFree(y);
|
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
// Workaround bug in glibc 2.26
|
|
|
|
// See also: https://issues.dlang.org/show_bug.cgi?id=17956
|
|
|
|
void* z = pureMalloc(size_t.max & ~255); // won't affect `errno`
|
|
|
|
assert(errno == fakePureErrno()); // errno shouldn't change
|
2022-07-15 14:33:44 +02:00
|
|
|
assert(z is null);
|
|
|
|
}
|
|
|
|
|
|
|
|
// locally purified for internal use here only
|
2023-06-18 01:43:18 +01:00
|
|
|
|
|
|
|
static import core.stdc.errno;
|
|
|
|
static if (__traits(getOverloads, core.stdc.errno, "errno").length == 1
|
|
|
|
&& __traits(getLinkage, core.stdc.errno.errno) == "C")
|
2022-07-15 14:33:44 +02:00
|
|
|
{
|
2023-06-18 01:43:18 +01:00
|
|
|
extern(C) pragma(mangle, __traits(identifier, core.stdc.errno.errno))
|
|
|
|
private ref int fakePureErrno() @nogc nothrow pure @system;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
extern(C) private @nogc nothrow pure @system
|
|
|
|
{
|
|
|
|
pragma(mangle, __traits(identifier, core.stdc.errno.getErrno))
|
|
|
|
@property int fakePureErrno();
|
2022-07-15 14:33:44 +02:00
|
|
|
|
2023-06-18 01:43:18 +01:00
|
|
|
pragma(mangle, __traits(identifier, core.stdc.errno.setErrno))
|
|
|
|
@property int fakePureErrno(int);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
version (D_BetterC) {}
|
|
|
|
else // TODO: remove this function after Phobos no longer needs it.
|
|
|
|
extern (C) private @system @nogc nothrow
|
|
|
|
{
|
|
|
|
ref int fakePureErrnoImpl()
|
|
|
|
{
|
|
|
|
import core.stdc.errno;
|
|
|
|
return errno();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern (C) private pure @system @nogc nothrow
|
|
|
|
{
|
2022-07-15 14:33:44 +02:00
|
|
|
pragma(mangle, "malloc") void* fakePureMalloc(size_t);
|
|
|
|
pragma(mangle, "calloc") void* fakePureCalloc(size_t nmemb, size_t size);
|
|
|
|
pragma(mangle, "realloc") void* fakePureRealloc(void* ptr, size_t size);
|
|
|
|
|
|
|
|
pragma(mangle, "free") void fakePureFree(void* ptr);
|
|
|
|
}
|
2023-06-18 01:43:18 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
Destroys and then deallocates an object.
|
|
|
|
|
|
|
|
In detail, `__delete(x)` returns with no effect if `x` is `null`. Otherwise, it
|
|
|
|
performs the following actions in sequence:
|
|
|
|
$(UL
|
|
|
|
$(LI
|
|
|
|
Calls the destructor `~this()` for the object referred to by `x`
|
|
|
|
(if `x` is a class or interface reference) or
|
|
|
|
for the object pointed to by `x` (if `x` is a pointer to a `struct`).
|
|
|
|
Arrays of structs call the destructor, if defined, for each element in the array.
|
|
|
|
If no destructor is defined, this step has no effect.
|
|
|
|
)
|
|
|
|
$(LI
|
|
|
|
Frees the memory allocated for `x`. If `x` is a reference to a class
|
|
|
|
or interface, the memory allocated for the underlying instance is freed. If `x` is
|
|
|
|
a pointer, the memory allocated for the pointed-to object is freed. If `x` is a
|
|
|
|
built-in array, the memory allocated for the array is freed.
|
|
|
|
If `x` does not refer to memory previously allocated with `new` (or the lower-level
|
|
|
|
equivalents in the GC API), the behavior is undefined.
|
|
|
|
)
|
|
|
|
$(LI
|
|
|
|
Lastly, `x` is set to `null`. Any attempt to read or write the freed memory via
|
|
|
|
other references will result in undefined behavior.
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
Note: Users should prefer $(REF1 destroy, object) to explicitly finalize objects,
|
|
|
|
and only resort to $(REF __delete, core,memory) when $(REF destroy, object)
|
|
|
|
wouldn't be a feasible option.
|
|
|
|
|
|
|
|
Params:
|
|
|
|
x = aggregate object that should be destroyed
|
|
|
|
|
|
|
|
See_Also: $(REF1 destroy, object), $(REF free, core,GC)
|
|
|
|
|
|
|
|
History:
|
|
|
|
|
|
|
|
The `delete` keyword allowed to free GC-allocated memory.
|
|
|
|
As this is inherently not `@safe`, it has been deprecated.
|
|
|
|
This function has been added to provide an easy transition from `delete`.
|
|
|
|
It performs the same functionality as the former `delete` keyword.
|
|
|
|
*/
|
|
|
|
void __delete(T)(ref T x) @system
|
|
|
|
{
|
|
|
|
static void _destructRecurse(S)(ref S s)
|
|
|
|
if (is(S == struct))
|
|
|
|
{
|
|
|
|
static if (__traits(hasMember, S, "__xdtor") &&
|
|
|
|
// Bugzilla 14746: Check that it's the exact member of S.
|
|
|
|
__traits(isSame, S, __traits(parent, s.__xdtor)))
|
|
|
|
s.__xdtor();
|
|
|
|
}
|
|
|
|
|
|
|
|
// See also: https://github.com/dlang/dmd/blob/v2.078.0/src/dmd/e2ir.d#L3886
|
|
|
|
static if (is(T == interface))
|
|
|
|
{
|
|
|
|
.object.destroy(x);
|
|
|
|
}
|
|
|
|
else static if (is(T == class))
|
|
|
|
{
|
|
|
|
.object.destroy(x);
|
|
|
|
}
|
|
|
|
else static if (is(T == U*, U))
|
|
|
|
{
|
|
|
|
static if (is(U == struct))
|
|
|
|
{
|
|
|
|
if (x)
|
|
|
|
_destructRecurse(*x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else static if (is(T : E[], E))
|
|
|
|
{
|
|
|
|
static if (is(E == struct))
|
|
|
|
{
|
|
|
|
foreach_reverse (ref e; x)
|
|
|
|
_destructRecurse(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
static assert(0, "It is not possible to delete: `" ~ T.stringof ~ "`");
|
|
|
|
}
|
|
|
|
|
|
|
|
static if (is(T == interface) ||
|
|
|
|
is(T == class) ||
|
|
|
|
is(T == U2*, U2))
|
|
|
|
{
|
|
|
|
GC.free(GC.addrOf(cast(void*) x));
|
|
|
|
x = null;
|
|
|
|
}
|
|
|
|
else static if (is(T : E2[], E2))
|
|
|
|
{
|
|
|
|
GC.free(GC.addrOf(cast(void*) x.ptr));
|
|
|
|
x = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Deleting classes
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
bool dtorCalled;
|
|
|
|
class B
|
|
|
|
{
|
|
|
|
int test;
|
|
|
|
~this()
|
|
|
|
{
|
|
|
|
dtorCalled = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
B b = new B();
|
|
|
|
B a = b;
|
|
|
|
b.test = 10;
|
|
|
|
|
|
|
|
assert(GC.addrOf(cast(void*) b) != null);
|
|
|
|
__delete(b);
|
|
|
|
assert(b is null);
|
|
|
|
assert(dtorCalled);
|
|
|
|
assert(GC.addrOf(cast(void*) b) == null);
|
|
|
|
// but be careful, a still points to it
|
|
|
|
assert(a !is null);
|
|
|
|
assert(GC.addrOf(cast(void*) a) == null); // but not a valid GC pointer
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Deleting interfaces
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
bool dtorCalled;
|
|
|
|
interface A
|
|
|
|
{
|
|
|
|
int quack();
|
|
|
|
}
|
|
|
|
class B : A
|
|
|
|
{
|
|
|
|
int a;
|
|
|
|
int quack()
|
|
|
|
{
|
|
|
|
a++;
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
~this()
|
|
|
|
{
|
|
|
|
dtorCalled = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
A a = new B();
|
|
|
|
a.quack();
|
|
|
|
|
|
|
|
assert(GC.addrOf(cast(void*) a) != null);
|
|
|
|
__delete(a);
|
|
|
|
assert(a is null);
|
|
|
|
assert(dtorCalled);
|
|
|
|
assert(GC.addrOf(cast(void*) a) == null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Deleting structs
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
bool dtorCalled;
|
|
|
|
struct A
|
|
|
|
{
|
|
|
|
string test;
|
|
|
|
~this()
|
|
|
|
{
|
|
|
|
dtorCalled = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
auto a = new A("foo");
|
|
|
|
|
|
|
|
assert(GC.addrOf(cast(void*) a) != null);
|
|
|
|
__delete(a);
|
|
|
|
assert(a is null);
|
|
|
|
assert(dtorCalled);
|
|
|
|
assert(GC.addrOf(cast(void*) a) == null);
|
|
|
|
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=22779
|
|
|
|
A *aptr;
|
|
|
|
__delete(aptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Deleting arrays
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
int[] a = [1, 2, 3];
|
|
|
|
auto b = a;
|
|
|
|
|
|
|
|
assert(GC.addrOf(b.ptr) != null);
|
|
|
|
__delete(b);
|
|
|
|
assert(b is null);
|
|
|
|
assert(GC.addrOf(b.ptr) == null);
|
|
|
|
// but be careful, a still points to it
|
|
|
|
assert(a !is null);
|
|
|
|
assert(GC.addrOf(a.ptr) == null); // but not a valid GC pointer
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Deleting arrays of structs
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
int dtorCalled;
|
|
|
|
struct A
|
|
|
|
{
|
|
|
|
int a;
|
|
|
|
~this()
|
|
|
|
{
|
|
|
|
assert(dtorCalled == a);
|
|
|
|
dtorCalled++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
auto arr = [A(1), A(2), A(3)];
|
|
|
|
arr[0].a = 2;
|
|
|
|
arr[1].a = 1;
|
|
|
|
arr[2].a = 0;
|
|
|
|
|
|
|
|
assert(GC.addrOf(arr.ptr) != null);
|
|
|
|
__delete(arr);
|
|
|
|
assert(dtorCalled == 3);
|
|
|
|
assert(GC.addrOf(arr.ptr) == null);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deleting raw memory
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import core.memory : GC;
|
|
|
|
auto a = GC.malloc(5);
|
|
|
|
assert(GC.addrOf(cast(void*) a) != null);
|
|
|
|
__delete(a);
|
|
|
|
assert(a is null);
|
|
|
|
assert(GC.addrOf(cast(void*) a) == null);
|
|
|
|
}
|
|
|
|
|
|
|
|
// __delete returns with no effect if x is null
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
Object x = null;
|
|
|
|
__delete(x);
|
|
|
|
|
|
|
|
struct S { ~this() { } }
|
|
|
|
class C { }
|
|
|
|
interface I { }
|
|
|
|
|
|
|
|
int[] a; __delete(a);
|
|
|
|
S[] as; __delete(as);
|
|
|
|
C c; __delete(c);
|
|
|
|
I i; __delete(i);
|
|
|
|
C* pc = &c; __delete(*pc);
|
|
|
|
I* pi = &i; __delete(*pi);
|
|
|
|
int* pint; __delete(pint);
|
|
|
|
S* ps; __delete(ps);
|
|
|
|
}
|
|
|
|
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=19092
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
const(int)[] x = [1, 2, 3];
|
|
|
|
assert(GC.addrOf(x.ptr) != null);
|
|
|
|
__delete(x);
|
|
|
|
assert(x is null);
|
|
|
|
assert(GC.addrOf(x.ptr) == null);
|
|
|
|
|
|
|
|
immutable(int)[] y = [1, 2, 3];
|
|
|
|
assert(GC.addrOf(y.ptr) != null);
|
|
|
|
__delete(y);
|
|
|
|
assert(y is null);
|
|
|
|
assert(GC.addrOf(y.ptr) == null);
|
|
|
|
}
|
|
|
|
|
|
|
|
// test realloc behaviour
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
static void set(int* p, size_t size)
|
|
|
|
{
|
|
|
|
foreach (i; 0 .. size)
|
|
|
|
*p++ = cast(int) i;
|
|
|
|
}
|
|
|
|
static void verify(int* p, size_t size)
|
|
|
|
{
|
|
|
|
foreach (i; 0 .. size)
|
|
|
|
assert(*p++ == i);
|
|
|
|
}
|
|
|
|
static void test(size_t memsize)
|
|
|
|
{
|
|
|
|
int* p = cast(int*) GC.malloc(memsize * int.sizeof);
|
|
|
|
assert(p);
|
|
|
|
set(p, memsize);
|
|
|
|
verify(p, memsize);
|
|
|
|
|
|
|
|
int* q = cast(int*) GC.realloc(p + 4, 2 * memsize * int.sizeof);
|
|
|
|
assert(q == null);
|
|
|
|
|
|
|
|
q = cast(int*) GC.realloc(p + memsize / 2, 2 * memsize * int.sizeof);
|
|
|
|
assert(q == null);
|
|
|
|
|
|
|
|
q = cast(int*) GC.realloc(p + memsize - 1, 2 * memsize * int.sizeof);
|
|
|
|
assert(q == null);
|
|
|
|
|
|
|
|
int* r = cast(int*) GC.realloc(p, 5 * memsize * int.sizeof);
|
|
|
|
verify(r, memsize);
|
|
|
|
set(r, 5 * memsize);
|
|
|
|
|
|
|
|
int* s = cast(int*) GC.realloc(r, 2 * memsize * int.sizeof);
|
|
|
|
verify(s, 2 * memsize);
|
|
|
|
|
|
|
|
assert(GC.realloc(s, 0) == null); // free
|
|
|
|
assert(GC.addrOf(p) == null);
|
|
|
|
}
|
|
|
|
|
|
|
|
test(16);
|
|
|
|
test(200);
|
|
|
|
test(800); // spans large and small pools
|
|
|
|
test(1200);
|
|
|
|
test(8000);
|
|
|
|
|
|
|
|
void* p = GC.malloc(100);
|
|
|
|
assert(GC.realloc(&p, 50) == null); // non-GC pointer
|
|
|
|
}
|
|
|
|
|
|
|
|
// test GC.profileStats
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
auto stats = GC.profileStats();
|
|
|
|
GC.collect();
|
|
|
|
auto nstats = GC.profileStats();
|
|
|
|
assert(nstats.numCollections > stats.numCollections);
|
|
|
|
}
|
|
|
|
|
|
|
|
// in rt.lifetime:
|
|
|
|
private extern (C) void* _d_newitemU(scope const TypeInfo _ti) @system pure nothrow;
|
|
|
|
|
|
|
|
/**
|
|
|
|
Moves a value to a new GC allocation.
|
|
|
|
|
|
|
|
Params:
|
|
|
|
value = Value to be moved. If the argument is an lvalue and a struct with a
|
|
|
|
destructor or postblit, it will be reset to its `.init` value.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
A pointer to the new GC-allocated value.
|
|
|
|
*/
|
|
|
|
T* moveToGC(T)(auto ref T value)
|
|
|
|
{
|
|
|
|
static T* doIt(ref T value) @trusted
|
|
|
|
{
|
|
|
|
import core.lifetime : moveEmplace;
|
|
|
|
auto mem = cast(T*) _d_newitemU(typeid(T)); // allocate but don't initialize
|
|
|
|
moveEmplace(value, *mem);
|
|
|
|
return mem;
|
|
|
|
}
|
|
|
|
|
|
|
|
return doIt(value); // T dtor might be @system
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
@safe pure nothrow unittest
|
|
|
|
{
|
|
|
|
struct S
|
|
|
|
{
|
|
|
|
int x;
|
|
|
|
this(this) @disable;
|
|
|
|
~this() @safe pure nothrow @nogc {}
|
|
|
|
}
|
|
|
|
|
|
|
|
S* p;
|
|
|
|
|
|
|
|
// rvalue
|
|
|
|
p = moveToGC(S(123));
|
|
|
|
assert(p.x == 123);
|
|
|
|
|
|
|
|
// lvalue
|
|
|
|
auto lval = S(456);
|
|
|
|
p = moveToGC(lval);
|
|
|
|
assert(p.x == 456);
|
|
|
|
assert(lval.x == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// @system dtor
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
struct S
|
|
|
|
{
|
|
|
|
int x;
|
|
|
|
~this() @system {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// lvalue case is @safe, ref param isn't destructed
|
|
|
|
static assert(__traits(compiles, (ref S lval) @safe { moveToGC(lval); }));
|
|
|
|
|
|
|
|
// rvalue case is @system, value param is destructed
|
|
|
|
static assert(!__traits(compiles, () @safe { moveToGC(S(0)); }));
|
|
|
|
}
|