RefCounted -> SafeRefCounted, OldRefCounted -> RefCounted.

This commit is contained in:
Ate Eskola 2022-08-27 17:04:23 +03:00
parent afdd7fff93
commit 490caa7090
6 changed files with 105 additions and 105 deletions

View file

@ -1,17 +1,17 @@
Added `borrow` for `RefCounted`. `@safe` with `-preview=dip1000`. Added `SafeRefCounted`, that can be used in `@safe` with `-preview=dip1000`.
`std.typecons.RefCounted` has so far been only available for `@system` code, `RefCounted` is only available for `@system` code, because of possibility of
because of possibility of escaping a reference to its payload past the end of escaping a reference to its payload past the end of its lifetime. We have
its lifetime. We have added a `borrow` function, that lets one to safely added a modified copy of it, `std.typecons.SafeRefCounted`, and a
access and modify the payload. `-dip1000` prevents escaping a reference to it `borrow` function, that lets one to safely access and modify the payload.
in `@safe` code. `-dip1000` prevents escaping a reference to it in `@safe` code.
------- -------
@safe pure nothrow void fun() @safe pure nothrow void fun()
{ {
import std.typecons; import std.typecons;
auto rcInt = refCounted(5); auto rcInt = safeRefCounted(5);
assert(rcInt.borrow!(theInt => theInt) == 5); assert(rcInt.borrow!(theInt => theInt) == 5);
auto sameInt = rcInt; auto sameInt = rcInt;
assert(sameInt.borrow!"a" == 5); assert(sameInt.borrow!"a" == 5);
@ -27,26 +27,28 @@ in `@safe` code.
} }
------- -------
Direct access to the payload unfortunately remains `@system`, though. While Direct access to the payload unfortunately has to be `@system`, though. While
`-dip1000` prevents escaping the reference, it is possible to destroy the last `-dip1000` could prevent escaping the reference, it is possible to destroy the
reference before the end of it's scope: last reference before the end of it's scope:
------- -------
int destroyFirstAndUseLater() int destroyFirstAndUseLater()
{ {
import std.typecons; import std.typecons;
auto rc = RefCounted!int(123); auto rc = SafeRefCounted!int(123);
int* ptr = &rc.refCountedPayload(); int* ptr = &rc.refCountedPayload();
destroy(rc); destroy(rc);
return *ptr; // Reads from freed memory. Don't do this. return *ptr; // Reads from freed memory. Don't do this.
} }
------- -------
As a side effect, $(REF dirEntries, std, file) is now also `@safe` with As a side effect, this enabled us to make $(REF dirEntries, std, file) `@safe`
`-preview=dip1000`. with `-preview=dip1000`.
Some member functions of `RefCounted` that were `@safe` are not anymore. Some member functions of `RefCounted` that are `@safe` are not so in
`OldRefCounted` type and `oldRefCounted` function have been added for the old `SafeRefCounted`. `RefCounted` type and `refCounted` function are still
behaviour. However, please be aware that those are intended only for easing available for the old behaviour. However, please be aware that they are
migration and are likely to be deprecated in the future. intended only for backwards compatibility, and are likely to be renamed and
deprecated in the future (so that `RefCounted` name will alias to
`SafeRefCounted`).

View file

@ -392,7 +392,7 @@ if (!is(immutable T == immutable bool))
import core.memory : GC; import core.memory : GC;
import std.exception : enforce; import std.exception : enforce;
import std.typecons : RefCounted = OldRefCounted, RefCountedAutoInitialize; import std.typecons : RefCounted, RefCountedAutoInitialize;
// This structure is not copyable. // This structure is not copyable.
private struct Payload private struct Payload

View file

@ -70,7 +70,7 @@ if (isRandomAccessRange!(Store) || isRandomAccessRange!(typeof(Store.init[])))
import std.algorithm.sorting : HeapOps; import std.algorithm.sorting : HeapOps;
import std.exception : enforce; import std.exception : enforce;
import std.functional : binaryFun; import std.functional : binaryFun;
import std.typecons : OldRefCounted, RefCountedAutoInitialize; import std.typecons : RefCounted, RefCountedAutoInitialize;
static if (isRandomAccessRange!Store) static if (isRandomAccessRange!Store)
alias Range = Store; alias Range = Store;
@ -89,9 +89,9 @@ if (isRandomAccessRange!(Store) || isRandomAccessRange!(typeof(Store.init[])))
Store _store; Store _store;
size_t _length; size_t _length;
} }
// TODO: migrate to use the RefCounted. The problem is that some member // TODO: migrate to use the SafeRefCounted. The problem is that some member
// functions here become @system with a naive switch. // functions here become @system with a naive switch.
private OldRefCounted!(Data, RefCountedAutoInitialize.no) _payload; private RefCounted!(Data, RefCountedAutoInitialize.no) _payload;
// Comparison predicate // Comparison predicate
private alias comp = binaryFun!(less); private alias comp = binaryFun!(less);
// Convenience accessors // Convenience accessors

View file

@ -4442,11 +4442,6 @@ version (Windows) @safe unittest
Throws: Throws:
$(LREF FileException) if there is an error (including if the given $(LREF FileException) if there is an error (including if the given
file is not a directory). file is not a directory).
ABI note:
This function is currently a zero-argument template, because it has to
be compiled together with client code. Otherwise the binary name
mangling would be wrong for code without -DIP1000.
+/ +/
void rmdirRecurse(scope const(char)[] pathname) @safe void rmdirRecurse(scope const(char)[] pathname) @safe
{ {
@ -4471,8 +4466,9 @@ void rmdirRecurse(ref scope DirEntry de) @safe
else else
{ {
// dirEntries is @system without DIP1000 because it uses // dirEntries is @system without DIP1000 because it uses
// a DirIterator with a RefCounted variable, but here, no references // a DirIterator with a SafeRefCounted variable, but here, no
// to the payload is escaped to the outside, so this should be @trusted // references to the payload are escaped to the outside, so this should
// be @trusted
() @trusted { () @trusted {
// all children, recursively depth-first // all children, recursively depth-first
foreach (DirEntry e; dirEntries(de.name, SpanMode.depth, false)) foreach (DirEntry e; dirEntries(de.name, SpanMode.depth, false))
@ -4877,7 +4873,7 @@ struct DirIterator(bool useDIP1000)
"Please don't override useDIP1000 to disagree with compiler switch."); "Please don't override useDIP1000 to disagree with compiler switch.");
private: private:
RefCounted!(DirIteratorImpl, RefCountedAutoInitialize.no) impl; SafeRefCounted!(DirIteratorImpl, RefCountedAutoInitialize.no) impl;
this(string pathname, SpanMode mode, bool followSymlink) @trusted this(string pathname, SpanMode mode, bool followSymlink) @trusted
{ {

View file

@ -2331,13 +2331,13 @@ Allows to directly use range operations on lines of a file.
private struct ByLineImpl(Char, Terminator) private struct ByLineImpl(Char, Terminator)
{ {
private: private:
import std.typecons : OldRefCounted, RefCountedAutoInitialize; import std.typecons : RefCounted, RefCountedAutoInitialize;
/* Ref-counting stops the source range's Impl /* Ref-counting stops the source range's Impl
* from getting out of sync after the range is copied, e.g. * from getting out of sync after the range is copied, e.g.
* when accessing range.front, then using std.range.take, * when accessing range.front, then using std.range.take,
* then accessing range.front again. */ * then accessing range.front again. */
alias PImpl = OldRefCounted!(Impl, RefCountedAutoInitialize.no); alias PImpl = RefCounted!(Impl, RefCountedAutoInitialize.no);
PImpl impl; PImpl impl;
static if (isScalarType!Terminator) static if (isScalarType!Terminator)
@ -2553,13 +2553,13 @@ the contents may well have changed).
private struct ByLineCopy(Char, Terminator) private struct ByLineCopy(Char, Terminator)
{ {
private: private:
import std.typecons : OldRefCounted, RefCountedAutoInitialize; import std.typecons : RefCounted, RefCountedAutoInitialize;
/* Ref-counting stops the source range's ByLineCopyImpl /* Ref-counting stops the source range's ByLineCopyImpl
* from getting out of sync after the range is copied, e.g. * from getting out of sync after the range is copied, e.g.
* when accessing range.front, then using std.range.take, * when accessing range.front, then using std.range.take,
* then accessing range.front again. */ * then accessing range.front again. */
alias Impl = OldRefCounted!(ByLineCopyImpl!(Char, Terminator), alias Impl = RefCounted!(ByLineCopyImpl!(Char, Terminator),
RefCountedAutoInitialize.no); RefCountedAutoInitialize.no);
Impl impl; Impl impl;

View file

@ -22,8 +22,8 @@ $(TR $(TD Flags) $(TD
$(LREF Yes) $(LREF Yes)
)) ))
$(TR $(TD Memory allocation) $(TD $(TR $(TD Memory allocation) $(TD
$(LREF RefCounted) $(LREF SafeRefCounted)
$(LREF refCounted) $(LREF rafeRefCounted)
$(LREF RefCountedAutoInitialize) $(LREF RefCountedAutoInitialize)
$(LREF scoped) $(LREF scoped)
$(LREF Unique) $(LREF Unique)
@ -6559,8 +6559,8 @@ package template Bind(alias Template, args1...)
/** /**
Options regarding auto-initialization of a `RefCounted` object (see Options regarding auto-initialization of a `SafeRefCounted` object (see
the definition of `RefCounted` below). the definition of `SafeRefCounted` below).
*/ */
enum RefCountedAutoInitialize enum RefCountedAutoInitialize
{ {
@ -6581,8 +6581,8 @@ enum RefCountedAutoInitialize
int a = 42; int a = 42;
} }
RefCounted!(Foo, RefCountedAutoInitialize.yes) rcAuto; SafeRefCounted!(Foo, RefCountedAutoInitialize.yes) rcAuto;
RefCounted!(Foo, RefCountedAutoInitialize.no) rcNoAuto; SafeRefCounted!(Foo, RefCountedAutoInitialize.no) rcNoAuto;
assert(rcAuto.refCountedPayload.a == 42); assert(rcAuto.refCountedPayload.a == 42);
@ -6595,16 +6595,16 @@ enum RefCountedAutoInitialize
Defines a reference-counted object containing a `T` value as Defines a reference-counted object containing a `T` value as
payload. payload.
An instance of `RefCounted` is a reference to a structure, An instance of `SafeRefCounted` is a reference to a structure,
which is referred to as the $(I store), or $(I storage implementation which is referred to as the $(I store), or $(I storage implementation
struct) in this documentation. The store contains a reference count struct) in this documentation. The store contains a reference count
and the `T` payload. `RefCounted` uses `malloc` to allocate and the `T` payload. `SafeRefCounted` uses `malloc` to allocate
the store. As instances of `RefCounted` are copied or go out of the store. As instances of `SafeRefCounted` are copied or go out of
scope, they will automatically increment or decrement the reference scope, they will automatically increment or decrement the reference
count. When the reference count goes down to zero, `RefCounted` count. When the reference count goes down to zero, `SafeRefCounted`
will call `destroy` against the payload and call `free` to will call `destroy` against the payload and call `free` to
deallocate the store. If the `T` payload contains any references deallocate the store. If the `T` payload contains any references
to GC-allocated memory, then `RefCounted` will add it to the GC memory to GC-allocated memory, then `SafeRefCounted` will add it to the GC memory
that is scanned for pointers, and remove it from GC scanning before that is scanned for pointers, and remove it from GC scanning before
`free` is called on the store. `free` is called on the store.
@ -6616,11 +6616,11 @@ still be valid during the destructor call. This allows the `T` to
deallocate or clean up any non-GC resources immediately after the deallocate or clean up any non-GC resources immediately after the
reference count has reached zero. reference count has reached zero.
Without -preview=dip1000, `RefCounted` is unsafe and should be Without -preview=dip1000, `SafeRefCounted` is unsafe and should be
used with care. No references to the payload should be escaped outside used with care. No references to the payload should be escaped outside
the `RefCounted` object. the `SafeRefCounted` object.
With -preview=dip1000, `RefCounted` is safe if it's payload is accessed only With -preview=dip1000, `SafeRefCounted` is safe if it's payload is accessed only
with the $(LREF borrow) function. Scope semantics can also prevent accidental with the $(LREF borrow) function. Scope semantics can also prevent accidental
escaping of `refCountedPayload`, but it's still up to the user to not destroy escaping of `refCountedPayload`, but it's still up to the user to not destroy
the last counted reference while the payload is in use. Due to that, the last counted reference while the payload is in use. Due to that,
@ -6639,9 +6639,9 @@ If `T.this()` is annotated with `@disable` then `autoInit` must be
`RefCountedAutoInitialize.no` in order to compile. `RefCountedAutoInitialize.no` in order to compile.
See_Also: See_Also:
$(LREF OldRefCounted) $(LREF RefCounted)
*/ */
struct RefCounted(T, RefCountedAutoInitialize autoInit = struct SafeRefCounted(T, RefCountedAutoInitialize autoInit =
RefCountedAutoInitialize.yes) RefCountedAutoInitialize.yes)
if (!is(T == class) && !(is(T == interface))) if (!is(T == class) && !(is(T == interface)))
{ {
@ -6674,7 +6674,7 @@ if (!is(T == class) && !(is(T == interface)))
"Attempted to use an uninitialized payload."); "Attempted to use an uninitialized payload.");
} }
/// `RefCounted` storage implementation. /// `SafeRefCounted` storage implementation.
struct RefCountedStore struct RefCountedStore
{ {
private struct Impl private struct Impl
@ -6842,8 +6842,8 @@ to deallocate the corresponding resource.
/** /**
Assignment operators. Assignment operators.
Note: You may not assign a new payload to an uninitialized RefCounted, if auto Note: You may not assign a new payload to an uninitialized SafeRefCounted, if
initialization is off. Assigning another counted reference is still okay. auto initialization is off. Assigning another counted reference is still okay.
*/ */
void opAssign(typeof(this) rhs) void opAssign(typeof(this) rhs)
{ {
@ -6869,7 +6869,7 @@ initialization is off. Assigning another counted reference is still okay.
RefCountedAutoInitialize.yes), calls $(D RefCountedAutoInitialize.yes), calls $(D
refCountedStore.ensureInitialized). Otherwise, just issues $(D refCountedStore.ensureInitialized). Otherwise, just issues $(D
assert(refCountedStore.isInitialized)). Used with $(D alias assert(refCountedStore.isInitialized)). Used with $(D alias
refCountedPayload this;), so callers can just use the `RefCounted` refCountedPayload this;), so callers can just use the `SafeRefCounted`
object as a `T`. object as a `T`.
$(BLUE The first overload exists only if $(D autoInit == RefCountedAutoInitialize.yes).) $(BLUE The first overload exists only if $(D autoInit == RefCountedAutoInitialize.yes).)
@ -6940,7 +6940,7 @@ assert(refCountedStore.isInitialized)).
{ {
// A pair of an `int` and a `size_t` - the latter being the // A pair of an `int` and a `size_t` - the latter being the
// reference count - will be dynamically allocated // reference count - will be dynamically allocated
auto rc1 = RefCounted!int(5); auto rc1 = SafeRefCounted!int(5);
assert(rc1 == 5); assert(rc1 == 5);
// No more allocation, add just one extra reference count // No more allocation, add just one extra reference count
auto rc2 = rc1; auto rc2 = rc1;
@ -6954,16 +6954,16 @@ assert(refCountedStore.isInitialized)).
// `initialize` method accessed here // `initialize` method accessed here
pure @safe nothrow @nogc unittest pure @safe nothrow @nogc unittest
{ {
auto rc1 = RefCounted!(int, RefCountedAutoInitialize.no)(5); auto rc1 = SafeRefCounted!(int, RefCountedAutoInitialize.no)(5);
rc1._refCounted.initialize(); rc1._refCounted.initialize();
} }
pure @system unittest pure @system unittest
{ {
RefCounted!int* p; SafeRefCounted!int* p;
{ {
auto rc1 = RefCounted!int(5); auto rc1 = SafeRefCounted!int(5);
p = &rc1; p = &rc1;
assert(rc1 == 5); assert(rc1 == 5);
assert(rc1._refCounted._store._count == 1); assert(rc1._refCounted._store._count == 1);
@ -6979,10 +6979,10 @@ pure @system unittest
} }
assert(p._refCounted._store == null); assert(p._refCounted._store == null);
// RefCounted as a member // SafeRefCounted as a member
struct A struct A
{ {
RefCounted!int x; SafeRefCounted!int x;
this(int y) this(int y)
{ {
x._refCounted.initialize(y); x._refCounted.initialize(y);
@ -7003,7 +7003,7 @@ pure @system unittest
{ {
import std.algorithm.mutation : swap; import std.algorithm.mutation : swap;
RefCounted!int p1, p2; SafeRefCounted!int p1, p2;
swap(p1, p2); swap(p1, p2);
} }
@ -7019,7 +7019,7 @@ pure @system unittest
U u; U u;
} }
alias SRC = RefCounted!S; alias SRC = SafeRefCounted!S;
} }
// https://issues.dlang.org/show_bug.cgi?id=6436 // https://issues.dlang.org/show_bug.cgi?id=6436
@ -7031,9 +7031,9 @@ pure @system unittest
this(ref int lval) { assert(lval == 3); ++lval; } this(ref int lval) { assert(lval == 3); ++lval; }
} }
auto s1 = RefCounted!S(1); auto s1 = SafeRefCounted!S(1);
int lval = 3; int lval = 3;
auto s2 = RefCounted!S(lval); auto s2 = SafeRefCounted!S(lval);
assert(lval == 4); assert(lval == 4);
} }
@ -7042,20 +7042,20 @@ pure @system unittest
{ {
struct S { int* p; } struct S { int* p; }
auto s = RefCounted!S(null); auto s = SafeRefCounted!S(null);
} }
@betterC @system pure nothrow @nogc unittest @betterC @system pure nothrow @nogc unittest
{ {
RefCounted!int a; SafeRefCounted!int a;
a = 5; //This should not assert a = 5; //This should not assert
assert(a == 5); assert(a == 5);
RefCounted!int b; SafeRefCounted!int b;
b = a; //This should not assert either b = a; //This should not assert either
assert(b == 5); assert(b == 5);
RefCounted!(int*) c; SafeRefCounted!(int*) c;
} }
// https://issues.dlang.org/show_bug.cgi?id=21638 // https://issues.dlang.org/show_bug.cgi?id=21638
@ -7067,7 +7067,7 @@ pure @system unittest
this(int x) @nogc nothrow pure { this.x = x; } this(int x) @nogc nothrow pure { this.x = x; }
int x; int x;
} }
auto rc = RefCounted!(NoDefaultCtor, RefCountedAutoInitialize.no)(5); auto rc = SafeRefCounted!(NoDefaultCtor, RefCountedAutoInitialize.no)(5);
assert(rc.x == 5); assert(rc.x == 5);
} }
@ -7079,21 +7079,21 @@ pure @system unittest
// structs that do not have either toString or alias this. // structs that do not have either toString or alias this.
static struct A { Object a; } static struct A { Object a; }
auto a = A(new Object()); auto a = A(new Object());
auto r = refCounted(a); auto r = safeRefCounted(a);
assert(to!string(r) == to!string(a)); assert(to!string(r) == to!string(a));
assert(to!string(cast(const) r) == to!string(cast(const) a)); assert(to!string(cast(const) r) == to!string(cast(const) a));
// Check that string conversion is still transparent for refcounted // Check that string conversion is still transparent for refcounted
// structs that have alias this. // structs that have alias this.
static struct B { int b; alias b this; } static struct B { int b; alias b this; }
static struct C { B b; alias b this; } static struct C { B b; alias b this; }
assert(to!string(refCounted(C(B(123)))) == to!string(C(B(123)))); assert(to!string(safeRefCounted(C(B(123)))) == to!string(C(B(123))));
// https://issues.dlang.org/show_bug.cgi?id=22093 // https://issues.dlang.org/show_bug.cgi?id=22093
// Check that uninitialized refcounted structs that previously could be // Check that uninitialized refcounted structs that previously could be
// converted to strings still can be. // converted to strings still can be.
alias R = typeof(r); alias R = typeof(r);
R r2; R r2;
cast(void) (((const ref R a) => to!string(a))(r2)); cast(void) (((const ref R a) => to!string(a))(r2));
cast(void) to!string(RefCounted!(A, RefCountedAutoInitialize.no).init); cast(void) to!string(SafeRefCounted!(A, RefCountedAutoInitialize.no).init);
} }
// We tried to make `refCountedPayload` `@safe` in // We tried to make `refCountedPayload` `@safe` in
@ -7115,7 +7115,7 @@ pure @system unittest
int[] getArr2 (scope Container local) int[] getArr2 (scope Container local)
{ {
RefCounted!Container rc = local; SafeRefCounted!Container rc = local;
// Escapes a reference to expired reference counted struct // Escapes a reference to expired reference counted struct
// don't do this! // don't do this!
return rc.refCountedPayload().data; return rc.refCountedPayload().data;
@ -7123,7 +7123,7 @@ pure @system unittest
int destroyFirstAndUseLater() int destroyFirstAndUseLater()
{ {
auto rc = RefCounted!int(123); auto rc = SafeRefCounted!int(123);
int* ptr = &rc.refCountedPayload(); int* ptr = &rc.refCountedPayload();
destroy(rc); destroy(rc);
return *ptr; return *ptr;
@ -7141,7 +7141,7 @@ pure @system unittest
} }
/** /**
Borrows the payload of $(LREF RefCounted) for use in `fun`. Inferred as `@safe` Borrows the payload of $(LREF SafeRefCounted) for use in `fun`. Inferred as `@safe`
if `fun` is `@safe` and does not escape a reference to the payload. if `fun` is `@safe` and does not escape a reference to the payload.
The reference count will be incremented for the duration of the operation, The reference count will be incremented for the duration of the operation,
so destroying the last reference will not leave dangling references in so destroying the last reference will not leave dangling references in
@ -7164,7 +7164,7 @@ template borrow(alias fun)
auto ref borrow(RC)(RC refCount) if auto ref borrow(RC)(RC refCount) if
( (
isInstanceOf!(RefCounted, RC) isInstanceOf!(SafeRefCounted, RC)
&& is(typeof(unaryFun!fun(refCount.refCountedPayload))) && is(typeof(unaryFun!fun(refCount.refCountedPayload)))
) )
{ {
@ -7183,7 +7183,7 @@ template borrow(alias fun)
/// This example can be marked `@safe` with `-preview=dip1000`. /// This example can be marked `@safe` with `-preview=dip1000`.
@safe pure nothrow unittest @safe pure nothrow unittest
{ {
auto rcInt = refCounted(5); auto rcInt = safeRefCounted(5);
assert(rcInt.borrow!(theInt => theInt) == 5); assert(rcInt.borrow!(theInt => theInt) == 5);
auto sameInt = rcInt; auto sameInt = rcInt;
assert(sameInt.borrow!"a" == 5); assert(sameInt.borrow!"a" == 5);
@ -7205,7 +7205,7 @@ template borrow(alias fun)
int torpedoesFired = 0; int torpedoesFired = 0;
struct Destroyer { ~this() @safe { torpedoesFired++; } } struct Destroyer { ~this() @safe { torpedoesFired++; } }
alias RcInt = typeof(refCounted(0)); alias RcInt = typeof(safeRefCounted(0));
auto standardUsage(RcInt arg) auto standardUsage(RcInt arg)
{ {
return borrow!((ref x) => x)(arg); return borrow!((ref x) => x)(arg);
@ -7228,7 +7228,7 @@ template borrow(alias fun)
} }
auto destroyDuringApply() auto destroyDuringApply()
{ {
auto rc = refCounted(Destroyer()); auto rc = safeRefCounted(Destroyer());
return borrow!((ref x) return borrow!((ref x)
{ {
// Destroys the last reference to the payload, decrementing it's // Destroys the last reference to the payload, decrementing it's
@ -7246,7 +7246,7 @@ template borrow(alias fun)
// First, let's verify the dangerous functions really do what they are // First, let's verify the dangerous functions really do what they are
// supposed to do. // supposed to do.
auto testRc = refCounted(42); auto testRc = safeRefCounted(42);
assert(sideChannelEscape(testRc) == 42); assert(sideChannelEscape(testRc) == 42);
assert(&problematicRefReturn(testRc) == globalPtr); assert(&problematicRefReturn(testRc) == globalPtr);
@ -7263,20 +7263,20 @@ template borrow(alias fun)
} }
/** /**
* Initializes a `RefCounted` with `val`. The template parameter * Initializes a `SafeRefCounted` with `val`. The template parameter
* `T` of `RefCounted` is inferred from `val`. * `T` of `SafeRefCounted` is inferred from `val`.
* This function can be used to move non-copyable values to the heap. * This function can be used to move non-copyable values to the heap.
* It also disables the `autoInit` option of `RefCounted`. * It also disables the `autoInit` option of `SafeRefCounted`.
* *
* Params: * Params:
* val = The value to be reference counted * val = The value to be reference counted
* Returns: * Returns:
* An initialized `RefCounted` containing `val`. * An initialized `SafeRefCounted` containing `val`.
* See_Also: * See_Also:
* $(LREF oldRefCounted) * $(LREF refCounted)
* $(HTTP en.cppreference.com/w/cpp/memory/shared_ptr/make_shared, C++'s make_shared) * $(HTTP en.cppreference.com/w/cpp/memory/shared_ptr/make_shared, C++'s make_shared)
*/ */
RefCounted!(T, RefCountedAutoInitialize.no) refCounted(T)(T val) SafeRefCounted!(T, RefCountedAutoInitialize.no) safeRefCounted(T)(T val)
{ {
typeof(return) res; typeof(return) res;
res._refCounted.move(val); res._refCounted.move(val);
@ -7301,13 +7301,13 @@ RefCounted!(T, RefCountedAutoInitialize.no) refCounted(T)(T val)
assert(File.nDestroyed == 0); assert(File.nDestroyed == 0);
// make the file refcounted to share ownership // make the file ref counted to share ownership
// Note: // Note:
// We write a compound statement (brace-delimited scope) in which all `RefCounted!File` handles are created and deleted. // We write a compound statement (brace-delimited scope) in which all `SafeRefCounted!File` handles are created and deleted.
// This allows us to see (after the scope) what happens after all handles have been destroyed. // This allows us to see (after the scope) what happens after all handles have been destroyed.
{ {
// We move the content of `file` to a separate (and heap-allocated) `File` object, // We move the content of `file` to a separate (and heap-allocated) `File` object,
// managed-and-accessed via one-or-multiple (initially: one) `RefCounted!File` objects ("handles"). // managed-and-accessed via one-or-multiple (initially: one) `SafeRefCounted!File` objects ("handles").
// This "moving": // This "moving":
// (1) invokes `file`'s destructor (=> `File.nDestroyed` is incremented from 0 to 1 and `file.name` becomes `null`); // (1) invokes `file`'s destructor (=> `File.nDestroyed` is incremented from 0 to 1 and `file.name` becomes `null`);
// (2) overwrites `file` with `File.init` (=> `file.name` becomes `null`). // (2) overwrites `file` with `File.init` (=> `file.name` becomes `null`).
@ -7315,18 +7315,18 @@ RefCounted!(T, RefCountedAutoInitialize.no) refCounted(T)(T val)
// but please note that (2) is only performed if `File` defines a destructor (or post-blit operator), // but please note that (2) is only performed if `File` defines a destructor (or post-blit operator),
// and in the absence of the `nDestroyed` instrumentation there would have been no reason to define a destructor. // and in the absence of the `nDestroyed` instrumentation there would have been no reason to define a destructor.
import std.algorithm.mutation : move; import std.algorithm.mutation : move;
auto rcFile = refCounted(move(file)); auto rcFile = safeRefCounted(move(file));
assert(rcFile.name == "name"); assert(rcFile.name == "name");
assert(File.nDestroyed == 1); assert(File.nDestroyed == 1);
assert(file.name == null); assert(file.name == null);
// We create another `RefCounted!File` handle to the same separate `File` object. // We create another `SafeRefCounted!File` handle to the same separate `File` object.
// While any of the handles is still alive, the `File` object is kept alive (=> `File.nDestroyed` is not modified). // While any of the handles is still alive, the `File` object is kept alive (=> `File.nDestroyed` is not modified).
auto rcFile2 = rcFile; auto rcFile2 = rcFile;
assert(rcFile.refCountedStore.refCount == 2); assert(rcFile.refCountedStore.refCount == 2);
assert(File.nDestroyed == 1); assert(File.nDestroyed == 1);
} }
// The separate `File` object is deleted when the last `RefCounted!File` handle is destroyed // The separate `File` object is deleted when the last `SafeRefCounted!File` handle is destroyed
// (i.e. at the closing brace of the compound statement above, which destroys both handles: `rcFile` and `rcFile2`) // (i.e. at the closing brace of the compound statement above, which destroys both handles: `rcFile` and `rcFile2`)
// (=> `File.nDestroyed` is incremented again, from 1 to 2): // (=> `File.nDestroyed` is incremented again, from 1 to 2):
assert(File.nDestroyed == 2); assert(File.nDestroyed == 2);
@ -10042,12 +10042,13 @@ unittest
assert(s2.get().b == 3); assert(s2.get().b == 3);
} }
/// The old version of $(LREF RefCounted), before $(LREF borrow) and fully /// The old version of $(LREF SafeRefCounted), before $(LREF borrow) existed.
/// `@safe` usage of `RefCounted` existed. Old code may be relying on `@safe`ty /// Old code may be relying on `@safe`ty of some of the member functions which
/// of some of the member functions which cannot be safe in the new scheme, and /// cannot be safe in the new scheme, and
/// can avoid immediate breakage by using this. `RefCounted` should be /// can avoid immediate breakage by using this. `SafeRefCounted` should be
/// preferred, as this type is likely to be deprecated in the future. /// preferred, as this type is likely to be renamed and deprecated in the
struct OldRefCounted(T, RefCountedAutoInitialize autoInit = /// future.
struct RefCounted(T, RefCountedAutoInitialize autoInit =
RefCountedAutoInitialize.yes) RefCountedAutoInitialize.yes)
{ {
version (D_BetterC) version (D_BetterC)
@ -10254,7 +10255,7 @@ struct OldRefCounted(T, RefCountedAutoInitialize autoInit =
/// ///
@betterC pure @system nothrow @nogc unittest @betterC pure @system nothrow @nogc unittest
{ {
auto rc1 = OldRefCounted!int(5); auto rc1 = RefCounted!int(5);
assert(rc1 == 5); assert(rc1 == 5);
auto rc2 = rc1; auto rc2 = rc1;
rc2 = 42; rc2 = 42;
@ -10262,11 +10263,12 @@ struct OldRefCounted(T, RefCountedAutoInitialize autoInit =
} }
/** /**
* Like $(LREF refCounted) but used to initialize $(LREF OldRefCounted) instead. * Like $(LREF safeRefCounted) but used to initialize $(LREF RefCounted)
* Use only for backwards compatibility, otherwise use `refCounted`. This * instead. Use only for backwards compatibility, otherwise use
* function is likely to be deprecated in the future. * `safeRefCounted`. This function is likely to be renamed and deprecated in
* the future.
*/ */
OldRefCounted!(T, RefCountedAutoInitialize.no) oldRefCounted(T)(T val) RefCounted!(T, RefCountedAutoInitialize.no) refCounted(T)(T val)
{ {
typeof(return) res; typeof(return) res;
res._refCounted.move(val); res._refCounted.move(val);
@ -10291,7 +10293,7 @@ OldRefCounted!(T, RefCountedAutoInitialize.no) oldRefCounted(T)(T val)
{ {
import std.algorithm.mutation : move; import std.algorithm.mutation : move;
auto rcFile = oldRefCounted(move(file)); auto rcFile = refCounted(move(file));
assert(rcFile.name == "name"); assert(rcFile.name == "name");
assert(File.nDestroyed == 1); assert(File.nDestroyed == 1);
assert(file.name == null); assert(file.name == null);
@ -10309,10 +10311,10 @@ OldRefCounted!(T, RefCountedAutoInitialize.no) oldRefCounted(T)(T val)
// copy/paste errors. // copy/paste errors.
@system unittest @system unittest
{ {
auto s1 = oldRefCounted(1); auto s1 = refCounted(1);
auto s2 = s1; auto s2 = s1;
s2 = 5; s2 = 5;
assert(s1 == 5); assert(s1 == 5);
static assert(is(typeof(s2) static assert(is(typeof(s2)
== OldRefCounted!(int, RefCountedAutoInitialize.no))); == RefCounted!(int, RefCountedAutoInitialize.no)));
} }