Wolfgang Thallergcc 15.1.0ada185d1 year agoHistory
3908 lines · 107.2 KB · D
RawDownload
// Written in the D programming language./** High-level interface for allocators. Implements bundled allocation/creationand destruction/deallocation of data including `struct`s and `class`es,and also array primitives related to allocation. This module is the entry pointfor both making use of allocators and for their documentation. $(SCRIPT inhibitQuickIndex = 1;)$(BOOKTABLE,$(TR $(TH Category) $(TH Functions))$(TR $(TD Make) $(TD    $(LREF make)    $(LREF makeArray)    $(LREF makeMultidimensionalArray)))$(TR $(TD Dispose) $(TD    $(LREF dispose)    $(LREF disposeMultidimensionalArray)))$(TR $(TD Modify) $(TD    $(LREF expandArray)    $(LREF shrinkArray)))$(TR $(TD Global) $(TD    $(LREF processAllocator)    $(LREF theAllocator)))$(TR $(TD Class interface) $(TD    $(LREF CAllocatorImpl)    $(LREF CSharedAllocatorImpl)    $(LREF IAllocator)    $(LREF ISharedAllocator)))$(TR $(TD Structs) $(TD    $(LREF allocatorObject)    $(LREF RCIAllocator)    $(LREF RCISharedAllocator)    $(LREF sharedAllocatorObject)    $(LREF ThreadLocal)))) Synopsis:$(RUNNABLE_EXAMPLE---// Allocate an int, initialize it with 42int* p = theAllocator.make!int(42);assert(*p == 42);// Destroy and deallocate ittheAllocator.dispose(p); // Allocate using the global process allocatorp = processAllocator.make!int(100);assert(*p == 100);// Destroy and deallocateprocessAllocator.dispose(p);---)$(RUNNABLE_EXAMPLE---// Create an array of 50 doubles initialized to -1.0double[] arr = theAllocator.makeArray!double(50, -1.0);// Append two zeros to ittheAllocator.expandArray(arr, 2, 0.0);// On second thought, take that backtheAllocator.shrinkArray(arr, 2);// Destroy and deallocatetheAllocator.dispose(arr);---) $(H2 Layered Structure) D's allocators have a layered structure in both implementation and documentation: $(OL$(LI A high-level, dynamically-typed layer (described further down in thismodule). It consists of an interface called $(LREF IAllocator), which concreteallocators need to implement. The interface primitives themselves are obliviousto the type of the objects being allocated; they only deal in `void[]`, bynecessity of the interface being dynamic (as opposed to type-parameterized).Each thread has a current allocator it uses by default, which is a thread-localvariable $(LREF theAllocator) of type $(LREF IAllocator). The process has aglobal allocator called $(LREF processAllocator), also of type $(LREFIAllocator). When a new thread is created, $(LREF processAllocator) is copiedinto $(LREF theAllocator). An application can change the objects to which thesereferences point. By default, at application startup, $(LREF processAllocator)refers to an object that uses D's garbage collected heap. This layer alsoinclude high-level functions such as $(LREF make) and $(LREF dispose) thatcomfortably allocate/create and respectively destroy/deallocate objects. Thislayer is all needed for most casual uses of allocation primitives.) $(LI A mid-level, statically-typed layer for assembling several allocators intoone. It uses properties of the type of the objects being created to routeallocation requests to possibly specialized allocators. This layer is relativelythin and implemented and documented in the $(MREFstd,experimental,allocator,typed) module. It allows an interested user to e.g.use different allocators for arrays versus fixed-sized objects, to the end ofbetter overall performance.) $(LI A low-level collection of highly generic $(I heap building blocks)$(MDASH)Lego-like pieces that can be used to assemble application-specific allocators.The real allocation smarts are occurring at this level. This layer is ofinterest to advanced applications that want to configure their own allocators.A good illustration of typical uses of these building blocks is module $(MREFstd,experimental,allocator,showcase) which defines a collection of frequently-used preassembled allocator objects. The implementation and documentation entrypoint is $(MREF std,experimental,allocator,building_blocks). By design, theprimitives of the static interface have the same signatures as the $(LREFIAllocator) primitives but are for the most part optional and driven by staticintrospection. The parameterized class $(LREF CAllocatorImpl) offers animmediate and useful means to package a static low-level allocator into animplementation of $(LREF IAllocator).) $(LI Core allocator objects that interface with D's garbage collected heap($(MREF std,experimental,allocator,gc_allocator)), the C `malloc` family($(MREF std,experimental,allocator,mallocator)), and the OS ($(MREFstd,experimental,allocator,mmap_allocator)). Most custom allocators wouldultimately obtain memory from one of these core allocators.)) $(H2 Idiomatic Use of `std.experimental.allocator`) As of this time, `std.experimental.allocator` is not integrated with D'sbuilt-in operators that allocate memory, such as `new`, array literals, orarray concatenation operators. That means `std.experimental.allocator` isopt-in$(MDASH)applications need to make explicit use of it. For casual creation and disposal of dynamically-allocated objects, use $(LREFmake), $(LREF dispose), and the array-specific functions $(LREF makeArray),$(LREF expandArray), and $(LREF shrinkArray). These use by default D's garbagecollected heap, but open the application to better configuration options. Theseprimitives work either with `theAllocator` but also with any allocator obtainedby combining heap building blocks. For example: ----void fun(size_t n){    // Use the current allocator    int[] a1 = theAllocator.makeArray!int(n);    scope(exit) theAllocator.dispose(a1);    ...}---- To experiment with alternative allocators, set $(LREF theAllocator) for thecurrent thread. For example, consider an application that allocates many 8-byteobjects. These are not well supported by the default allocator, so a$(MREF_ALTTEXT free list allocator,std,experimental,allocator,building_blocks,free_list) would be recommended.To install one in `main`, the application would use: ----void main(){    import std.experimental.allocator.building_blocks.free_list        : FreeList;    theAllocator = allocatorObject(FreeList!8());    ...}---- $(H3 Saving the `IAllocator` Reference For Later Use) As with any global resource, setting `theAllocator` and `processAllocator`should not be done often and casually. In particular, allocating memory withone allocator and deallocating with another causes undefined behavior.Typically, these variables are set during application initialization phase andlast through the application. To avoid this, long-lived objects that need to perform allocations,reallocations, and deallocations relatively often may want to store a referenceto the allocator object they use throughout their lifetime. Then, instead ofusing `theAllocator` for internal allocation-related tasks, they'd use theinternally held reference. For example, consider a user-defined hash table: ----struct HashTable{    private IAllocator allocator;    this(size_t buckets, IAllocator allocator = theAllocator) {        this.allocator = allocator;        ...    }    // Getter and setter    IAllocator allocator() { return allocator; }    void allocator(IAllocator a) { assert(empty); allocator = a; }}---- Following initialization, the `HashTable` object would consistently use its`allocator` object for acquiring memory. Furthermore, setting`HashTable.allocator` to point to a different allocator should be legal butonly if the object is empty; otherwise, the object wouldn't be able todeallocate its existing state. $(H3 Using Allocators without `IAllocator`) Allocators assembled from the heap building blocks don't need to go through`IAllocator` to be usable. They have the same primitives as `IAllocator` andthey work with $(LREF make), $(LREF makeArray), $(LREF dispose) etc. So itsuffice to create allocator objects wherever fit and use them appropriately: ----void fun(size_t n){    // Use a stack-installed allocator for up to 64KB    StackFront!65536 myAllocator;    int[] a2 = myAllocator.makeArray!int(n);    scope(exit) myAllocator.dispose(a2);    ...}---- In this case, `myAllocator` does not obey the `IAllocator` interface, butimplements its primitives so it can work with `makeArray` by means of ducktyping. One important thing to note about this setup is that statically-typed assembledallocators are almost always faster than allocators that go through`IAllocator`. An important rule of thumb is: "assemble allocator first, adaptto `IAllocator` after". A good allocator implements intricate logic by means oftemplate assembly, and gets wrapped with `IAllocator` (usually by means of$(LREF allocatorObject)) only once, at client level. Copyright: Andrei Alexandrescu 2013-. License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP erdani.com, Andrei Alexandrescu) Source: $(PHOBOSSRC std/experimental/allocator) */ module std.experimental.allocator; public import std.experimental.allocator.common,    std.experimental.allocator.typed; // Fix https://issues.dlang.org/show_bug.cgi?id=17806// this should always be the first unittest in this module in order to ensure// that we use the `processAllocator` setter before the getter@system unittest{    import std.experimental.allocator.mallocator : Mallocator;    import std.experimental.allocator.gc_allocator : GCAllocator;    auto newAlloc = sharedAllocatorObject(Mallocator.instance);    processAllocator = newAlloc;    assert(processAllocator is newAlloc);    processAllocator = sharedAllocatorObject(GCAllocator.instance);} // Example in the synopsis above@system unittest{    import std.algorithm.comparison : min, max;    import std.experimental.allocator.building_blocks.allocator_list        : AllocatorList;    import std.experimental.allocator.building_blocks.bitmapped_block        : BitmappedBlock;    import std.experimental.allocator.building_blocks.bucketizer : Bucketizer;    import std.experimental.allocator.building_blocks.free_list : FreeList;    import std.experimental.allocator.building_blocks.segregator : Segregator;    import std.experimental.allocator.gc_allocator : GCAllocator;     alias FList = FreeList!(GCAllocator, 0, unbounded);    alias A = Segregator!(        8, FreeList!(GCAllocator, 0, 8),        128, Bucketizer!(FList, 1, 128, 16),        256, Bucketizer!(FList, 129, 256, 32),        512, Bucketizer!(FList, 257, 512, 64),        1024, Bucketizer!(FList, 513, 1024, 128),        2048, Bucketizer!(FList, 1025, 2048, 256),        3584, Bucketizer!(FList, 2049, 3584, 512),        4072 * 1024, AllocatorList!(            (n) => BitmappedBlock!(4096)(                    cast(ubyte[])(GCAllocator.instance.allocate(                        max(n, 4072 * 1024))))),        GCAllocator    );    A tuMalloc;    auto b = tuMalloc.allocate(500);    assert(b.length == 500);    auto c = tuMalloc.allocate(113);    assert(c.length == 113);    assert(tuMalloc.expand(c, 14));    tuMalloc.deallocate(b);    tuMalloc.deallocate(c);} import std.range.primitives;import std.traits;import std.typecons; /**Dynamic allocator interface. Code that defines allocators ultimately implementsthis interface. This should be used wherever a uniform type is required forencapsulating various allocator implementations. Composition of allocators is not recommended at this level due toinflexibility of dynamic interfaces and inefficiencies caused by cascadedmultiple calls. Instead, compose allocators using the static interface definedin $(MREF std,experimental,allocator,building_blocks),then adapt the composedallocator to `IAllocator` (possibly by using $(LREF CAllocatorImpl) below). Methods returning `Ternary` return `Ternary.yes` upon success,`Ternary.no` upon failure, and `Ternary.unknown` if the primitive is notimplemented by the allocator instance.*/interface IAllocator{nothrow:    /**    Returns the alignment offered.    */    @property uint alignment();     /**    Returns the good allocation size that guarantees zero internal    fragmentation.    */    size_t goodAllocSize(size_t s);     /**    Allocates `n` bytes of memory.    */    void[] allocate(size_t, TypeInfo ti = null);     /**    Allocates `n` bytes of memory with specified alignment `a`. Implementations    that do not support this primitive should always return `null`.    */    void[] alignedAllocate(size_t n, uint a);     /**    Allocates and returns all memory available to this allocator.    Implementations that do not support this primitive should always return    `null`.    */    void[] allocateAll();     /**    Expands a memory block in place and returns `true` if successful.    Implementations that don't support this primitive should always return    `false`.    */    bool expand(ref void[], size_t);     /// Reallocates a memory block.    bool reallocate(ref void[], size_t);     /// Reallocates a memory block with specified alignment.    bool alignedReallocate(ref void[] b, size_t size, uint alignment);     /**    Returns `Ternary.yes` if the allocator owns `b`, `Ternary.no` if    the allocator doesn't own `b`, and `Ternary.unknown` if ownership    cannot be determined. Implementations that don't support this primitive    should always return `Ternary.unknown`.    */    Ternary owns(void[] b);     /**    Resolves an internal pointer to the full block allocated. Implementations    that don't support this primitive should always return `Ternary.unknown`.    */    Ternary resolveInternalPointer(const void* p, ref void[] result);     /**    Deallocates a memory block. Implementations that don't support this    primitive should always return `false`. A simple way to check that an    allocator supports deallocation is to call `deallocate(null)`.    */    bool deallocate(void[] b);     /**    Deallocates all memory. Implementations that don't support this primitive    should always return `false`.    */    bool deallocateAll();     /**    Returns `Ternary.yes` if no memory is currently allocated from this    allocator, `Ternary.no` if some allocations are currently active, or    `Ternary.unknown` if not supported.    */    Ternary empty();     /**    Increases the reference count of the concrete class that implements this    interface.     For stateless allocators, this does nothing.    */    @safe @nogc pure    void incRef();     /**    Decreases the reference count of the concrete class that implements this    interface.    When the reference count is `0`, the object self-destructs.     Returns: `true` if the reference count is greater than `0` and `false` when    it hits `0`. For stateless allocators, it always returns `true`.    */    @safe @nogc pure    bool decRef();} /**A reference counted struct that wraps the dynamic allocator interface.This should be used wherever a uniform type is required for encapsulatingvarious allocator implementations. Code that defines allocators ultimately implements the $(LREF IAllocator)interface, possibly by using $(LREF CAllocatorImpl) below, and then build a`RCIAllocator` out of this. Composition of allocators is not recommended at this level due toinflexibility of dynamic interfaces and inefficiencies caused by cascadedmultiple calls. Instead, compose allocators using the static interface definedin $(A std_experimental_allocator_building_blocks.html,`std.experimental.allocator.building_blocks`), then adapt the composedallocator to `RCIAllocator` (possibly by using $(LREF allocatorObject) below).*/struct RCIAllocator{    private IAllocator _alloc; nothrow:    private @nogc pure @safe    this(this _)(IAllocator alloc)    {        assert(alloc);        _alloc = alloc;    }     @nogc pure @safe    this(this)    {        if (_alloc !is null)        {            _alloc.incRef();        }    }     @nogc pure @safe    ~this()    {        if (_alloc !is null)        {            bool isLast = !_alloc.decRef();            if (isLast) _alloc = null;        }    }     @nogc pure @safe    auto ref opAssign()(typeof(this) rhs)    {        if (_alloc is rhs._alloc)        {            return this;        }        // incRef was allready called by rhs posblit, so we're just moving        // calling dtor is the equivalent of decRef        __dtor();        _alloc = rhs._alloc;        // move        rhs._alloc = null;        return this;    }     @nogc pure @safe    bool isNull(this _)()    {        return _alloc is null;    }     @property uint alignment()    {        assert(_alloc);        return _alloc.alignment();    }     size_t goodAllocSize(size_t s)    {        assert(_alloc);        return _alloc.goodAllocSize(s);    }     void[] allocate(size_t n, TypeInfo ti = null)    {        assert(_alloc);        return _alloc.allocate(n, ti);    }     void[] alignedAllocate(size_t n, uint a)

Lines 1–500 of 3908.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t