Skip to content

Commit

Permalink
Allows passing by reference through spawn* if is a shared type
Browse files Browse the repository at this point in the history
It is often useful to pass a mutable by reference type to the spawned
thread, the need to have to use a raw pointer before this patch made
the syntax boring and not in line with the rest of the DLang style
(use pointer as little as possible).

Signed-off-by: Ernesto Castellotti <erny.castell@gmail.com>
  • Loading branch information
Ernesto Castellotti committed Aug 8, 2019
1 parent 225a6af commit eec972c
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 1 deletion.
20 changes: 20 additions & 0 deletions changelog/spawn-by-ref.dd
@@ -0,0 +1,20 @@
Allows passing by reference through spawn* if is a shared type

Now it will be possible to pass variables to the thread created with spawn/spawnLinked by reference,
to do this you need the shared type and add ref to the attributes of the arguments of the function to be executed.

---
import std.concurrency : spawn;
import core.atomic : atomicOp;
import core.thread : thread_joinAll;

static void f1(ref shared(int) number)
{
atomicOp!"+="(number, 1);
}

shared(int) number = 10;
spawn(&f1, number);
thread_joinAll();
assert(number == 11);
---
115 changes: 114 additions & 1 deletion std/concurrency.d
Expand Up @@ -106,6 +106,30 @@ private
static assert(!hasLocalAliasing!(SysTime, Container));
}

bool hasLocalReferencePassing(F, Types...)()
{
bool doesIt = false;
static foreach (i, T; Types)
{
static if (!is(T == shared) && !is(T == Tid))
{
static if (ParameterStorageClassTuple!F[i] == ParameterStorageClass.ref_)
{
doesIt = true;
}
}
}

return doesIt;
}

@safe unittest
{
static void fun(ref int) {}
static assert(hasLocalReferencePassing!(typeof(fun), int));
static assert(!hasLocalReferencePassing!(typeof(fun), shared(int)));
}

enum MsgType
{
standard,
Expand Down Expand Up @@ -455,13 +479,27 @@ private template isSpawnable(F, T...)
* pointer indirection. This is necessary for enforcing isolation among
* threads.
*/

Tid spawn(F, T...)(F fn, T args)
if (isSpawnable!(F, T))
{
static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed.");
return _spawnnoref(false, fn, args);
}

Tid spawn(F, T...)(F fn, ref T args)
if (isSpawnable!(F, T))
{
static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed.");
static assert(!hasLocalReferencePassing!(F, T), "Passing by reference thread-local data is not allowed.");
return _spawn(false, fn, args);
}

Tid spawn(F)(F fn)
{
return _spawnnoargs(false, fn);
}

///
@system unittest
{
Expand Down Expand Up @@ -511,6 +549,36 @@ if (isSpawnable!(F, T))
assert(receivedMessage == "Hello World");
}

@system unittest
{
import core.atomic : atomicOp;
import core.thread : thread_joinAll;
import std.stdio;

static void f1(ref shared(int) number)
{
atomicOp!"+="(number, 1);
}

shared(int) number = 10;
spawn(&f1, number);
thread_joinAll();
assert(number == 11);
}

@system unittest
{
import core.atomic : atomicOp;

static void f1(ref shared(int) number)
{
atomicOp!"+="(number, 1);
}

int number = 10;
static assert(!__traits(compiles, spawn(&f1, number)));
}

/**
* Starts fn(args) in a logical thread and will receive a LinkTerminated
* message when the operation terminates.
Expand All @@ -530,17 +598,62 @@ if (isSpawnable!(F, T))
* Returns:
* A Tid representing the new thread.
*/

Tid spawnLinked(F, T...)(F fn, T args)
if (isSpawnable!(F, T))
{
static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed.");
return _spawnnoref(true, fn, args);
}

Tid spawnLinked(F, T...)(F fn, ref T args)
if (isSpawnable!(F, T))
{
static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed.");
static assert(!hasLocalReferencePassing!(F, T), "Passing by reference thread-local data is not allowed.");
return _spawn(true, fn, args);
}

Tid spawnLinked(F)(F fn)
{
return _spawnnoargs(true, fn);
}

/*
*
*/
private Tid _spawn(F, T...)(bool linked, F fn, T args)
private Tid _spawn(F, T...)(bool linked, F fn, ref T args)
if (isSpawnable!(F, T))
{
// TODO: MessageList and &exec should be shared.
auto spawnTid = Tid(new MessageBox);
auto ownerTid = thisTid;

void exec()
{
thisInfo.ident = spawnTid;
thisInfo.owner = ownerTid;
fn(args);
}

// TODO: MessageList and &exec should be shared.
if (scheduler !is null)
scheduler.spawn(&exec);
else
{
auto t = new Thread(&exec);
t.start();
}
thisInfo.links[spawnTid] = linked;
return spawnTid;
}

private Tid _spawnnoargs(F)(bool linked, F fn)
{
return _spawn(linked, fn);
}

private Tid _spawnnoref(F, T...)(bool linked, F fn, T args)
if (isSpawnable!(F, T))
{
// TODO: MessageList and &exec should be shared.
Expand Down

0 comments on commit eec972c

Please sign in to comment.