Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions changelog/spawn-by-ref.dd
Original file line number Diff line number Diff line change
@@ -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);
---
72 changes: 66 additions & 6 deletions std/concurrency.d
Original file line number Diff line number Diff line change
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 @@ -457,11 +481,13 @@ private template isSpawnable(F, T...)
* pointer indirection. This is necessary for enforcing isolation among
* threads.
*/
Tid spawn(F, T...)(F fn, T args)
Tid spawn(F, T...)(F fn, auto ref T args)
if (isSpawnable!(F, T))
{
import std.functional : forward;
static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed.");
return _spawn(false, fn, args);
static assert(!hasLocalReferencePassing!(F, T), "Passing by reference thread-local data is not allowed.");
return _spawn(false, fn, forward!args);
}

///
Expand Down Expand Up @@ -513,6 +539,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 @@ -532,19 +588,23 @@ if (isSpawnable!(F, T))
* Returns:
* A Tid representing the new thread.
*/
Tid spawnLinked(F, T...)(F fn, T args)
Tid spawnLinked(F, T...)(F fn, auto ref T args)
if (isSpawnable!(F, T))
{
import std.functional : forward;
static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed.");
return _spawn(true, fn, args);
static assert(!hasLocalReferencePassing!(F, T), "Passing by reference thread-local data is not allowed.");
return _spawn(true, fn, forward!args);
}

/*
*
*/
private Tid _spawn(F, T...)(bool linked, F fn, T args)
private Tid _spawn(F, T...)(bool linked, F fn, auto ref T args)
if (isSpawnable!(F, T))
{
import std.functional : forward;

// TODO: MessageList and &exec should be shared.
auto spawnTid = Tid(new MessageBox);
auto ownerTid = thisTid;
Expand All @@ -553,7 +613,7 @@ if (isSpawnable!(F, T))
{
thisInfo.ident = spawnTid;
thisInfo.owner = ownerTid;
fn(args);
fn(forward!args);
}

// TODO: MessageList and &exec should be shared.
Expand Down