The `shared` keyword currently means one of two things:
1. You can use core.atomic with it
2. It's some struct and you BYOM (Bring Your Own Mutex)
Do you have to send mutable data to other threads? Are you tired
of locking a mutex and casting? Now you don't have to:
https://code.dlang.org/packages/fearless
I was envious of std::sync::Mutex from Rust and thought: can I
use DIP1000 to make this work in D and be @safe? Turns out, yes.
I'm going to write a blog post about this explaining the why and
how. I'm too tired right now though, so here's some code:
// compile with -dip1000
import fearless;
struct Foo {
int i;
}
int* gEvilInt;
void main() @safe {
// create an instance of Exclusive!Foo allocated on the GC
heap
auto foo = gcExclusive!Foo(42);
// from now the value inside `foo` can only be used by
calling `lock` (a.k.a. `borrow`)
{
int* oldIntPtr; // only here to demostrate scopes, see
below
auto xfoo = foo.lock(); // get exclusive access to the
data (this locks a mutex)
safeWriteln("i: ", xfoo.i);
xfoo.i = 1;
safeWriteln("i: ", xfoo.i);
// can't escape to a global
static assert(!__traits(compiles, gEvilInt = &xfoo.i));
// ok to assign to a local that lives less
int* intPtr;
static assert(__traits(compiles, intPtr = &xfoo.i));
// not ok to assign to a local that lives longer
static assert(!__traits(compiles, oldIntPtr = &xfoo.i));
}
// Demonstrate sending to another thread and mutating
auto tid = spawn(&func, thisTid);
tid.send(foo);
receiveOnly!Ended;
safeWriteln("i: ", foo.lock.i);
}
struct Ended{}
void func(Tid tid) @safe {
receive(
(Exclusive!Foo* m) {
auto xfoo = m.lock;
xfoo.i++;
},
);
tid.send(Ended());
}
// for some reason the writelns here are all @system
void safeWriteln(A...)(auto ref A args) {
import std.stdio: writeln;
import std.functional: forward;
() @trusted { writeln(forward!args); }();
}