/** * $(SCRIPT inhibitQuickIndex = 1;) * $(DIVC quickindex, * $(BOOKTABLE, * $(TR $(TH Category) $(TH Symbols)) * $(TR $(TD Tid) $(TD * $(MYREF join) * $(MYREF locate) * $(MYREF ownerTid) * $(MYREF register) * $(MYREF spawn) * $(MYREF spawnLinked) * $(MYREF thisTid) * $(MYREF Tid) * $(MYREF TidMissingException) * $(MYREF unregister) * )) * $(TR $(TD Message passing) $(TD * $(MYREF prioritySend) * $(MYREF receive) * $(MYREF receiveOnly) * $(MYREF receiveTimeout) * $(MYREF send) * $(MYREF setMaxMailboxSize) * )) * $(TR $(TD Message-related types) $(TD * $(MYREF LinkTerminated) * $(MYREF MailboxFull) * $(MYREF MessageMismatch) * $(MYREF OnCrowding) * $(MYREF OwnerTerminated) * $(MYREF PriorityMessageException) * )) * $(TR $(TD Scheduler) $(TD * $(MYREF FiberScheduler) * $(MYREF Generator) * $(MYREF Scheduler) * $(MYREF scheduler) * $(MYREF ThreadInfo) * $(MYREF ThreadScheduler) * $(MYREF yield) * )) * $(TR $(TD Misc) $(TD * $(MYREF initOnce) * )) * )) * * This is a low-level messaging API upon which more structured or restrictive * APIs may be built. The general idea is that every messageable entity is * represented by a common handle type called a `Tid`, which allows messages to * be sent to logical threads that are executing in both the current process * and in external processes using the same interface. This is an important * aspect of scalability because it allows the components of a program to be * spread across available resources with few to no changes to the actual * implementation. * * A logical thread is an execution context that has its own stack and which * runs asynchronously to other logical threads. These may be preemptively * scheduled kernel threads, $(MREF_ALTTEXT fibers, core, thread, fiber) * (cooperative user-space threads), or some other concept with similar behavior. * * The type of concurrency used when logical threads are created is determined * by the $(LREF Scheduler) selected at initialization time. The default behavior is * currently to create a new kernel thread per call to spawn, but other * schedulers are available that multiplex fibers across the main thread or * use some combination of the two approaches. * * Copyright: Copyright Sean Kelly 2009 - 2014. * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. * Authors: Sean Kelly, Alex Rønne Petersen, Martin Nowak * Source: $(PHOBOSSRC std/concurrency.d) *//* Copyright Sean Kelly 2009 - 2014. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) */module std.concurrency; public import std.variant; import core.atomic;import core.sync.condition;import core.sync.mutex;import core.thread;import std.range.primitives;import std.range.interfaces : InputRange;import std.traits; ///@system unittest{ __gshared string received; static void spawnedFunc(Tid ownerTid) { import std.conv : text; // Receive a message from the owner thread. receive((int i){ received = text("Received the number ", i); // Send a message back to the owner thread // indicating success. send(ownerTid, true); }); } // Start spawnedFunc in a new thread. auto childTid = spawn(&spawnedFunc, thisTid); // Send the number 42 to this new thread. send(childTid, 42); // Receive the result code. auto wasSuccessful = receiveOnly!(bool); assert(wasSuccessful); assert(received == "Received the number 42");} private{ bool hasLocalAliasing(Types...)() { import std.typecons : Rebindable; // Works around "statement is not reachable" bool doesIt = false; static foreach (T; Types) { static if (is(T == Tid)) { /* Allowed */ } else static if (is(T : Rebindable!R, R)) doesIt |= hasLocalAliasing!R; else static if (is(T == struct)) doesIt |= hasLocalAliasing!(typeof(T.tupleof)); else doesIt |= std.traits.hasUnsharedAliasing!(T); } return doesIt; } @safe unittest { static struct Container { Tid t; } static assert(!hasLocalAliasing!(Tid, Container, int)); } // https://issues.dlang.org/show_bug.cgi?id=20097 @safe unittest { import std.datetime.systime : SysTime; static struct Container { SysTime time; } static assert(!hasLocalAliasing!(SysTime, Container)); } enum MsgType { standard, priority, linkDead, } struct Message { MsgType type; Variant data; this(T...)(MsgType t, T vals) if (T.length > 0) { static if (T.length == 1) { type = t; data = vals[0]; } else { import std.typecons : Tuple; type = t; data = Tuple!(T)(vals); } } @property auto convertsTo(T...)() { static if (T.length == 1) { return is(T[0] == Variant) || data.convertsTo!(T); } else { import std.typecons : Tuple; return data.convertsTo!(Tuple!(T)); } } @property auto get(T...)() { static if (T.length == 1) { static if (is(T[0] == Variant)) return data; else return data.get!(T); } else { import std.typecons : Tuple; return data.get!(Tuple!(T)); } } auto map(Op)(Op op) { alias Args = Parameters!(Op); static if (Args.length == 1) { static if (is(Args[0] == Variant)) return op(data); else return op(data.get!(Args)); } else { import std.typecons : Tuple; return op(data.get!(Tuple!(Args)).expand); } } } void checkops(T...)(T ops) { import std.format : format; foreach (i, t1; T) { static assert(isFunctionPointer!t1 || isDelegate!t1, format!"T %d is not a function pointer or delegates"(i)); alias a1 = Parameters!(t1); alias r1 = ReturnType!(t1); static if (i < T.length - 1 && is(r1 == void)) { static assert(a1.length != 1 || !is(a1[0] == Variant), "function with arguments " ~ a1.stringof ~ " occludes successive function"); foreach (t2; T[i + 1 .. $]) { alias a2 = Parameters!(t2); static assert(!is(a1 == a2), "function with arguments " ~ a1.stringof ~ " occludes successive function"); } } } } @property ref ThreadInfo thisInfo() nothrow { import core.atomic : atomicLoad; auto localScheduler = atomicLoad(scheduler); if (localScheduler is null) return ThreadInfo.thisInfo; return localScheduler.thisInfo; }} static ~this(){ thisInfo.cleanup();} // Exceptions /** * Thrown on calls to $(LREF receiveOnly) if a message other than the type * the receiving thread expected is sent. */class MessageMismatch : Exception{ /// this(string msg = "Unexpected message type") @safe pure nothrow @nogc { super(msg); }} /** * Thrown on calls to $(LREF receive) if the thread that spawned the receiving * thread has terminated and no more messages exist. */class OwnerTerminated : Exception{ /// this(Tid t, string msg = "Owner terminated") @safe pure nothrow @nogc { super(msg); tid = t; } Tid tid;} /** * Thrown if a linked thread has terminated. */class LinkTerminated : Exception{ /// this(Tid t, string msg = "Link terminated") @safe pure nothrow @nogc { super(msg); tid = t; } Tid tid;} /** * Thrown if a message was sent to a thread via * $(REF prioritySend, std,concurrency) and the receiver does not have a handler * for a message of this type. */class PriorityMessageException : Exception{ /// this(Variant vals) { super("Priority message"); message = vals; } /** * The message that was sent. */ Variant message;} /** * Thrown on mailbox crowding if the mailbox is configured with * `OnCrowding.throwException`. */class MailboxFull : Exception{ /// this(Tid t, string msg = "Mailbox full") @safe pure nothrow @nogc { super(msg); tid = t; } Tid tid;} /** * Thrown when a `Tid` is missing, e.g. when $(LREF ownerTid) doesn't * find an owner thread. */class TidMissingException : Exception{ import std.exception : basicExceptionCtors; /// mixin basicExceptionCtors;} // Thread ID /** * An opaque type used to represent a logical thread. */struct Tid{private: this(MessageBox m) @safe pure nothrow @nogc { mbox = m; } MessageBox mbox; public: /** * Generate a convenient string for identifying this `Tid`. This is only * useful to see if `Tid`'s that are currently executing are the same or * different, e.g. for logging and debugging. It is potentially possible * that a `Tid` executed in the future will have the same `toString()` output * as another `Tid` that has already terminated. */ void toString(W)(ref W w) const { import std.format.write : formattedWrite; auto p = () @trusted { return cast(void*) mbox; }(); formattedWrite(w, "Tid(%x)", p); } } @safe unittest{ import std.conv : text; Tid tid; assert(text(tid) == "Tid(0)"); auto tid2 = thisTid; assert(text(tid2) != "Tid(0)"); auto tid3 = tid2; assert(text(tid2) == text(tid3));} // https://issues.dlang.org/show_bug.cgi?id=21512@system unittest{ import std.format : format; const(Tid) b = spawn(() {}); assert(format!"%s"(b)[0 .. 4] == "Tid(");} /** * Returns: The `Tid` of the caller's thread. */@property Tid thisTid() @safe{ // TODO: remove when concurrency is safe static auto trus() @trusted { if (thisInfo.ident != Tid.init) return thisInfo.ident; thisInfo.ident = Tid(new MessageBox); return thisInfo.ident; } return trus();} /** * Return the `Tid` of the thread which spawned the caller's thread. * * Throws: A `TidMissingException` exception if * there is no owner thread. */@property Tid ownerTid(){ import std.exception : enforce; enforce!TidMissingException(thisInfo.owner.mbox !is null, "Error: Thread has no owner thread."); return thisInfo.owner;} @system unittest{ import std.exception : assertThrown; static void fun() { string res = receiveOnly!string(); assert(res == "Main calling"); ownerTid.send("Child responding"); } assertThrown!TidMissingException(ownerTid); auto child = spawn(&fun); child.send("Main calling"); string res = receiveOnly!string(); assert(res == "Child responding");} // Thread Creation private template isSpawnable(F, T...){ template isParamsImplicitlyConvertible(F1, F2, int i = 0) { alias param1 = Parameters!F1; alias param2 = Parameters!F2; static if (param1.length != param2.length) enum isParamsImplicitlyConvertible = false; else static if (param1.length == i) enum isParamsImplicitlyConvertible = true; else static if (is(param2[i] : param1[i])) enum isParamsImplicitlyConvertible = isParamsImplicitlyConvertible!(F1, F2, i + 1); else enum isParamsImplicitlyConvertible = false; } enum isSpawnable = isCallable!F && is(ReturnType!F : void) && isParamsImplicitlyConvertible!(F, void function(T)) && (isFunctionPointer!F || !hasUnsharedAliasing!F);} /** * Starts `fn(args)` in a new logical thread. * * Executes the supplied function in a new logical thread represented byLines 1–500 of 2925. Load more
/** * $(SCRIPT inhibitQuickIndex = 1;) * $(DIVC quickindex, * $(BOOKTABLE, * $(TR $(TH Category) $(TH Symbols)) * $(TR $(TD Tid) $(TD * $(MYREF join) * $(MYREF locate) * $(MYREF ownerTid) * $(MYREF register) * $(MYREF spawn) * $(MYREF spawnLinked) * $(MYREF thisTid) * $(MYREF Tid) * $(MYREF TidMissingException) * $(MYREF unregister) * )) * $(TR $(TD Message passing) $(TD * $(MYREF prioritySend) * $(MYREF receive) * $(MYREF receiveOnly) * $(MYREF receiveTimeout) * $(MYREF send) * $(MYREF setMaxMailboxSize) * )) * $(TR $(TD Message-related types) $(TD * $(MYREF LinkTerminated) * $(MYREF MailboxFull) * $(MYREF MessageMismatch) * $(MYREF OnCrowding) * $(MYREF OwnerTerminated) * $(MYREF PriorityMessageException) * )) * $(TR $(TD Scheduler) $(TD * $(MYREF FiberScheduler) * $(MYREF Generator) * $(MYREF Scheduler) * $(MYREF scheduler) * $(MYREF ThreadInfo) * $(MYREF ThreadScheduler) * $(MYREF yield) * )) * $(TR $(TD Misc) $(TD * $(MYREF initOnce) * )) * )) * * This is a low-level messaging API upon which more structured or restrictive * APIs may be built. The general idea is that every messageable entity is * represented by a common handle type called a `Tid`, which allows messages to * be sent to logical threads that are executing in both the current process * and in external processes using the same interface. This is an important * aspect of scalability because it allows the components of a program to be * spread across available resources with few to no changes to the actual * implementation. * * A logical thread is an execution context that has its own stack and which * runs asynchronously to other logical threads. These may be preemptively * scheduled kernel threads, $(MREF_ALTTEXT fibers, core, thread, fiber) * (cooperative user-space threads), or some other concept with similar behavior. * * The type of concurrency used when logical threads are created is determined * by the $(LREF Scheduler) selected at initialization time. The default behavior is * currently to create a new kernel thread per call to spawn, but other * schedulers are available that multiplex fibers across the main thread or * use some combination of the two approaches. * * Copyright: Copyright Sean Kelly 2009 - 2014. * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. * Authors: Sean Kelly, Alex Rønne Petersen, Martin Nowak * Source: $(PHOBOSSRC std/concurrency.d) */ /* Copyright Sean Kelly 2009 - 2014. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) */ module std.concurrency; public import std.variant; import core.atomic; import core.sync.condition; import core.sync.mutex; import core.thread; import std.range.primitives; import std.range.interfaces : InputRange; import std.traits; /// @system unittest { __gshared string received; static void spawnedFunc(Tid ownerTid) { import std.conv : text; // Receive a message from the owner thread. receive((int i){ received = text("Received the number ", i); // Send a message back to the owner thread // indicating success. send(ownerTid, true); }); } // Start spawnedFunc in a new thread. auto childTid = spawn(&spawnedFunc, thisTid); // Send the number 42 to this new thread. send(childTid, 42); // Receive the result code. auto wasSuccessful = receiveOnly!(bool); assert(wasSuccessful); assert(received == "Received the number 42"); } private { bool hasLocalAliasing(Types...)() { import std.typecons : Rebindable; // Works around "statement is not reachable" bool doesIt = false; static foreach (T; Types) { static if (is(T == Tid)) { /* Allowed */ } else static if (is(T : Rebindable!R, R)) doesIt |= hasLocalAliasing!R; else static if (is(T == struct)) doesIt |= hasLocalAliasing!(typeof(T.tupleof)); else doesIt |= std.traits.hasUnsharedAliasing!(T); } return doesIt; } @safe unittest { static struct Container { Tid t; } static assert(!hasLocalAliasing!(Tid, Container, int)); } // https://issues.dlang.org/show_bug.cgi?id=20097 @safe unittest { import std.datetime.systime : SysTime; static struct Container { SysTime time; } static assert(!hasLocalAliasing!(SysTime, Container)); } enum MsgType { standard, priority, linkDead, } struct Message { MsgType type; Variant data; this(T...)(MsgType t, T vals) if (T.length > 0) { static if (T.length == 1) { type = t; data = vals[0]; } else { import std.typecons : Tuple; type = t; data = Tuple!(T)(vals); } } @property auto convertsTo(T...)() { static if (T.length == 1) { return is(T[0] == Variant) || data.convertsTo!(T); } else { import std.typecons : Tuple; return data.convertsTo!(Tuple!(T)); } } @property auto get(T...)() { static if (T.length == 1) { static if (is(T[0] == Variant)) return data; else return data.get!(T); } else { import std.typecons : Tuple; return data.get!(Tuple!(T)); } } auto map(Op)(Op op) { alias Args = Parameters!(Op); static if (Args.length == 1) { static if (is(Args[0] == Variant)) return op(data); else return op(data.get!(Args)); } else { import std.typecons : Tuple; return op(data.get!(Tuple!(Args)).expand); } } } void checkops(T...)(T ops) { import std.format : format; foreach (i, t1; T) { static assert(isFunctionPointer!t1 || isDelegate!t1, format!"T %d is not a function pointer or delegates"(i)); alias a1 = Parameters!(t1); alias r1 = ReturnType!(t1); static if (i < T.length - 1 && is(r1 == void)) { static assert(a1.length != 1 || !is(a1[0] == Variant), "function with arguments " ~ a1.stringof ~ " occludes successive function"); foreach (t2; T[i + 1 .. $]) { alias a2 = Parameters!(t2); static assert(!is(a1 == a2), "function with arguments " ~ a1.stringof ~ " occludes successive function"); } } } } @property ref ThreadInfo thisInfo() nothrow { import core.atomic : atomicLoad; auto localScheduler = atomicLoad(scheduler); if (localScheduler is null) return ThreadInfo.thisInfo; return localScheduler.thisInfo; } } static ~this() { thisInfo.cleanup(); } // Exceptions /** * Thrown on calls to $(LREF receiveOnly) if a message other than the type * the receiving thread expected is sent. */ class MessageMismatch : Exception { /// this(string msg = "Unexpected message type") @safe pure nothrow @nogc { super(msg); } } /** * Thrown on calls to $(LREF receive) if the thread that spawned the receiving * thread has terminated and no more messages exist. */ class OwnerTerminated : Exception { /// this(Tid t, string msg = "Owner terminated") @safe pure nothrow @nogc { super(msg); tid = t; } Tid tid; } /** * Thrown if a linked thread has terminated. */ class LinkTerminated : Exception { /// this(Tid t, string msg = "Link terminated") @safe pure nothrow @nogc { super(msg); tid = t; } Tid tid; } /** * Thrown if a message was sent to a thread via * $(REF prioritySend, std,concurrency) and the receiver does not have a handler * for a message of this type. */ class PriorityMessageException : Exception { /// this(Variant vals) { super("Priority message"); message = vals; } /** * The message that was sent. */ Variant message; } /** * Thrown on mailbox crowding if the mailbox is configured with * `OnCrowding.throwException`. */ class MailboxFull : Exception { /// this(Tid t, string msg = "Mailbox full") @safe pure nothrow @nogc { super(msg); tid = t; } Tid tid; } /** * Thrown when a `Tid` is missing, e.g. when $(LREF ownerTid) doesn't * find an owner thread. */ class TidMissingException : Exception { import std.exception : basicExceptionCtors; /// mixin basicExceptionCtors; } // Thread ID /** * An opaque type used to represent a logical thread. */ struct Tid { private: this(MessageBox m) @safe pure nothrow @nogc { mbox = m; } MessageBox mbox; public: /** * Generate a convenient string for identifying this `Tid`. This is only * useful to see if `Tid`'s that are currently executing are the same or * different, e.g. for logging and debugging. It is potentially possible * that a `Tid` executed in the future will have the same `toString()` output * as another `Tid` that has already terminated. */ void toString(W)(ref W w) const { import std.format.write : formattedWrite; auto p = () @trusted { return cast(void*) mbox; }(); formattedWrite(w, "Tid(%x)", p); } } @safe unittest { import std.conv : text; Tid tid; assert(text(tid) == "Tid(0)"); auto tid2 = thisTid; assert(text(tid2) != "Tid(0)"); auto tid3 = tid2; assert(text(tid2) == text(tid3)); } // https://issues.dlang.org/show_bug.cgi?id=21512 @system unittest { import std.format : format; const(Tid) b = spawn(() {}); assert(format!"%s"(b)[0 .. 4] == "Tid("); } /** * Returns: The `Tid` of the caller's thread. */ @property Tid thisTid() @safe { // TODO: remove when concurrency is safe static auto trus() @trusted { if (thisInfo.ident != Tid.init) return thisInfo.ident; thisInfo.ident = Tid(new MessageBox); return thisInfo.ident; } return trus(); } /** * Return the `Tid` of the thread which spawned the caller's thread. * * Throws: A `TidMissingException` exception if * there is no owner thread. */ @property Tid ownerTid() { import std.exception : enforce; enforce!TidMissingException(thisInfo.owner.mbox !is null, "Error: Thread has no owner thread."); return thisInfo.owner; } @system unittest { import std.exception : assertThrown; static void fun() { string res = receiveOnly!string(); assert(res == "Main calling"); ownerTid.send("Child responding"); } assertThrown!TidMissingException(ownerTid); auto child = spawn(&fun); child.send("Main calling"); string res = receiveOnly!string(); assert(res == "Child responding"); } // Thread Creation private template isSpawnable(F, T...) { template isParamsImplicitlyConvertible(F1, F2, int i = 0) { alias param1 = Parameters!F1; alias param2 = Parameters!F2; static if (param1.length != param2.length) enum isParamsImplicitlyConvertible = false; else static if (param1.length == i) enum isParamsImplicitlyConvertible = true; else static if (is(param2[i] : param1[i])) enum isParamsImplicitlyConvertible = isParamsImplicitlyConvertible!(F1, F2, i + 1); else enum isParamsImplicitlyConvertible = false; } enum isSpawnable = isCallable!F && is(ReturnType!F : void) && isParamsImplicitlyConvertible!(F, void function(T)) && (isFunctionPointer!F || !hasUnsharedAliasing!F); } /** * Starts `fn(args)` in a new logical thread. * * Executes the supplied function in a new logical thread represented by