"Fossies" - the Fresh Open Source Software Archive

Member "dmd2/src/phobos/std/experimental/allocator/building_blocks/scoped_allocator.d" (20 Nov 2020, 8128 Bytes) of package /linux/misc/dmd.2.094.2.linux.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) D source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // Written in the D programming language.
    2 /**
    3 Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/scoped_allocator.d)
    4 */
    5 module std.experimental.allocator.building_blocks.scoped_allocator;
    6 
    7 import std.experimental.allocator.common;
    8 
    9 /**
   10 
   11 `ScopedAllocator` delegates all allocation requests to `ParentAllocator`.
   12 When destroyed, the `ScopedAllocator` object automatically calls $(D
   13 deallocate) for all memory allocated through its lifetime. (The $(D
   14 deallocateAll) function is also implemented with the same semantics.)
   15 
   16 `deallocate` is also supported, which is where most implementation effort
   17 and overhead of `ScopedAllocator` go. If `deallocate` is not needed, a
   18 simpler design combining `AllocatorList` with `Region` is recommended.
   19 
   20 */
   21 struct ScopedAllocator(ParentAllocator)
   22 {
   23     static if (!stateSize!ParentAllocator)
   24     {
   25         // This test is available only for stateless allocators
   26         version (StdUnittest)
   27         @system unittest
   28         {
   29             testAllocator!(() => ScopedAllocator());
   30         }
   31     }
   32 
   33     import std.experimental.allocator.building_blocks.affix_allocator
   34         : AffixAllocator;
   35     import std.traits : hasMember;
   36     import std.typecons : Ternary;
   37 
   38     private struct Node
   39     {
   40         Node* prev;
   41         Node* next;
   42         size_t length;
   43     }
   44 
   45     alias Allocator = AffixAllocator!(ParentAllocator, Node);
   46 
   47     // state
   48     /**
   49     If `ParentAllocator` is stateful, `parent` is a property giving access
   50     to an `AffixAllocator!ParentAllocator`. Otherwise, `parent` is an alias for `AffixAllocator!ParentAllocator.instance`.
   51     */
   52     static if (stateSize!ParentAllocator)
   53     {
   54         Allocator parent;
   55     }
   56     else
   57     {
   58         alias parent = Allocator.instance;
   59     }
   60     private Node* root;
   61 
   62     /**
   63     `ScopedAllocator` is not copyable.
   64     */
   65     @disable this(this);
   66 
   67     /**
   68     `ScopedAllocator`'s destructor releases all memory allocated during its
   69     lifetime.
   70     */
   71     ~this()
   72     {
   73         deallocateAll;
   74     }
   75 
   76     /// Alignment offered
   77     enum alignment = Allocator.alignment;
   78 
   79     /**
   80     Forwards to `parent.goodAllocSize` (which accounts for the management
   81     overhead).
   82     */
   83     size_t goodAllocSize(size_t n)
   84     {
   85         return parent.goodAllocSize(n);
   86     }
   87 
   88     // Common code shared between allocate and allocateZeroed.
   89     private enum _processAndReturnAllocateResult =
   90     q{
   91        if (!b.ptr) return b;
   92         Node* toInsert = & parent.prefix(b);
   93         toInsert.prev = null;
   94         toInsert.next = root;
   95         toInsert.length = n;
   96         assert(!root || !root.prev);
   97         if (root) root.prev = toInsert;
   98         root = toInsert;
   99         return b;
  100     };
  101 
  102     /**
  103     Allocates memory. For management it actually allocates extra memory from
  104     the parent.
  105     */
  106     void[] allocate(size_t n)
  107     {
  108         auto b = parent.allocate(n);
  109         mixin(_processAndReturnAllocateResult);
  110     }
  111 
  112     static if (hasMember!(Allocator, "allocateZeroed"))
  113     package(std) void[] allocateZeroed()(size_t n)
  114     {
  115         auto b = parent.allocateZeroed(n);
  116         mixin(_processAndReturnAllocateResult);
  117     }
  118 
  119     /**
  120     Forwards to $(D parent.expand(b, delta)).
  121     */
  122     static if (hasMember!(Allocator, "expand"))
  123     bool expand(ref void[] b, size_t delta)
  124     {
  125         auto result = parent.expand(b, delta);
  126         if (result && b)
  127         {
  128             () @trusted { parent.prefix(b).length = b.length; }();
  129         }
  130         return result;
  131     }
  132 
  133     /**
  134     Reallocates `b` to new size `s`.
  135     */
  136     bool reallocate(ref void[] b, size_t s)
  137     {
  138         // Remove from list
  139         if (b.ptr)
  140         {
  141             Node* n = & parent.prefix(b);
  142             if (n.prev) n.prev.next = n.next;
  143             else root = n.next;
  144             if (n.next) n.next.prev = n.prev;
  145         }
  146         auto result = parent.reallocate(b, s);
  147         // Add back to list
  148         if (b.ptr)
  149         {
  150             Node* n = & parent.prefix(b);
  151             n.prev = null;
  152             n.next = root;
  153             n.length = s;
  154             if (root) root.prev = n;
  155             root = n;
  156         }
  157         return result;
  158     }
  159 
  160     /**
  161     Forwards to `parent.owns(b)`.
  162     */
  163     static if (hasMember!(Allocator, "owns"))
  164     Ternary owns(void[] b)
  165     {
  166         return parent.owns(b);
  167     }
  168 
  169     /**
  170     Deallocates `b`.
  171     */
  172     static if (hasMember!(Allocator, "deallocate"))
  173     bool deallocate(void[] b)
  174     {
  175         // Remove from list
  176         if (b.ptr)
  177         {
  178             Node* n = & parent.prefix(b);
  179             if (n.prev) n.prev.next = n.next;
  180             else root = n.next;
  181             if (n.next) n.next.prev = n.prev;
  182         }
  183         return parent.deallocate(b);
  184     }
  185 
  186     /**
  187     Deallocates all memory allocated.
  188     */
  189     bool deallocateAll()
  190     {
  191         bool result = true;
  192         for (auto n = root; n; )
  193         {
  194             void* p = n + 1;
  195             auto length = n.length;
  196             n = n.next;
  197             if (!parent.deallocate(p[0 .. length]))
  198                 result = false;
  199         }
  200         root = null;
  201         return result;
  202     }
  203 
  204     /**
  205     Returns `Ternary.yes` if this allocator is not responsible for any memory,
  206     `Ternary.no` otherwise. (Never returns `Ternary.unknown`.)
  207     */
  208     pure nothrow @safe @nogc
  209     Ternary empty() const
  210     {
  211         return Ternary(root is null);
  212     }
  213 }
  214 
  215 ///
  216 @system unittest
  217 {
  218     import std.experimental.allocator.mallocator : Mallocator;
  219     import std.typecons : Ternary;
  220     ScopedAllocator!Mallocator alloc;
  221     assert(alloc.empty == Ternary.yes);
  222     const b = alloc.allocate(10);
  223     assert(b.length == 10);
  224     assert(alloc.empty == Ternary.no);
  225 }
  226 
  227 version (StdUnittest)
  228 @system unittest
  229 {
  230     import std.experimental.allocator.gc_allocator : GCAllocator;
  231     testAllocator!(() => ScopedAllocator!GCAllocator());
  232 }
  233 
  234 @system unittest // https://issues.dlang.org/show_bug.cgi?id=16046
  235 {
  236     import std.exception;
  237     import std.experimental.allocator;
  238     import std.experimental.allocator.mallocator;
  239     ScopedAllocator!Mallocator alloc;
  240     auto foo = alloc.make!int(1).enforce;
  241     auto bar = alloc.make!int(2).enforce;
  242     alloc.dispose(foo);
  243     alloc.dispose(bar); // segfault here
  244 }
  245 
  246 @system unittest
  247 {
  248     import std.experimental.allocator.gc_allocator : GCAllocator;
  249     ScopedAllocator!GCAllocator a;
  250 
  251     assert(__traits(compiles, (() nothrow @safe @nogc => a.goodAllocSize(0))()));
  252 
  253     // Ensure deallocate inherits from parent allocators
  254     auto b = a.allocate(42);
  255     assert(b.length == 42);
  256     () nothrow @nogc { a.deallocate(b); }();
  257 }
  258 
  259 // Test that deallocateAll infers from parent
  260 @system unittest
  261 {
  262     import std.experimental.allocator.building_blocks.region : Region;
  263 
  264     ScopedAllocator!(Region!()) a;
  265     a.parent.parent = Region!()(new ubyte[1024 * 64]);
  266     auto b = a.allocate(42);
  267     assert(b.length == 42);
  268     assert((() pure nothrow @safe @nogc => a.expand(b, 22))());
  269     assert(b.length == 64);
  270     assert((() nothrow @nogc => a.reallocate(b, 100))());
  271     assert(b.length == 100);
  272     assert((() nothrow @nogc => a.deallocateAll())());
  273 }
  274 
  275 @system unittest
  276 {
  277     import std.experimental.allocator.building_blocks.region : Region;
  278     import std.experimental.allocator.mallocator : Mallocator;
  279     import std.typecons : Ternary;
  280 
  281     auto a = Region!(Mallocator)(1024 * 64);
  282     auto b = a.allocate(42);
  283     assert(b.length == 42);
  284     assert((() pure nothrow @safe @nogc => a.expand(b, 22))());
  285     assert(b.length == 64);
  286     assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes);
  287     assert((() nothrow @nogc => a.reallocate(b, 100))());
  288     assert(b.length == 100);
  289     assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes);
  290     assert((() pure nothrow @safe @nogc => a.owns(null))() == Ternary.no);
  291 }
  292 
  293 // Test empty
  294 @system unittest
  295 {
  296     import std.experimental.allocator.mallocator : Mallocator;
  297     import std.typecons : Ternary;
  298     ScopedAllocator!Mallocator alloc;
  299 
  300     assert((() pure nothrow @safe @nogc => alloc.empty)() == Ternary.yes);
  301     const b = alloc.allocate(10);
  302     assert((() pure nothrow @safe @nogc => alloc.empty)() == Ternary.no);
  303 }