shared: Has anyone used it without a lot of pain?
via Digitalmars-d
digitalmars-d at puremagic.com
Wed Apr 5 09:39:58 PDT 2017
On Tuesday, 4 April 2017 at 21:56:37 UTC, Atila Neves wrote:
> I feel dirty if I write `__gshared`. I sneeze when I read it.
> But everytime I try and use `shared` I get trouble for it.
>
> TIL that if I want a struct to be both `shared` and not,
> destructors are out of the way. Because while constructors are
> easy because we can have more than one:
>
> struct Foo {
> this(this T)() { }
> }
>
> auto f1 = const Foo();
> auto f2 = shared Foo();
>
> There can be only one destructor:
>
>
> struct Bar {
> this(this T)() { }
> ~this() {} // no shared, if there was the problem would
> reverse
> // ~this(this T)() {} isn't a thing
> }
>
> auto b1 = const Bar();
> // Error: non-shared method foo.Bar.~this is not callable using
> a shared object
> // auto b2 = shared Bar(); //oops
>
> The reason why what I was trying to do isn't possible is
> obvious in hindsight, but it's still annoying. So either code
> duplication or mixins, huh?
>
> Atila
The error message pretty much tells you that multiple threads
should not be allowed to call the destructor concurrently, i.e.
you should somehow guarantee that by the end of the scope only
one thread has access to the object, which is what should happen
in most multi-threaded programs.
A workable, but non the less dirty way of sharing RAII objects
would be something along the lines:
struct Widget
{
this() { /* ... */ }
void doWork() scope shared { /* ... */ }
~this() { /* ... */ }
}
Owner thread A
{
/* 0) Make a new widget w/ automatic
storage (RAII). Note: calling
non-shared constructor since
construction is a one thread endeavor
anyway and we need non-shared `this`
to call the destructor. */
auto w = Widget();
/* 1) share `w` with other threads
and perform some useful work... */
// Other thread B
(ref scope shared(Widget) w) @safe
{
ssw.doWork();
}
/* 2) Ensure that A is now the only
thread with reference to `w`. */
/* 3) w.~this() called automatically.
Safe, since thread A is only
one with reference to w. */
}
2) can be achieved only if you pass `scope` references to `w` in
1), so that non of the other threads would be able to store a
pointer to `w` in a variable with longer lifetime.
You also need to have a way of ensuring that the other threads
have shorter lifetime than `w` (i.e. simple fork-join
parallelism), or you need some sort of task infrastructure that
allows you to block thread A until thread B finishes working on
the task created in 1) and ensuring no references have escaped in
thread B.
The other approach is to not use RAII at all, but instead to use
an Arc (atomic reference counting) wrapper that @trusted-ly knows
to cast the 'shared' qualifier off Widget when the ref count
drops to zero in order to call the destructor.
// create a non-shared Widget and transfer
// the ownership to the Arc wrapper which
// makes the object `shared` with the world.
auto arcW = Arc!(shared W)(new W());
// auto w = new W();
// auto arcFromNonUniqueW = Arc!(shared W)(w); <- doesn't compile
void use1(ref shared(W) w) @safe;
// arcW.get().use1(); // Doesn't compile:
// Error: reference to local variable arcW assigned to non-scope
parameter w calling arc_test.use1
void use2(ref scope shared(W) w) @safe;
arcW.get().use2(); // OK
More information about the Digitalmars-d
mailing list