mirror of
https://github.com/dlang/phobos.git
synced 2025-05-01 15:40:36 +03:00
Merge pull request #5921 from edi33416/rc_iallocator
Replace IAllocator with reference counted struct merged-on-behalf-of: Sebastian Wilzbach <sebi.wilzbach@gmail.com>
This commit is contained in:
commit
aa7b6c03ae
5 changed files with 658 additions and 58 deletions
19
changelog/std-experimental-allocator-rciallocator.dd
Normal file
19
changelog/std-experimental-allocator-rciallocator.dd
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Replace `std.experimental.allocator.IAllocator` with `std.experimental.allocator.RCIAllocator`
|
||||||
|
|
||||||
|
$(B Motivation):
|
||||||
|
|
||||||
|
Keep track of references to allocators so they don't escape, dangle,
|
||||||
|
and cause undefined behavior.
|
||||||
|
|
||||||
|
From now on, `RCIAllocator` will be used instead of the old `IAllocator`
|
||||||
|
interface. $(REF allocatorObject, std, experimental, allocator) can be used to build
|
||||||
|
a `RCIAllocator` out of a custom allocator.
|
||||||
|
|
||||||
|
------
|
||||||
|
import std.experimental.allocator.mallocator : Mallocator;
|
||||||
|
|
||||||
|
RCIAllocator a = allocatorObject(Mallocator.instance);
|
||||||
|
auto b = a.allocate(100);
|
||||||
|
assert(b.length == 100);
|
||||||
|
assert(a.deallocate(b));
|
||||||
|
------
|
23
changelog/std-experimental-allocator-rcisharedallocator.dd
Normal file
23
changelog/std-experimental-allocator-rcisharedallocator.dd
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
Replace `std.experimental.allocator.ISharedAllocator` with `std.experimental.allocator.RCISharedAllocator`
|
||||||
|
|
||||||
|
$(B Motivation):
|
||||||
|
|
||||||
|
Keep track of references to allocators so they don't escape, dangle,
|
||||||
|
and cause undefined behavior.
|
||||||
|
|
||||||
|
From now on, `RCISharedAllocator` will be used instead of the old
|
||||||
|
`ISharedAllocator` interface.
|
||||||
|
$(REF sharedAllocatorObject, std, experimental, allocator)` can be used to build
|
||||||
|
a `RCISharedAllocator` out of a custom allocator.
|
||||||
|
|
||||||
|
------
|
||||||
|
import std.experimental.allocator.building_blocks.free_list : SharedFreeList;
|
||||||
|
import std.experimental.allocator.mallocator : Mallocator;
|
||||||
|
|
||||||
|
shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) sharedFL;
|
||||||
|
shared RCISharedAllocator sharedFLObj = sharedAllocatorObject(sharedFL);
|
||||||
|
|
||||||
|
auto b = sharedFLObj.allocate(100);
|
||||||
|
assert(b.length == 100);
|
||||||
|
assert(sharedFLObj.deallocate(b));
|
||||||
|
------
|
|
@ -24,7 +24,7 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
|
||||||
{
|
{
|
||||||
import std.algorithm.comparison : min;
|
import std.algorithm.comparison : min;
|
||||||
import std.conv : emplace;
|
import std.conv : emplace;
|
||||||
import std.experimental.allocator : IAllocator, theAllocator;
|
import std.experimental.allocator : RCIAllocator, theAllocator;
|
||||||
import std.experimental.allocator.common : stateSize, forwardToMember,
|
import std.experimental.allocator.common : stateSize, forwardToMember,
|
||||||
roundUpToMultipleOf, alignedAt, alignDownTo, roundUpToMultipleOf,
|
roundUpToMultipleOf, alignedAt, alignDownTo, roundUpToMultipleOf,
|
||||||
hasStaticallyKnownAlignment;
|
hasStaticallyKnownAlignment;
|
||||||
|
@ -69,11 +69,11 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
|
||||||
static if (stateSize!Allocator)
|
static if (stateSize!Allocator)
|
||||||
{
|
{
|
||||||
Allocator _parent;
|
Allocator _parent;
|
||||||
static if (is(Allocator == IAllocator))
|
static if (is(Allocator == RCIAllocator))
|
||||||
{
|
{
|
||||||
Allocator parent()
|
Allocator parent()
|
||||||
{
|
{
|
||||||
if (_parent is null) _parent = theAllocator;
|
if (_parent.isNull) _parent = theAllocator;
|
||||||
assert(alignment <= _parent.alignment);
|
assert(alignment <= _parent.alignment);
|
||||||
return _parent;
|
return _parent;
|
||||||
}
|
}
|
||||||
|
@ -376,10 +376,10 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
|
||||||
@system unittest
|
@system unittest
|
||||||
{
|
{
|
||||||
import std.experimental.allocator.gc_allocator : GCAllocator;
|
import std.experimental.allocator.gc_allocator : GCAllocator;
|
||||||
import std.experimental.allocator : theAllocator, IAllocator;
|
import std.experimental.allocator : theAllocator, RCIAllocator;
|
||||||
|
|
||||||
// One word before and after each allocation.
|
// One word before and after each allocation.
|
||||||
auto A = AffixAllocator!(IAllocator, size_t, size_t)(theAllocator);
|
auto A = AffixAllocator!(RCIAllocator, size_t, size_t)(theAllocator);
|
||||||
auto a = A.allocate(11);
|
auto a = A.allocate(11);
|
||||||
A.prefix(a) = 0xCAFE_BABE;
|
A.prefix(a) = 0xCAFE_BABE;
|
||||||
A.suffix(a) = 0xDEAD_BEEF;
|
A.suffix(a) = 0xDEAD_BEEF;
|
||||||
|
@ -387,7 +387,7 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
|
||||||
&& A.suffix(a) == 0xDEAD_BEEF);
|
&& A.suffix(a) == 0xDEAD_BEEF);
|
||||||
|
|
||||||
// One word before and after each allocation.
|
// One word before and after each allocation.
|
||||||
auto B = AffixAllocator!(IAllocator, size_t, size_t)();
|
auto B = AffixAllocator!(RCIAllocator, size_t, size_t)();
|
||||||
auto b = B.allocate(11);
|
auto b = B.allocate(11);
|
||||||
B.prefix(b) = 0xCAFE_BABE;
|
B.prefix(b) = 0xCAFE_BABE;
|
||||||
B.suffix(b) = 0xDEAD_BEEF;
|
B.suffix(b) = 0xDEAD_BEEF;
|
||||||
|
|
|
@ -481,7 +481,7 @@ Forwards each of the methods in `funs` (if defined) to `member`.
|
||||||
|
|
||||||
version(unittest)
|
version(unittest)
|
||||||
{
|
{
|
||||||
import std.experimental.allocator : IAllocator, ISharedAllocator;
|
import std.experimental.allocator : RCIAllocator, RCISharedAllocator;
|
||||||
|
|
||||||
package void testAllocator(alias make)()
|
package void testAllocator(alias make)()
|
||||||
{
|
{
|
||||||
|
@ -607,18 +607,18 @@ version(unittest)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
package void testAllocatorObject(AllocInterface)(AllocInterface a)
|
package void testAllocatorObject(RCAllocInterface)(RCAllocInterface a)
|
||||||
if (is(AllocInterface : IAllocator)
|
if (is(RCAllocInterface == RCIAllocator)
|
||||||
|| is (AllocInterface : shared ISharedAllocator))
|
|| is (RCAllocInterface == shared RCISharedAllocator))
|
||||||
{
|
{
|
||||||
import std.conv : text;
|
import std.conv : text;
|
||||||
import std.math : isPowerOf2;
|
import std.math : isPowerOf2;
|
||||||
import std.stdio : writeln, stderr;
|
import std.stdio : writeln, stderr;
|
||||||
import std.typecons : Ternary;
|
import std.typecons : Ternary;
|
||||||
scope(failure) stderr.writeln("testAllocatorObject failed for ",
|
scope(failure) stderr.writeln("testAllocatorObject failed for ",
|
||||||
AllocInterface.stringof);
|
RCAllocInterface.stringof);
|
||||||
|
|
||||||
assert(a);
|
assert(!a.isNull);
|
||||||
|
|
||||||
// Test alignment
|
// Test alignment
|
||||||
assert(a.alignment.isPowerOf2);
|
assert(a.alignment.isPowerOf2);
|
||||||
|
|
|
@ -286,6 +286,7 @@ implemented by the allocator instance.
|
||||||
*/
|
*/
|
||||||
interface IAllocator
|
interface IAllocator
|
||||||
{
|
{
|
||||||
|
nothrow:
|
||||||
/**
|
/**
|
||||||
Returns the alignment offered.
|
Returns the alignment offered.
|
||||||
*/
|
*/
|
||||||
|
@ -361,6 +362,251 @@ interface IAllocator
|
||||||
$(D Ternary.unknown) if not supported.
|
$(D Ternary.unknown) if not supported.
|
||||||
*/
|
*/
|
||||||
Ternary empty();
|
Ternary empty();
|
||||||
|
|
||||||
|
/**
|
||||||
|
Increases the reference count of the concrete class that implements this
|
||||||
|
interface.
|
||||||
|
|
||||||
|
For stateless allocators, this does nothing.
|
||||||
|
*/
|
||||||
|
@safe @nogc
|
||||||
|
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
|
||||||
|
bool decRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
A reference counted struct that wraps the dynamic allocator interface.
|
||||||
|
This should be used wherever a uniform type is required for encapsulating
|
||||||
|
various 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 to
|
||||||
|
inflexibility of dynamic interfaces and inefficiencies caused by cascaded
|
||||||
|
multiple calls. Instead, compose allocators using the static interface defined
|
||||||
|
in $(A std_experimental_allocator_building_blocks.html,
|
||||||
|
`std.experimental.allocator.building_blocks`), then adapt the composed
|
||||||
|
allocator to `RCIAllocator` (possibly by using $(LREF allocatorObject) below).
|
||||||
|
*/
|
||||||
|
struct RCIAllocator
|
||||||
|
{
|
||||||
|
private IAllocator _alloc;
|
||||||
|
|
||||||
|
nothrow:
|
||||||
|
private this(this _)(IAllocator alloc)
|
||||||
|
{
|
||||||
|
assert(alloc);
|
||||||
|
_alloc = alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@nogc @safe
|
||||||
|
this(this)
|
||||||
|
{
|
||||||
|
if (_alloc !is null)
|
||||||
|
{
|
||||||
|
_alloc.incRef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@nogc
|
||||||
|
~this()
|
||||||
|
{
|
||||||
|
if (_alloc !is null)
|
||||||
|
{
|
||||||
|
bool isLast = !_alloc.decRef();
|
||||||
|
if (isLast) _alloc = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@nogc
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pure nothrow @safe @nogc
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.alignedAllocate(n, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void[] allocateAll()
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.allocateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool expand(ref void[] b, size_t size)
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.expand(b, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reallocate(ref void[] b, size_t size)
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.reallocate(b, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool alignedReallocate(ref void[] b, size_t size, uint alignment)
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.alignedReallocate(b, size, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ternary owns(void[] b)
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.owns(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ternary resolveInternalPointer(const void* p, ref void[] result)
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.resolveInternalPointer(p, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool deallocate(void[] b)
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.deallocate(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool deallocateAll()
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.deallocateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ternary empty()
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
import std.experimental.allocator.building_blocks.region : Region;
|
||||||
|
import std.conv : emplace;
|
||||||
|
|
||||||
|
auto reg = Region!()(new ubyte[1024]);
|
||||||
|
auto state = reg.allocate(stateSize!(CAllocatorImpl!(Region!(), Yes.indirect)));
|
||||||
|
auto regObj = emplace!(CAllocatorImpl!(Region!(), Yes.indirect))(state, ®);
|
||||||
|
|
||||||
|
auto rcalloc = RCIAllocator(regObj);
|
||||||
|
auto b = rcalloc.allocate(10);
|
||||||
|
assert(b.length == 10);
|
||||||
|
|
||||||
|
// The reference counting is zero based
|
||||||
|
assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 1);
|
||||||
|
{
|
||||||
|
auto rca2 = rcalloc;
|
||||||
|
assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 2);
|
||||||
|
}
|
||||||
|
assert((cast(CAllocatorImpl!(Region!(), Yes.indirect))(rcalloc._alloc)).rc == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
import std.conv;
|
||||||
|
import std.experimental.allocator.mallocator;
|
||||||
|
import std.experimental.allocator.building_blocks.stats_collector;
|
||||||
|
|
||||||
|
alias SCAlloc = StatsCollector!(Mallocator, Options.bytesUsed);
|
||||||
|
SCAlloc statsCollectorAlloc;
|
||||||
|
|
||||||
|
ulong bytesUsed = statsCollectorAlloc.bytesUsed;
|
||||||
|
assert(bytesUsed == 0);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto _allocator = allocatorObject(&statsCollectorAlloc);
|
||||||
|
bytesUsed = statsCollectorAlloc.bytesUsed;
|
||||||
|
assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc, Yes.indirect)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesUsed = statsCollectorAlloc.bytesUsed;
|
||||||
|
assert(bytesUsed == 0, "RCIAllocator leaks memory; leaked "
|
||||||
|
~ to!string(bytesUsed) ~ " bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
import std.conv;
|
||||||
|
import std.experimental.allocator.mallocator;
|
||||||
|
import std.experimental.allocator.building_blocks.stats_collector;
|
||||||
|
|
||||||
|
alias SCAlloc = StatsCollector!(Mallocator, Options.bytesUsed);
|
||||||
|
SCAlloc statsCollectorAlloc;
|
||||||
|
|
||||||
|
ulong bytesUsed = statsCollectorAlloc.bytesUsed;
|
||||||
|
assert(bytesUsed == 0);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto _allocator = allocatorObject(statsCollectorAlloc);
|
||||||
|
|
||||||
|
// Ensure that the allocator was passed through in CAllocatorImpl
|
||||||
|
// This allocator was used to allocate the chunk that holds the
|
||||||
|
// CAllocatorImpl object; which is it's own wrapper
|
||||||
|
bytesUsed = (cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed;
|
||||||
|
assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc)),
|
||||||
|
"RCIAllocator leaks memory; leaked " ~ to!string(bytesUsed) ~ " bytes");
|
||||||
|
_allocator.allocate(1);
|
||||||
|
bytesUsed = (cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed;
|
||||||
|
assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc)) + 1,
|
||||||
|
"RCIAllocator leaks memory; leaked " ~ to!string(bytesUsed) ~ " bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesUsed = statsCollectorAlloc.bytesUsed;
|
||||||
|
assert(bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc)),
|
||||||
|
"RCIAllocator leaks memory; leaked "
|
||||||
|
~ to!string(bytesUsed) ~ " bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -382,6 +628,7 @@ implemented by the allocator instance.
|
||||||
*/
|
*/
|
||||||
interface ISharedAllocator
|
interface ISharedAllocator
|
||||||
{
|
{
|
||||||
|
nothrow:
|
||||||
/**
|
/**
|
||||||
Returns the alignment offered.
|
Returns the alignment offered.
|
||||||
*/
|
*/
|
||||||
|
@ -457,18 +704,192 @@ interface ISharedAllocator
|
||||||
$(D Ternary.unknown) if not supported.
|
$(D Ternary.unknown) if not supported.
|
||||||
*/
|
*/
|
||||||
Ternary empty() shared;
|
Ternary empty() shared;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Increases the reference count of the concrete class that implements this
|
||||||
|
interface.
|
||||||
|
|
||||||
|
For stateless allocators, this does nothing.
|
||||||
|
*/
|
||||||
|
@safe @nogc
|
||||||
|
void incRef() shared;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Decreases the reference count of the concrete class that implements this
|
||||||
|
interface.
|
||||||
|
When the reference count is `0`, the object self-destructs.
|
||||||
|
|
||||||
|
For stateless allocators, this does nothing.
|
||||||
|
|
||||||
|
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
|
||||||
|
bool decRef() shared;
|
||||||
}
|
}
|
||||||
|
|
||||||
private shared ISharedAllocator _processAllocator;
|
/**
|
||||||
private IAllocator _threadAllocator;
|
A reference counted struct that wraps the dynamic shared allocator interface.
|
||||||
|
This should be used wherever a uniform type is required for encapsulating
|
||||||
|
various allocator implementations.
|
||||||
|
|
||||||
private IAllocator setupThreadAllocator() nothrow @nogc @safe
|
Code that defines allocators shareable across threads ultimately implements the
|
||||||
|
$(LREF ISharedAllocator) interface, possibly by using
|
||||||
|
$(LREF CSharedAllocatorImpl) below, and then build a `RCISharedAllocator` out
|
||||||
|
of this.
|
||||||
|
|
||||||
|
Composition of allocators is not recommended at this level due to
|
||||||
|
inflexibility of dynamic interfaces and inefficiencies caused by cascaded
|
||||||
|
multiple calls. Instead, compose allocators using the static interface defined
|
||||||
|
in $(A std_experimental_allocator_building_blocks.html,
|
||||||
|
`std.experimental.allocator.building_blocks`), then adapt the composed allocator
|
||||||
|
to `RCISharedAllocator` (possibly by using $(LREF sharedAllocatorObject) below).
|
||||||
|
*/
|
||||||
|
struct RCISharedAllocator
|
||||||
|
{
|
||||||
|
private shared ISharedAllocator _alloc;
|
||||||
|
|
||||||
|
nothrow:
|
||||||
|
private this(shared ISharedAllocator alloc)
|
||||||
|
{
|
||||||
|
assert(alloc);
|
||||||
|
_alloc = alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@nogc
|
||||||
|
this(this) shared
|
||||||
|
{
|
||||||
|
if (_alloc !is null)
|
||||||
|
{
|
||||||
|
_alloc.incRef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@nogc
|
||||||
|
~this()
|
||||||
|
{
|
||||||
|
if (_alloc !is null)
|
||||||
|
{
|
||||||
|
bool isLast = !_alloc.decRef();
|
||||||
|
if (isLast) _alloc = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@nogc
|
||||||
|
auto ref opAssign()(shared RCISharedAllocator rhs) shared
|
||||||
|
{
|
||||||
|
if (_alloc is rhs._alloc)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
// incRef was allready called by rhs posblit, so we're just moving
|
||||||
|
if (_alloc !is null)
|
||||||
|
{
|
||||||
|
_alloc.decRef();
|
||||||
|
}
|
||||||
|
_alloc = rhs._alloc;
|
||||||
|
// move
|
||||||
|
rhs._alloc = null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
pure nothrow @safe @nogc
|
||||||
|
bool isNull(this _)() shared
|
||||||
|
{
|
||||||
|
return _alloc is null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property uint alignment() shared
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.alignment();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t goodAllocSize(size_t s) shared
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.goodAllocSize(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void[] allocate(size_t n, TypeInfo ti = null) shared
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.allocate(n, ti);
|
||||||
|
}
|
||||||
|
|
||||||
|
void[] alignedAllocate(size_t n, uint a) shared
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.alignedAllocate(n, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void[] allocateAll() shared
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.allocateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool expand(ref void[] b, size_t size) shared
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.expand(b, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool reallocate(ref void[] b, size_t size) shared
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.reallocate(b, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool alignedReallocate(ref void[] b, size_t size, uint alignment) shared
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.alignedReallocate(b, size, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ternary owns(void[] b) shared
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.owns(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ternary resolveInternalPointer(const void* p, ref void[] result) shared
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.resolveInternalPointer(p, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool deallocate(void[] b) shared
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.deallocate(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool deallocateAll() shared
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.deallocateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ternary empty() shared
|
||||||
|
{
|
||||||
|
assert(_alloc);
|
||||||
|
return _alloc.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private shared RCISharedAllocator _processAllocator;
|
||||||
|
private RCIAllocator _threadAllocator;
|
||||||
|
|
||||||
|
nothrow @nogc @safe
|
||||||
|
private ref RCIAllocator setupThreadAllocator()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Forwards the `_threadAllocator` calls to the `processAllocator`
|
Forwards the `_threadAllocator` calls to the `processAllocator`
|
||||||
*/
|
*/
|
||||||
static class ThreadAllocator : IAllocator
|
static class ThreadAllocator : IAllocator
|
||||||
{
|
{
|
||||||
|
nothrow:
|
||||||
override @property uint alignment()
|
override @property uint alignment()
|
||||||
{
|
{
|
||||||
return processAllocator.alignment();
|
return processAllocator.alignment();
|
||||||
|
@ -533,12 +954,26 @@ private IAllocator setupThreadAllocator() nothrow @nogc @safe
|
||||||
{
|
{
|
||||||
return processAllocator.empty();
|
return processAllocator.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nothrow @safe @nogc
|
||||||
|
@safe @nogc
|
||||||
|
override void incRef()
|
||||||
|
{
|
||||||
|
processAllocator._alloc.incRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!_threadAllocator);
|
//nothrow @safe @nogc
|
||||||
|
@safe @nogc
|
||||||
|
override bool decRef()
|
||||||
|
{
|
||||||
|
return processAllocator._alloc.decRef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(_threadAllocator.isNull);
|
||||||
import std.conv : emplace;
|
import std.conv : emplace;
|
||||||
static ulong[stateSize!(ThreadAllocator).divideRoundUp(ulong.sizeof)] _threadAllocatorState;
|
static ulong[stateSize!(ThreadAllocator).divideRoundUp(ulong.sizeof)] _threadAllocatorState;
|
||||||
_threadAllocator = () @trusted { return emplace!(ThreadAllocator)(_threadAllocatorState[]); } ();
|
() @trusted { _threadAllocator = RCIAllocator(emplace!(ThreadAllocator)(_threadAllocatorState[])); }();
|
||||||
return _threadAllocator;
|
return _threadAllocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,16 +984,18 @@ to be shared across threads, use $(D processAllocator) (below). By default,
|
||||||
$(D theAllocator) ultimately fetches memory from $(D processAllocator), which
|
$(D theAllocator) ultimately fetches memory from $(D processAllocator), which
|
||||||
in turn uses the garbage collected heap.
|
in turn uses the garbage collected heap.
|
||||||
*/
|
*/
|
||||||
nothrow @safe @nogc @property IAllocator theAllocator()
|
nothrow @safe @nogc
|
||||||
|
@property ref RCIAllocator theAllocator()
|
||||||
{
|
{
|
||||||
auto p = _threadAllocator;
|
alias p = _threadAllocator;
|
||||||
return p !is null ? p : setupThreadAllocator();
|
return !p.isNull() ? p : setupThreadAllocator();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto
|
/// Ditto
|
||||||
nothrow @safe @nogc @property void theAllocator(IAllocator a)
|
nothrow @system @nogc
|
||||||
|
@property void theAllocator(RCIAllocator a)
|
||||||
{
|
{
|
||||||
assert(a);
|
assert(!a.isNull);
|
||||||
_threadAllocator = a;
|
_threadAllocator = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,18 +1019,26 @@ Gets/sets the allocator for the current process. This allocator must be used
|
||||||
for allocating memory shared across threads. Objects created using this
|
for allocating memory shared across threads. Objects created using this
|
||||||
allocator can be cast to $(D shared).
|
allocator can be cast to $(D shared).
|
||||||
*/
|
*/
|
||||||
@property shared(ISharedAllocator) processAllocator()
|
@trusted nothrow @nogc
|
||||||
|
@property ref shared(RCISharedAllocator) processAllocator()
|
||||||
{
|
{
|
||||||
import std.experimental.allocator.gc_allocator : GCAllocator;
|
import std.experimental.allocator.gc_allocator : GCAllocator;
|
||||||
import std.concurrency : initOnce;
|
import std.concurrency : initOnce;
|
||||||
return initOnce!_processAllocator(
|
|
||||||
|
static shared(RCISharedAllocator)* forceAttributes()
|
||||||
|
{
|
||||||
|
return &initOnce!_processAllocator(
|
||||||
sharedAllocatorObject(GCAllocator.instance));
|
sharedAllocatorObject(GCAllocator.instance));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return *(cast(shared(RCISharedAllocator)* function() nothrow @nogc)(&forceAttributes))();
|
||||||
|
}
|
||||||
|
|
||||||
/// Ditto
|
/// Ditto
|
||||||
@property void processAllocator(shared ISharedAllocator a)
|
nothrow @system @nogc
|
||||||
|
@property void processAllocator(shared RCISharedAllocator a)
|
||||||
{
|
{
|
||||||
assert(a);
|
assert(!a.isNull);
|
||||||
_processAllocator = a;
|
_processAllocator = a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,34 +1049,47 @@ allocator can be cast to $(D shared).
|
||||||
import std.experimental.allocator.building_blocks.free_list : SharedFreeList;
|
import std.experimental.allocator.building_blocks.free_list : SharedFreeList;
|
||||||
import std.experimental.allocator.mallocator : Mallocator;
|
import std.experimental.allocator.mallocator : Mallocator;
|
||||||
|
|
||||||
assert(processAllocator);
|
assert(!processAllocator.isNull);
|
||||||
assert(theAllocator);
|
assert(!theAllocator.isNull);
|
||||||
|
|
||||||
testAllocatorObject(processAllocator);
|
testAllocatorObject(processAllocator);
|
||||||
testAllocatorObject(theAllocator);
|
testAllocatorObject(theAllocator);
|
||||||
|
|
||||||
shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) sharedFL;
|
shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) sharedFL;
|
||||||
shared ISharedAllocator sharedFLObj = sharedAllocatorObject(sharedFL);
|
shared RCISharedAllocator sharedFLObj = sharedAllocatorObject(sharedFL);
|
||||||
assert(sharedFLObj);
|
alias SharedAllocT = CSharedAllocatorImpl!(
|
||||||
|
shared SharedFreeList!(
|
||||||
|
Mallocator, chooseAtRuntime, chooseAtRuntime));
|
||||||
|
|
||||||
|
assert((cast(SharedAllocT)(sharedFLObj._alloc)).rc == 1);
|
||||||
|
assert(!sharedFLObj.isNull);
|
||||||
testAllocatorObject(sharedFLObj);
|
testAllocatorObject(sharedFLObj);
|
||||||
|
|
||||||
// Test processAllocator setter
|
// Test processAllocator setter
|
||||||
shared ISharedAllocator oldProcessAllocator = processAllocator;
|
shared RCISharedAllocator oldProcessAllocator = processAllocator;
|
||||||
processAllocator = sharedFLObj;
|
processAllocator = sharedFLObj;
|
||||||
assert(processAllocator is sharedFLObj);
|
assert((cast(SharedAllocT)(sharedFLObj._alloc)).rc == 2);
|
||||||
|
assert(processAllocator._alloc is sharedFLObj._alloc);
|
||||||
|
|
||||||
testAllocatorObject(processAllocator);
|
testAllocatorObject(processAllocator);
|
||||||
testAllocatorObject(theAllocator);
|
testAllocatorObject(theAllocator);
|
||||||
assertThrown!AssertError(processAllocator = null);
|
assertThrown!AssertError(processAllocator = RCISharedAllocator(null));
|
||||||
|
|
||||||
// Restore initial processAllocator state
|
// Restore initial processAllocator state
|
||||||
processAllocator = oldProcessAllocator;
|
processAllocator = oldProcessAllocator;
|
||||||
|
assert((cast(SharedAllocT)(sharedFLObj._alloc)).rc == 1);
|
||||||
assert(processAllocator is oldProcessAllocator);
|
assert(processAllocator is oldProcessAllocator);
|
||||||
|
|
||||||
shared ISharedAllocator indirectShFLObj = sharedAllocatorObject(&sharedFL);
|
shared RCISharedAllocator indirectShFLObj = sharedAllocatorObject(&sharedFL);
|
||||||
testAllocatorObject(indirectShFLObj);
|
testAllocatorObject(indirectShFLObj);
|
||||||
|
alias IndirectSharedAllocT = CSharedAllocatorImpl!(
|
||||||
|
shared SharedFreeList!(
|
||||||
|
Mallocator, chooseAtRuntime, chooseAtRuntime)
|
||||||
|
, Yes.indirect);
|
||||||
|
|
||||||
IAllocator indirectMallocator = allocatorObject(&Mallocator.instance);
|
assert((cast(IndirectSharedAllocT)(indirectShFLObj._alloc)).rc == 1);
|
||||||
|
|
||||||
|
RCIAllocator indirectMallocator = allocatorObject(&Mallocator.instance);
|
||||||
testAllocatorObject(indirectMallocator);
|
testAllocatorObject(indirectMallocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2029,7 +2487,7 @@ own statically-typed allocator.)
|
||||||
)
|
)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
CAllocatorImpl!A allocatorObject(A)(auto ref A a)
|
RCIAllocator allocatorObject(A)(auto ref A a)
|
||||||
if (!isPointer!A)
|
if (!isPointer!A)
|
||||||
{
|
{
|
||||||
import std.conv : emplace;
|
import std.conv : emplace;
|
||||||
|
@ -2037,13 +2495,13 @@ if (!isPointer!A)
|
||||||
{
|
{
|
||||||
enum s = stateSize!(CAllocatorImpl!A).divideRoundUp(ulong.sizeof);
|
enum s = stateSize!(CAllocatorImpl!A).divideRoundUp(ulong.sizeof);
|
||||||
static __gshared ulong[s] state;
|
static __gshared ulong[s] state;
|
||||||
static __gshared CAllocatorImpl!A result;
|
static __gshared RCIAllocator result;
|
||||||
if (!result)
|
if (result.isNull)
|
||||||
{
|
{
|
||||||
// Don't care about a few races
|
// Don't care about a few races
|
||||||
result = emplace!(CAllocatorImpl!A)(state[]);
|
result = RCIAllocator(emplace!(CAllocatorImpl!A)(state[]));
|
||||||
}
|
}
|
||||||
assert(result);
|
assert(!result.isNull);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -2057,12 +2515,12 @@ if (!isPointer!A)
|
||||||
}
|
}
|
||||||
auto tmp = cast(CAllocatorImpl!A) emplace!(CAllocatorImpl!A)(state);
|
auto tmp = cast(CAllocatorImpl!A) emplace!(CAllocatorImpl!A)(state);
|
||||||
move(a, tmp.impl);
|
move(a, tmp.impl);
|
||||||
return tmp;
|
return RCIAllocator(tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto
|
/// Ditto
|
||||||
CAllocatorImpl!(A, Yes.indirect) allocatorObject(A)(A* pa)
|
RCIAllocator allocatorObject(A)(A* pa)
|
||||||
{
|
{
|
||||||
assert(pa);
|
assert(pa);
|
||||||
import std.conv : emplace;
|
import std.conv : emplace;
|
||||||
|
@ -2072,15 +2530,16 @@ CAllocatorImpl!(A, Yes.indirect) allocatorObject(A)(A* pa)
|
||||||
{
|
{
|
||||||
scope(failure) pa.deallocate(state);
|
scope(failure) pa.deallocate(state);
|
||||||
}
|
}
|
||||||
return emplace!(CAllocatorImpl!(A, Yes.indirect))
|
return RCIAllocator(emplace!(CAllocatorImpl!(A, Yes.indirect))
|
||||||
(state, pa);
|
(state, pa));
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@system unittest
|
@system unittest
|
||||||
{
|
{
|
||||||
import std.experimental.allocator.mallocator : Mallocator;
|
import std.experimental.allocator.mallocator : Mallocator;
|
||||||
IAllocator a = allocatorObject(Mallocator.instance);
|
|
||||||
|
RCIAllocator a = allocatorObject(Mallocator.instance);
|
||||||
auto b = a.allocate(100);
|
auto b = a.allocate(100);
|
||||||
assert(b.length == 100);
|
assert(b.length == 100);
|
||||||
assert(a.deallocate(b));
|
assert(a.deallocate(b));
|
||||||
|
@ -2109,9 +2568,11 @@ unittest
|
||||||
// Ensure that the allocator was passed through in CAllocatorImpl
|
// Ensure that the allocator was passed through in CAllocatorImpl
|
||||||
// This allocator was used to allocate the chunk that holds the
|
// This allocator was used to allocate the chunk that holds the
|
||||||
// CAllocatorImpl object; which is it's own wrapper
|
// CAllocatorImpl object; which is it's own wrapper
|
||||||
assert(_allocator.impl.bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc)));
|
assert((cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed
|
||||||
|
== stateSize!(CAllocatorImpl!(SCAlloc)));
|
||||||
_allocator.allocate(1);
|
_allocator.allocate(1);
|
||||||
assert(_allocator.impl.bytesUsed == stateSize!(CAllocatorImpl!(SCAlloc)) + 1);
|
assert((cast(CAllocatorImpl!(SCAlloc))(_allocator._alloc)).impl.bytesUsed
|
||||||
|
== stateSize!(CAllocatorImpl!(SCAlloc)) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2134,22 +2595,33 @@ statically-typed allocator.)
|
||||||
)
|
)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
shared(CSharedAllocatorImpl!A) sharedAllocatorObject(A)(auto ref A a)
|
//nothrow @safe
|
||||||
|
//nothrow @nogc @safe
|
||||||
|
nothrow
|
||||||
|
shared(RCISharedAllocator) sharedAllocatorObject(A)(auto ref A a)
|
||||||
if (!isPointer!A)
|
if (!isPointer!A)
|
||||||
{
|
{
|
||||||
import std.conv : emplace;
|
import std.conv : emplace;
|
||||||
static if (stateSize!A == 0)
|
static if (stateSize!A == 0)
|
||||||
{
|
{
|
||||||
enum s = stateSize!(CSharedAllocatorImpl!A).divideRoundUp(ulong.sizeof);
|
enum s = stateSize!(CSharedAllocatorImpl!A).divideRoundUp(ulong.sizeof);
|
||||||
static __gshared ulong[s] state;
|
static shared ulong[s] state;
|
||||||
static shared CSharedAllocatorImpl!A result;
|
static shared RCISharedAllocator result;
|
||||||
if (!result)
|
if (result.isNull)
|
||||||
{
|
{
|
||||||
// Don't care about a few races
|
// Don't care about a few races
|
||||||
result = cast(shared
|
//auto tmp = emplace!(CSharedAllocatorImpl!A)(
|
||||||
CSharedAllocatorImpl!A)(emplace!(CSharedAllocatorImpl!A)(state[]));
|
//(() @trusted => cast(ulong[]) state[])());
|
||||||
|
//result = RCISharedAllocator(
|
||||||
|
//(() @trusted => (cast(shared CSharedAllocatorImpl!A)(
|
||||||
|
//tmp)
|
||||||
|
//))());
|
||||||
|
result = RCISharedAllocator(
|
||||||
|
(cast(shared CSharedAllocatorImpl!A)(
|
||||||
|
emplace!(CSharedAllocatorImpl!A)(
|
||||||
|
(() @trusted => cast(ulong[]) state[])()))));
|
||||||
}
|
}
|
||||||
assert(result);
|
assert(!result.isNull);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else static if (is(typeof({ shared A b = a; shared A c = b; }))) // copyable
|
else static if (is(typeof({ shared A b = a; shared A c = b; }))) // copyable
|
||||||
|
@ -2163,7 +2635,7 @@ if (!isPointer!A)
|
||||||
}
|
}
|
||||||
auto tmp = emplace!(shared CSharedAllocatorImpl!A)(state);
|
auto tmp = emplace!(shared CSharedAllocatorImpl!A)(state);
|
||||||
move(a, tmp.impl);
|
move(a, tmp.impl);
|
||||||
return tmp;
|
return RCISharedAllocator(tmp);
|
||||||
}
|
}
|
||||||
else // the allocator object is not copyable
|
else // the allocator object is not copyable
|
||||||
{
|
{
|
||||||
|
@ -2172,7 +2644,7 @@ if (!isPointer!A)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ditto
|
/// Ditto
|
||||||
shared(CSharedAllocatorImpl!(A, Yes.indirect)) sharedAllocatorObject(A)(A* pa)
|
shared(RCISharedAllocator) sharedAllocatorObject(A)(A* pa)
|
||||||
{
|
{
|
||||||
assert(pa);
|
assert(pa);
|
||||||
import std.conv : emplace;
|
import std.conv : emplace;
|
||||||
|
@ -2182,7 +2654,7 @@ shared(CSharedAllocatorImpl!(A, Yes.indirect)) sharedAllocatorObject(A)(A* pa)
|
||||||
{
|
{
|
||||||
scope(failure) pa.deallocate(state);
|
scope(failure) pa.deallocate(state);
|
||||||
}
|
}
|
||||||
return emplace!(shared CSharedAllocatorImpl!(A, Yes.indirect))(state, pa);
|
return RCISharedAllocator(emplace!(shared CSharedAllocatorImpl!(A, Yes.indirect))(state, pa));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2199,12 +2671,16 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
|
||||||
{
|
{
|
||||||
import std.traits : hasMember;
|
import std.traits : hasMember;
|
||||||
|
|
||||||
|
static if (stateSize!Allocator) private size_t rc = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The implementation is available as a public member.
|
The implementation is available as a public member.
|
||||||
*/
|
*/
|
||||||
static if (indirect)
|
static if (indirect)
|
||||||
{
|
{
|
||||||
|
nothrow:
|
||||||
private Allocator* pimpl;
|
private Allocator* pimpl;
|
||||||
|
@nogc
|
||||||
ref Allocator impl()
|
ref Allocator impl()
|
||||||
{
|
{
|
||||||
return *pimpl;
|
return *pimpl;
|
||||||
|
@ -2220,6 +2696,7 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
|
||||||
else alias impl = Allocator.instance;
|
else alias impl = Allocator.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nothrow:
|
||||||
/// Returns `impl.alignment`.
|
/// Returns `impl.alignment`.
|
||||||
override @property uint alignment()
|
override @property uint alignment()
|
||||||
{
|
{
|
||||||
|
@ -2366,6 +2843,44 @@ class CAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nothrow @safe @nogc
|
||||||
|
override void incRef()
|
||||||
|
{
|
||||||
|
static if (stateSize!Allocator) ++rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
nothrow @trusted @nogc
|
||||||
|
override bool decRef()
|
||||||
|
{
|
||||||
|
static if (stateSize!Allocator)
|
||||||
|
{
|
||||||
|
import core.stdc.string : memcpy;
|
||||||
|
|
||||||
|
if (rc == 1)
|
||||||
|
{
|
||||||
|
static if (indirect)
|
||||||
|
{
|
||||||
|
Allocator* tmp = pimpl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Allocator tmp;
|
||||||
|
memcpy(&tmp, &this.impl, Allocator.sizeof);
|
||||||
|
}
|
||||||
|
void[] support = (cast(void*) this)[0 .. stateSize!(typeof(this))];
|
||||||
|
tmp.deallocate(support);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
--rc;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2381,13 +2896,18 @@ class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
|
||||||
: ISharedAllocator
|
: ISharedAllocator
|
||||||
{
|
{
|
||||||
import std.traits : hasMember;
|
import std.traits : hasMember;
|
||||||
|
import core.atomic : atomicOp, atomicLoad;
|
||||||
|
|
||||||
|
static if (stateSize!Allocator) shared size_t rc = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The implementation is available as a public member.
|
The implementation is available as a public member.
|
||||||
*/
|
*/
|
||||||
static if (indirect)
|
static if (indirect)
|
||||||
{
|
{
|
||||||
|
nothrow:
|
||||||
private shared Allocator* pimpl;
|
private shared Allocator* pimpl;
|
||||||
|
@nogc
|
||||||
ref Allocator impl() shared
|
ref Allocator impl() shared
|
||||||
{
|
{
|
||||||
return *pimpl;
|
return *pimpl;
|
||||||
|
@ -2403,6 +2923,7 @@ class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
|
||||||
else alias impl = Allocator.instance;
|
else alias impl = Allocator.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nothrow:
|
||||||
/// Returns `impl.alignment`.
|
/// Returns `impl.alignment`.
|
||||||
override @property uint alignment() shared
|
override @property uint alignment() shared
|
||||||
{
|
{
|
||||||
|
@ -2549,6 +3070,43 @@ class CSharedAllocatorImpl(Allocator, Flag!"indirect" indirect = No.indirect)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nothrow @safe @nogc
|
||||||
|
override void incRef() shared
|
||||||
|
{
|
||||||
|
static if (stateSize!Allocator) atomicOp!"+="(rc, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
nothrow @trusted @nogc
|
||||||
|
override bool decRef() shared
|
||||||
|
{
|
||||||
|
static if (stateSize!Allocator)
|
||||||
|
{
|
||||||
|
import core.stdc.string : memcpy;
|
||||||
|
|
||||||
|
// rc starts as 1 to avoid comparing with size_t(0) - 1
|
||||||
|
if (atomicOp!"-="(rc, 1) == 0)
|
||||||
|
{
|
||||||
|
static if (indirect)
|
||||||
|
{
|
||||||
|
Allocator* tmp = pimpl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Allocator tmp;
|
||||||
|
memcpy(cast(void*) &tmp, cast(void*) &this.impl, Allocator.sizeof);
|
||||||
|
}
|
||||||
|
void[] support = (cast(void*) this)[0 .. stateSize!(typeof(this))];
|
||||||
|
tmp.deallocate(support);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue