https://github.com/atilaneves/fearless
Safe concurrency in D (`shared` made easier)
https://github.com/atilaneves/fearless
concurrency d dip1000 dlang dlanguage lifetimes multithread multithreading mutex mutex-synchronisation nogc ownership reference-counting rust rust-lang rust-language rustlang safe safety threading
Last synced: 13 days ago
JSON representation
Safe concurrency in D (`shared` made easier)
- Host: GitHub
- URL: https://github.com/atilaneves/fearless
- Owner: atilaneves
- License: bsl-1.0
- Created: 2018-05-22T20:34:15.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2024-04-22T17:36:09.000Z (about 1 year ago)
- Last Synced: 2025-03-24T02:05:32.636Z (about 1 month ago)
- Topics: concurrency, d, dip1000, dlang, dlanguage, lifetimes, multithread, multithreading, mutex, mutex-synchronisation, nogc, ownership, reference-counting, rust, rust-lang, rust-language, rustlang, safe, safety, threading
- Language: D
- Homepage:
- Size: 41 KB
- Stars: 54
- Watchers: 5
- Forks: 3
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# fearless
[](https://travis-ci.org/atilaneves/fearless)
[](https://codecov.io/gh/atilaneves/fearless)Safe concurrency in D
This package implements `@safe` easy sharing of mutable data between threads without having
to cast from shared and lock/unlock a mutex. It does so by using `scope` and
[DIP1000](https://github.com/dlang/DIPs/blob/master/DIPs/DIP1000.md). It was inspired by
Rust's [std::sync::Mutex](https://doc.rust-lang.org/1.21.0/std/sync/struct.Mutex.html).The main type is `Exclusive!T` which is safely shareable between
threads even if T is not `immutable` or `shared`. To create one, call
one of `gcExclusive` or `rcExclusive` with the parameters to the
constructor to create a type T. Passing an already created T would not
be safe since references to it or its internal data might exist
elsewhere.As the names indicate, `gcExclusive` allocates on the GC heap, whereas `rcExclusive` uses
`RefCounted` from [automem](https://github.com/atilaneves/automem). This works automatically
if automem can be imported, which is always the case when automem is listed as a DUB
dependency.To actually get access to the protected value, use `.lock()` (`borrow`
exists as an alias) to get exclusive access for the current block of
code.An example (notice that `main` is `@safe`):
```d
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`{
int* oldIntPtr; // only here to demonstrate 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(
// ref Exclusive!Foo doesn't compile, use pointer instead
(Exclusive!Foo* m) {
auto xfoo = m.lock;
xfoo.i++;
},
);tid.send(Ended());
}void safeWriteln(A...)(auto ref A args) { // for some reason the writelns here are all @system
import std.stdio: writeln;
import std.functional: forward;
() @trusted { writeln(forward!args); }();
}```
This program prints:
```
i: 42
i: 1
i: 2
```Please consult the examples directory and/or unit tests for more.