aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/concurrency.d
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2019-06-18 20:42:10 +0200
committerIain Buclaw <ibuclaw@gdcproject.org>2021-11-30 16:53:28 +0100
commit5fee5ec362f7a243f459e6378fd49dfc89dc9fb5 (patch)
tree61d1bdbca854a903c0860406f457f06b2040be7a /libphobos/src/std/concurrency.d
parentb3f60112edcb85b459e60f66c44a55138b1cef49 (diff)
downloadgcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.zip
gcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.tar.gz
gcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.tar.bz2
d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)
The D front-end is now itself written in D, in order to build GDC, you will need a working GDC compiler (GCC version 9.1 or later). GCC changes: - Add support for bootstrapping the D front-end. These add the required components in order to have a D front-end written in D itself. Because the compiler front-end only depends on the core runtime modules, only libdruntime is built for the bootstrap stages. D front-end changes: - Import dmd v2.098.0-beta.1. Druntime changes: - Import druntime v2.098.0-beta.1. Phobos changes: - Import phobos v2.098.0-beta.1. The jump from v2.076.1 to v2.098.0 covers nearly 4 years worth of development on the D programming language and run-time libraries. ChangeLog: * Makefile.def: Add bootstrap to libbacktrace, libphobos, zlib, and libatomic. * Makefile.in: Regenerate. * Makefile.tpl (POSTSTAGE1_HOST_EXPORTS): Fix command for GDC. (STAGE1_CONFIGURE_FLAGS): Add --with-libphobos-druntime-only if target-libphobos-bootstrap. (STAGE2_CONFIGURE_FLAGS): Likewise. * configure: Regenerate. * configure.ac: Add support for bootstrapping D front-end. config/ChangeLog: * acx.m4 (ACX_PROG_GDC): New m4 function. gcc/ChangeLog: * Makefile.in (GDC): New variable. (GDCFLAGS): New variable. * configure: Regenerate. * configure.ac: Add call to ACX_PROG_GDC. Substitute GDCFLAGS. gcc/d/ChangeLog: * dmd/MERGE: Merge upstream dmd b8384668f. * Make-lang.in (d-warn): Use strict warnings. (DMD_WARN_CXXFLAGS): Remove. (DMD_COMPILE): Remove. (CHECKING_DFLAGS): Define. (WARN_DFLAGS): Define. (ALL_DFLAGS): Define. (DCOMPILE.base): Define. (DCOMPILE): Define. (DPOSTCOMPILE): Define. (DLINKER): Define. (DLLINKER): Define. (D_FRONTEND_OBJS): Add new dmd front-end objects. (D_GENERATED_SRCS): Remove. (D_GENERATED_OBJS): Remove. (D_ALL_OBJS): Remove D_GENERATED_OBJS. (d21$(exeext)): Build using DLLINKER and -static-libphobos. (d.tags): Remove dmd/*.c and dmd/root/*.c. (d.mostlyclean): Remove D_GENERATED_SRCS, d/idgen$(build_exeext), d/impcnvgen$(build_exeext). (D_INCLUDES): Include $(srcdir)/d/dmd/res. (CFLAGS-d/id.o): Remove. (CFLAGS-d/impcnvtab.o): Remove. (d/%.o): Build using DCOMPILE and DPOSTCOMPILE. Update dependencies from d/dmd/%.c to d/dmd/%.d. (d/idgen$(build_exeext)): Remove. (d/impcnvgen$(build_exeext)): Remove. (d/id.c): Remove. (d/id.h): Remove. (d/impcnvtab.c): Remove. (d/%.dmdgen.o): Remove. (D_SYSTEM_H): Remove. (d/idgen.dmdgen.o): Remove. (d/impcnvgen.dmdgen.o): Remove. * config-lang.in (boot_language): New variable. * d-attribs.cc: Include dmd/expression.h. * d-builtins.cc: Include d-frontend.h. (build_frontend_type): Update for new front-end interface. (d_eval_constant_expression): Likewise. (d_build_builtins_module): Likewise. (maybe_set_builtin_1): Likewise. (d_build_d_type_nodes): Likewise. * d-codegen.cc (d_decl_context): Likewise. (declaration_reference_p): Likewise. (declaration_type): Likewise. (parameter_reference_p): Likewise. (parameter_type): Likewise. (get_array_length): Likewise. (build_delegate_cst): Likewise. (build_typeof_null_value): Likewise. (identity_compare_p): Likewise. (lower_struct_comparison): Likewise. (build_filename_from_loc): Likewise. (build_assert_call): Remove LIBCALL_SWITCH_ERROR. (build_bounds_index_condition): Call LIBCALL_ARRAYBOUNDS_INDEXP on bounds error. (build_bounds_slice_condition): Call LIBCALL_ARRAYBOUNDS_SLICEP on bounds error. (array_bounds_check): Update for new front-end interface. (checkaction_trap_p): Handle CHECKACTION_context. (get_function_type): Update for new front-end interface. (d_build_call): Likewise. * d-compiler.cc: Remove include of dmd/scope.h. (Compiler::genCmain): Remove. (Compiler::paintAsType): Update for new front-end interface. (Compiler::onParseModule): Likewise. * d-convert.cc (convert_expr): Remove call to LIBCALL_ARRAYCAST. (convert_for_rvalue): Update for new front-end interface. (convert_for_assignment): Likewise. (convert_for_condition): Likewise. (d_array_convert): Likewise. * d-diagnostic.cc (error): Remove. (errorSupplemental): Remove. (warning): Remove. (warningSupplemental): Remove. (deprecation): Remove. (deprecationSupplemental): Remove. (message): Remove. (vtip): New. * d-frontend.cc (global): Remove. (Global::_init): Remove. (Global::startGagging): Remove. (Global::endGagging): Remove. (Global::increaseErrorCount): Remove. (Loc::Loc): Remove. (Loc::toChars): Remove. (Loc::equals): Remove. (isBuiltin): Update for new front-end interface. (eval_builtin): Likewise. (getTypeInfoType): Likewise. (inlineCopy): Remove. * d-incpath.cc: Include d-frontend.h. (add_globalpaths): Call d_gc_malloc to allocate Strings. (add_filepaths): Likewise. * d-lang.cc: Include dmd/id.h, dmd/root/file.h, d-frontend.h. Remove include of dmd/mars.h, id.h. (entrypoint_module): Remove. (entrypoint_root_module): Remove. (deps_write_string): Update for new front-end interface. (deps_write): Likewise. (d_init_options): Call rt_init. Remove setting global params that are default initialized by the front-end. (d_handle_option): Handle OPT_fcheckaction_, OPT_fdump_c___spec_, OPT_fdump_c___spec_verbose, OPT_fextern_std_, OPT_fpreview, OPT_revert, OPT_fsave_mixins_, and OPT_ftransition. (d_post_options): Propagate dip1021 and dip1000 preview flags to dip25, and flag_diagnostics_show_caret to printErrorContext. (d_add_entrypoint_module): Remove. (d_parse_file): Update for new front-end interface. (d_type_promotes_to): Likewise. (d_types_compatible_p): Likewise. * d-longdouble.cc (CTFloat::zero): Remove. (CTFloat::one): Remove. (CTFloat::minusone): Remove. (CTFloat::half): Remove. * d-system.h (POSIX): Remove. (realpath): Remove. (isalpha): Remove. (isalnum): Remove. (isdigit): Remove. (islower): Remove. (isprint): Remove. (isspace): Remove. (isupper): Remove. (isxdigit): Remove. (tolower): Remove. (_mkdir): Remove. (INT32_MAX): Remove. (INT32_MIN): Remove. (INT64_MIN): Remove. (UINT32_MAX): Remove. (UINT64_MAX): Remove. * d-target.cc: Include calls.h. (target): Remove. (define_float_constants): Remove initialization of snan. (Target::_init): Update for new front-end interface. (Target::isVectorTypeSupported): Likewise. (Target::isVectorOpSupported): Remove cases for unordered operators. (TargetCPP::typeMangle): Update for new front-end interface. (TargetCPP::parameterType): Likewise. (Target::systemLinkage): Likewise. (Target::isReturnOnStack): Likewise. (Target::isCalleeDestroyingArgs): Define. (Target::preferPassByRef): Define. * d-tree.h (d_add_entrypoint_module): Remove. * decl.cc (gcc_attribute_p): Update for new front-end interface. (apply_pragma_crt): Define. (DeclVisitor::visit(PragmaDeclaration *)): Handle pragmas crt_constructor and crt_destructor. (DeclVisitor::visit(TemplateDeclaration *)): Update for new front-end interface. (DeclVisitor::visit): Likewise. (DeclVisitor::finish_vtable): Likewise. (get_symbol_decl): Error if template has more than one nesting context. Update for new front-end interface. (make_thunk): Update for new front-end interface. (get_vtable_decl): Likewise. * expr.cc (ExprVisitor::visit): Likewise. (build_return_dtor): Likewise. * imports.cc (ImportVisitor::visit): Likewise. * intrinsics.cc: Include dmd/expression.h. Remove include of dmd/mangle.h. (maybe_set_intrinsic): Update for new front-end interface. * intrinsics.def (INTRINSIC_ROL): Update intrinsic signature. (INTRINSIC_ROR): Likewise. (INTRINSIC_ROR_TIARG): Likewise. (INTRINSIC_TOPREC): Likewise. (INTRINSIC_TOPRECL): Likewise. (INTRINSIC_TAN): Update intrinsic module and signature. (INTRINSIC_ISNAN): Likewise. (INTRINSIC_ISFINITE): Likewise. (INTRINSIC_COPYSIGN): Define intrinsic. (INTRINSIC_COPYSIGNI): Define intrinsic. (INTRINSIC_EXP): Update intrinsic module. (INTRINSIC_EXPM1): Likewise. (INTRINSIC_EXP2): Likewise. (INTRINSIC_LOG): Likewise. (INTRINSIC_LOG2): Likewise. (INTRINSIC_LOG10): Likewise. (INTRINSIC_POW): Likewise. (INTRINSIC_ROUND): Likewise. (INTRINSIC_FLOORF): Likewise. (INTRINSIC_FLOOR): Likewise. (INTRINSIC_FLOORL): Likewise. (INTRINSIC_CEILF): Likewise. (INTRINSIC_CEIL): Likewise. (INTRINSIC_CEILL): Likewise. (INTRINSIC_TRUNC): Likewise. (INTRINSIC_FMIN): Likewise. (INTRINSIC_FMAX): Likewise. (INTRINSIC_FMA): Likewise. (INTRINSIC_VA_ARG): Update intrinsic signature. (INTRINSIC_VASTART): Likewise. * lang.opt (fcheck=): Add alternate aliases for contract switches. (fcheckaction=): New option. (check_action): New Enum and EnumValue entries. (fdump-c++-spec-verbose): New option. (fdump-c++-spec=): New option. (fextern-std=): New option. (extern_stdcpp): New Enum and EnumValue entries (fpreview=): New options. (frevert=): New options. (fsave-mixins): New option. (ftransition=): Update options. * modules.cc (get_internal_fn): Replace Prot with Visibility. (build_internal_fn): Likewise. (build_dso_cdtor_fn): Likewise. (build_module_tree): Remove check for __entrypoint module. * runtime.def (P5): Define. (ARRAYBOUNDS_SLICEP): Define. (ARRAYBOUNDS_INDEXP): Define. (NEWTHROW): Define. (ADCMP2): Remove. (ARRAYCAST): Remove. (SWITCH_STRING): Remove. (SWITCH_USTRING): Remove. (SWITCH_DSTRING): Remove. (SWITCH_ERROR): Remove. * toir.cc (IRVisitor::visit): Update for new front-end interface. (IRVisitor::check_previous_goto): Remove checks for case and default statements. (IRVisitor::visit(SwitchStatement *)): Remove handling of string switch conditions. * typeinfo.cc: Include d-frontend.h. (get_typeinfo_kind): Update for new front-end interface. (make_frontend_typeinfo): Likewise. (TypeInfoVisitor::visit): Likewise. (builtin_typeinfo_p): Likewise. (get_typeinfo_decl): Likewise. (build_typeinfo): Likewise. * types.cc (valist_array_p): Likewise. (make_array_type): Likewise. (merge_aggregate_types): Likewise. (TypeVisitor::visit(TypeBasic *)): Likewise. (TypeVisitor::visit(TypeFunction *)): Likewise. (TypeVisitor::visit(TypeStruct *)): Update comment. * verstr.h: Removed. * d-frontend.h: New file. gcc/po/ChangeLog: * EXCLUDES: Remove d/dmd sources from list. gcc/testsuite/ChangeLog: * gdc.dg/Wcastresult2.d: Update test. * gdc.dg/asm1.d: Likewise. * gdc.dg/asm2.d: Likewise. * gdc.dg/asm3.d: Likewise. * gdc.dg/gdc282.d: Likewise. * gdc.dg/imports/gdc170.d: Likewise. * gdc.dg/intrinsics.d: Likewise. * gdc.dg/pr101672.d: Likewise. * gdc.dg/pr90650a.d: Likewise. * gdc.dg/pr90650b.d: Likewise. * gdc.dg/pr94777a.d: Likewise. * gdc.dg/pr95250.d: Likewise. * gdc.dg/pr96869.d: Likewise. * gdc.dg/pr98277.d: Likewise. * gdc.dg/pr98457.d: Likewise. * gdc.dg/simd1.d: Likewise. * gdc.dg/simd2a.d: Likewise. * gdc.dg/simd2b.d: Likewise. * gdc.dg/simd2c.d: Likewise. * gdc.dg/simd2d.d: Likewise. * gdc.dg/simd2e.d: Likewise. * gdc.dg/simd2f.d: Likewise. * gdc.dg/simd2g.d: Likewise. * gdc.dg/simd2h.d: Likewise. * gdc.dg/simd2i.d: Likewise. * gdc.dg/simd2j.d: Likewise. * gdc.dg/simd7951.d: Likewise. * gdc.dg/torture/gdc309.d: Likewise. * gdc.dg/torture/pr94424.d: Likewise. * gdc.dg/torture/pr94777b.d: Likewise. * lib/gdc-utils.exp (gdc-convert-args): Handle new compiler options. (gdc-convert-test): Handle CXXFLAGS, EXTRA_OBJC_SOURCES, and ARG_SETS test directives. (gdc-do-test): Only import modules in the test run directory. * gdc.dg/pr94777c.d: New test. * gdc.dg/pr96156b.d: New test. * gdc.dg/pr96157c.d: New test. * gdc.dg/simd_ctfe.d: New test. * gdc.dg/torture/simd17344.d: New test. * gdc.dg/torture/simd20052.d: New test. * gdc.dg/torture/simd6.d: New test. * gdc.dg/torture/simd7.d: New test. libphobos/ChangeLog: * libdruntime/MERGE: Merge upstream druntime e6caaab9. * libdruntime/Makefile.am (D_EXTRA_FLAGS): Build libdruntime with -fpreview=dip1000, -fpreview=fieldwise, and -fpreview=dtorfields. (ALL_DRUNTIME_SOURCES): Add DRUNTIME_DSOURCES_STDCXX. (DRUNTIME_DSOURCES): Update list of C binding modules. (DRUNTIME_DSOURCES_STDCXX): Likewise. (DRUNTIME_DSOURCES_LINUX): Likewise. (DRUNTIME_DSOURCES_OPENBSD): Likewise. (DRUNTIME_DISOURCES): Remove __entrypoint.di. * libdruntime/Makefile.in: Regenerated. * libdruntime/__entrypoint.di: Removed. * libdruntime/gcc/deh.d (_d_isbaseof): Update signature. (_d_createTrace): Likewise. (__gdc_begin_catch): Remove reference to the exception. (_d_throw): Increment reference count of thrown object before unwind. (__gdc_personality): Chain exceptions with Throwable.chainTogether. * libdruntime/gcc/emutls.d: Update imports. * libdruntime/gcc/sections/elf.d: Update imports. (DSO.moduleGroup): Update signature. * libdruntime/gcc/sections/macho.d: Update imports. (DSO.moduleGroup): Update signature. * libdruntime/gcc/sections/pecoff.d: Update imports. (DSO.moduleGroup): Update signature. * src/MERGE: Merge upstream phobos 5ab9ad256. * src/Makefile.am (D_EXTRA_DFLAGS): Add -fpreview=dip1000 and -fpreview=dtorfields flags. (PHOBOS_DSOURCES): Update list of std modules. * src/Makefile.in: Regenerate. * testsuite/lib/libphobos.exp (libphobos-dg-test): Handle assembly compile types. (dg-test): Override. (additional_prunes): Define. (libphobos-dg-prune): Filter any additional_prunes set by tests. * testsuite/libphobos.aa/test_aa.d: Update test. * testsuite/libphobos.druntime/druntime.exp (version_flags): Add -fversion=CoreUnittest. * testsuite/libphobos.druntime_shared/druntime_shared.exp (version_flags): Add -fversion=CoreUnittest -fversion=Shared. * testsuite/libphobos.exceptions/unknown_gc.d: Update test. * testsuite/libphobos.hash/test_hash.d: Update test. * testsuite/libphobos.phobos/phobos.exp (version_flags): Add -fversion=StdUnittest * testsuite/libphobos.phobos_shared/phobos_shared.exp (version_flags): Likewise. * testsuite/libphobos.shared/host.c: Update test. * testsuite/libphobos.shared/load.d: Update test. * testsuite/libphobos.shared/load_13414.d: Update test. * testsuite/libphobos.thread/fiber_guard_page.d: Update test. * testsuite/libphobos.thread/tlsgc_sections.d: Update test. * testsuite/testsuite_flags.in: Add -fpreview=dip1000 to --gdcflags. * testsuite/libphobos.shared/link_mod_collision.d: Removed. * testsuite/libphobos.shared/load_mod_collision.d: Removed. * testsuite/libphobos.betterc/betterc.exp: New test. * testsuite/libphobos.config/config.exp: New test. * testsuite/libphobos.gc/gc.exp: New test. * testsuite/libphobos.imports/imports.exp: New test. * testsuite/libphobos.lifetime/lifetime.exp: New test. * testsuite/libphobos.unittest/unittest.exp: New test.
Diffstat (limited to 'libphobos/src/std/concurrency.d')
-rw-r--r--libphobos/src/std/concurrency.d695
1 files changed, 495 insertions, 200 deletions
diff --git a/libphobos/src/std/concurrency.d b/libphobos/src/std/concurrency.d
index cf77911..d101ce4 100644
--- a/libphobos/src/std/concurrency.d
+++ b/libphobos/src/std/concurrency.d
@@ -1,4 +1,49 @@
/**
+ * $(SCRIPT inhibitQuickIndex = 1;)
+ * $(DIVC quickindex,
+ * $(BOOKTABLE,
+ * $(TR $(TH Category) $(TH Symbols))
+ * $(TR $(TD Tid) $(TD
+ * $(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
@@ -22,7 +67,7 @@
* 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)
+ * Source: $(PHOBOSSRC std/concurrency.d)
*/
/* Copyright Sean Kelly 2009 - 2014.
* Distributed under the Boost Software License, Version 1.0.
@@ -72,13 +117,38 @@ import std.traits;
private
{
- template hasLocalAliasing(T...)
+ bool hasLocalAliasing(Types...)()
{
- static if (!T.length)
- enum hasLocalAliasing = false;
- else
- enum hasLocalAliasing = (std.traits.hasUnsharedAliasing!(T[0]) && !is(T[0] == Tid)) ||
- std.concurrency.hasLocalAliasing!(T[1 .. $]);
+ 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
@@ -159,9 +229,12 @@ private
void checkops(T...)(T ops)
{
+ import std.format : format;
+
foreach (i, t1; T)
{
- static assert(isFunctionPointer!t1 || isDelegate!t1);
+ 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);
@@ -173,7 +246,6 @@ private
foreach (t2; T[i + 1 .. $])
{
- static assert(isFunctionPointer!t2 || isDelegate!t2);
alias a2 = Parameters!(t2);
static assert(!is(a1 == a2),
@@ -199,7 +271,7 @@ static ~this()
// Exceptions
/**
- * Thrown on calls to $(D receiveOnly) if a message other than the type
+ * Thrown on calls to `receiveOnly` if a message other than the type
* the receiving thread expected is sent.
*/
class MessageMismatch : Exception
@@ -212,7 +284,7 @@ class MessageMismatch : Exception
}
/**
- * Thrown on calls to $(D receive) if the thread that spawned the receiving
+ * Thrown on calls to `receive` if the thread that spawned the receiving
* thread has terminated and no more messages exist.
*/
class OwnerTerminated : Exception
@@ -264,7 +336,7 @@ class PriorityMessageException : Exception
/**
* Thrown on mailbox crowding if the mailbox is configured with
- * $(D OnCrowding.throwException).
+ * `OnCrowding.throwException`.
*/
class MailboxFull : Exception
{
@@ -279,7 +351,7 @@ class MailboxFull : Exception
}
/**
- * Thrown when a Tid is missing, e.g. when $(D ownerTid) doesn't
+ * Thrown when a Tid is missing, e.g. when `ownerTid` doesn't
* find an owner thread.
*/
class TidMissingException : Exception
@@ -315,17 +387,17 @@ public:
* that a Tid executed in the future will have the same toString() output
* as another Tid that has already terminated.
*/
- void toString(scope void delegate(const(char)[]) sink)
+ void toString(W)(ref W w) const
{
- import std.format : formattedWrite;
- formattedWrite(sink, "Tid(%x)", cast(void*) mbox);
+ import std.format.write : formattedWrite;
+ auto p = () @trusted { return cast(void*) mbox; }();
+ formattedWrite(w, "Tid(%x)", p);
}
}
-@system unittest
+@safe unittest
{
- // text!Tid is @system
import std.conv : text;
Tid tid;
assert(text(tid) == "Tid(0)");
@@ -335,6 +407,15 @@ public:
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 $(LREF Tid) of the caller's thread.
*/
@@ -355,7 +436,7 @@ public:
/**
* Return the Tid of the thread which spawned the caller's thread.
*
- * Throws: A $(D TidMissingException) exception if
+ * Throws: A `TidMissingException` exception if
* there is no owner thread.
*/
@property Tid ownerTid()
@@ -412,10 +493,10 @@ private template isSpawnable(F, T...)
* Starts fn(args) in a new logical thread.
*
* Executes the supplied function in a new logical thread represented by
- * $(D Tid). The calling thread is designated as the owner of the new thread.
- * When the owner thread terminates an $(D OwnerTerminated) message will be
- * sent to the new thread, causing an $(D OwnerTerminated) exception to be
- * thrown on $(D receive()).
+ * `Tid`. The calling thread is designated as the owner of the new thread.
+ * When the owner thread terminates an `OwnerTerminated` message will be
+ * sent to the new thread, causing an `OwnerTerminated` exception to be
+ * thrown on `receive()`.
*
* Params:
* fn = The function to execute.
@@ -425,46 +506,69 @@ private template isSpawnable(F, T...)
* A Tid representing the new logical thread.
*
* Notes:
- * $(D args) must not have unshared aliasing. In other words, all arguments
- * to $(D fn) must either be $(D shared) or $(D immutable) or have no
+ * `args` must not have unshared aliasing. In other words, all arguments
+ * to `fn` must either be `shared` or `immutable` or have no
* pointer indirection. This is necessary for enforcing isolation among
* threads.
*
- * Example:
- * ---
- * import std.stdio, std.concurrency;
- *
- * void f1(string str)
- * {
- * writeln(str);
- * }
- *
- * void f2(char[] str)
- * {
- * writeln(str);
- * }
- *
- * void main()
- * {
- * auto str = "Hello, world";
- *
- * // Works: string is immutable.
- * auto tid1 = spawn(&f1, str);
- *
- * // Fails: char[] has mutable aliasing.
- * auto tid2 = spawn(&f2, str.dup);
- *
- * // New thread with anonymous function
- * spawn({ writeln("This is so great!"); });
- * }
- * ---
- */
-Tid spawn(F, T...)(F fn, T args) if (isSpawnable!(F, T))
+ * Similarly, if `fn` is a delegate, it must not have unshared aliases, meaning
+ * `fn` must be either `shared` or `immutable`. */
+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 _spawn(false, fn, args);
}
+///
+@system unittest
+{
+ static void f(string msg)
+ {
+ assert(msg == "Hello World");
+ }
+
+ auto tid = spawn(&f, "Hello World");
+}
+
+/// Fails: char[] has mutable aliasing.
+@system unittest
+{
+ string msg = "Hello, World!";
+
+ static void f1(string msg) {}
+ static assert(!__traits(compiles, spawn(&f1, msg.dup)));
+ static assert( __traits(compiles, spawn(&f1, msg.idup)));
+
+ static void f2(char[] msg) {}
+ static assert(!__traits(compiles, spawn(&f2, msg.dup)));
+ static assert(!__traits(compiles, spawn(&f2, msg.idup)));
+}
+
+/// New thread with anonymous function
+@system unittest
+{
+ spawn({
+ ownerTid.send("This is so great!");
+ });
+ assert(receiveOnly!string == "This is so great!");
+}
+
+@system unittest
+{
+ import core.thread : thread_joinAll;
+
+ __gshared string receivedMessage;
+ static void f1(string msg)
+ {
+ receivedMessage = msg;
+ }
+
+ auto tid1 = spawn(&f1, "Hello World");
+ thread_joinAll;
+ assert(receivedMessage == "Hello World");
+}
+
/**
* Starts fn(args) in a logical thread and will receive a LinkTerminated
* message when the operation terminates.
@@ -484,7 +588,8 @@ Tid spawn(F, T...)(F fn, T args) if (isSpawnable!(F, T))
* Returns:
* A Tid representing the new thread.
*/
-Tid spawnLinked(F, T...)(F fn, T args) if (isSpawnable!(F, T))
+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 _spawn(true, fn, args);
@@ -493,7 +598,8 @@ Tid spawnLinked(F, T...)(F fn, T args) if (isSpawnable!(F, T))
/*
*
*/
-private Tid _spawn(F, T...)(bool linked, F fn, T args) if (isSpawnable!(F, T))
+private Tid _spawn(F, T...)(bool linked, F fn, T args)
+if (isSpawnable!(F, T))
{
// TODO: MessageList and &exec should be shared.
auto spawnTid = Tid(new MessageBox);
@@ -568,9 +674,10 @@ private Tid _spawn(F, T...)(bool linked, F fn, T args) if (isSpawnable!(F, T))
* Places the values as a message at the back of tid's message queue.
*
* Sends the supplied value to the thread represented by tid. As with
- * $(REF spawn, std,concurrency), $(D T) must not have unshared aliasing.
+ * $(REF spawn, std,concurrency), `T` must not have unshared aliasing.
*/
void send(T...)(Tid tid, T vals)
+in (tid.mbox !is null)
{
static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed.");
_send(tid, vals);
@@ -579,11 +686,12 @@ void send(T...)(Tid tid, T vals)
/**
* Places the values as a message on the front of tid's message queue.
*
- * Send a message to $(D tid) but place it at the front of $(D tid)'s message
+ * Send a message to `tid` but place it at the front of `tid`'s message
* queue instead of at the back. This function is typically used for
* out-of-band communication, to signal exceptional conditions, etc.
*/
void prioritySend(T...)(Tid tid, T vals)
+in (tid.mbox !is null)
{
static assert(!hasLocalAliasing!(T), "Aliases to mutable thread-local data not allowed.");
_send(MsgType.priority, tid, vals);
@@ -593,6 +701,7 @@ void prioritySend(T...)(Tid tid, T vals)
* ditto
*/
private void _send(T...)(Tid tid, T vals)
+in (tid.mbox !is null)
{
_send(MsgType.standard, tid, vals);
}
@@ -602,6 +711,7 @@ private void _send(T...)(Tid tid, T vals)
* both Tid.send() and .send().
*/
private void _send(T...)(MsgType type, Tid tid, T vals)
+in (tid.mbox !is null)
{
auto msg = Message(type, vals);
tid.mbox.put(msg);
@@ -615,32 +725,16 @@ private void _send(T...)(MsgType type, Tid tid, T vals)
* a message against a set of delegates and executing the first match found.
*
* If a delegate that accepts a $(REF Variant, std,variant) is included as
- * the last argument to $(D receive), it will match any message that was not
+ * the last argument to `receive`, it will match any message that was not
* matched by an earlier delegate. If more than one argument is sent,
- * the $(D Variant) will contain a $(REF Tuple, std,typecons) of all values
+ * the `Variant` will contain a $(REF Tuple, std,typecons) of all values
* sent.
*
- * Example:
- * ---
- * import std.stdio;
- * import std.variant;
- * import std.concurrency;
- *
- * void spawnedFunction()
- * {
- * receive(
- * (int i) { writeln("Received an int."); },
- * (float f) { writeln("Received a float."); },
- * (Variant v) { writeln("Received some other type."); }
- * );
- * }
+ * Params:
+ * ops = Variadic list of function pointers and delegates. Entries
+ * in this list must not occlude later entries.
*
- * void main()
- * {
- * auto tid = spawn(&spawnedFunction);
- * send(tid, 42);
- * }
- * ---
+ * Throws: $(LREF OwnerTerminated) when the sending thread was terminated.
*/
void receive(T...)( T ops )
in
@@ -649,13 +743,45 @@ in
"Cannot receive a message until a thread was spawned "
~ "or thisTid was passed to a running thread.");
}
-body
+do
{
checkops( ops );
thisInfo.ident.mbox.get( ops );
}
+///
+@system unittest
+{
+ import std.variant : Variant;
+
+ auto process = ()
+ {
+ receive(
+ (int i) { ownerTid.send(1); },
+ (double f) { ownerTid.send(2); },
+ (Variant v) { ownerTid.send(3); }
+ );
+ };
+
+ {
+ auto tid = spawn(process);
+ send(tid, 42);
+ assert(receiveOnly!int == 1);
+ }
+
+ {
+ auto tid = spawn(process);
+ send(tid, 3.14);
+ assert(receiveOnly!int == 2);
+ }
+
+ {
+ auto tid = spawn(process);
+ send(tid, "something else");
+ assert(receiveOnly!int == 3);
+ }
+}
@safe unittest
{
@@ -677,7 +803,7 @@ body
}
// Make sure receive() works with free functions as well.
-version (unittest)
+version (StdUnittest)
{
private void receiveFunction(int x) {}
}
@@ -705,31 +831,17 @@ private template receiveOnlyRet(T...)
}
/**
- * Receives only messages with arguments of types $(D T).
+ * Receives only messages with arguments of the specified types.
*
- * Throws: $(D MessageMismatch) if a message of types other than $(D T)
- * is received.
+ * Params:
+ * T = Variadic list of types to be received.
*
- * Returns: The received message. If $(D T.length) is greater than one,
+ * Returns: The received message. If `T` has more than one entry,
* the message will be packed into a $(REF Tuple, std,typecons).
*
- * Example:
- * ---
- * import std.concurrency;
- *
- * void spawnedFunc()
- * {
- * auto msg = receiveOnly!(int, string)();
- * assert(msg[0] == 42);
- * assert(msg[1] == "42");
- * }
- *
- * void main()
- * {
- * auto tid = spawn(&spawnedFunc);
- * send(tid, 42, "42");
- * }
- * ---
+ * Throws: $(LREF MessageMismatch) if a message of types other than `T`
+ * is received,
+ * $(LREF OwnerTerminated) when the sending thread was terminated.
*/
receiveOnlyRet!(T) receiveOnly(T...)()
in
@@ -737,16 +849,27 @@ in
assert(thisInfo.ident.mbox !is null,
"Cannot receive a message until a thread was spawned or thisTid was passed to a running thread.");
}
-body
+do
{
import std.format : format;
+ import std.meta : allSatisfy;
import std.typecons : Tuple;
Tuple!(T) ret;
thisInfo.ident.mbox.get((T val) {
static if (T.length)
- ret.field = val;
+ {
+ static if (allSatisfy!(isAssignable, T))
+ {
+ ret.field = val;
+ }
+ else
+ {
+ import core.lifetime : emplace;
+ emplace(&ret, val);
+ }
+ }
},
(LinkTerminated e) { throw e; },
(OwnerTerminated e) { throw e; },
@@ -765,6 +888,42 @@ body
return ret;
}
+///
+@system unittest
+{
+ auto tid = spawn(
+ {
+ assert(receiveOnly!int == 42);
+ });
+ send(tid, 42);
+}
+
+///
+@system unittest
+{
+ auto tid = spawn(
+ {
+ assert(receiveOnly!string == "text");
+ });
+ send(tid, "text");
+}
+
+///
+@system unittest
+{
+ struct Record { string name; int age; }
+
+ auto tid = spawn(
+ {
+ auto msg = receiveOnly!(double, Record);
+ assert(msg[0] == 0.5);
+ assert(msg[1].name == "Alice");
+ assert(msg[1].age == 31);
+ });
+
+ send(tid, 0.5, Record("Alice", 31));
+}
+
@system unittest
{
static void t1(Tid mainTid)
@@ -786,14 +945,37 @@ body
assert(result == "Unexpected message type: expected 'string', got 'int'");
}
+// https://issues.dlang.org/show_bug.cgi?id=21663
+@safe unittest
+{
+ alias test = receiveOnly!(string, bool, bool);
+}
+
/**
- * Tries to receive but will give up if no matches arrive within duration.
- * Won't wait at all if provided $(REF Duration, core,time) is negative.
+ * Receives a message from another thread and gives up if no match
+ * arrives within a specified duration.
+ *
+ * Receive a message from another thread, or block until `duration` exceeds,
+ * if no messages of the specified types are available. This function works
+ * by pattern matching a message against a set of delegates and executing
+ * the first match found.
+ *
+ * If a delegate that accepts a $(REF Variant, std,variant) is included as
+ * the last argument, it will match any message that was not
+ * matched by an earlier delegate. If more than one argument is sent,
+ * the `Variant` will contain a $(REF Tuple, std,typecons) of all values
+ * sent.
+ *
+ * Params:
+ * duration = Duration, how long to wait. If `duration` is negative,
+ * won't wait at all.
+ * ops = Variadic list of function pointers and delegates. Entries
+ * in this list must not occlude later entries.
*
- * Same as $(D receive) except that rather than wait forever for a message,
- * it waits until either it receives a message or the given
- * $(REF Duration, core,time) has passed. It returns $(D true) if it received a
- * message and $(D false) if it timed out waiting for one.
+ * Returns: `true` if it received a message and `false` if it timed out waiting
+ * for one.
+ *
+ * Throws: $(LREF OwnerTerminated) when the sending thread was terminated.
*/
bool receiveTimeout(T...)(Duration duration, T ops)
in
@@ -801,7 +983,7 @@ in
assert(thisInfo.ident.mbox !is null,
"Cannot receive a message until a thread was spawned or thisTid was passed to a running thread.");
}
-body
+do
{
checkops(ops);
@@ -873,6 +1055,7 @@ private
* mailbox.
*/
void setMaxMailboxSize(Tid tid, size_t messages, OnCrowding doThis) @safe pure
+in (tid.mbox !is null)
{
final switch (doThis)
{
@@ -899,6 +1082,7 @@ void setMaxMailboxSize(Tid tid, size_t messages, OnCrowding doThis) @safe pure
* mailbox.
*/
void setMaxMailboxSize(Tid tid, size_t messages, bool function(Tid) onCrowdingDoThis)
+in (tid.mbox !is null)
{
tid.mbox.setMaxMsgs(messages, onCrowdingDoThis);
}
@@ -916,18 +1100,17 @@ private @property Mutex registryLock()
return impl;
}
-private void unregisterMe()
+private void unregisterMe(ref ThreadInfo me)
{
- auto me = thisInfo.ident;
- if (thisInfo.ident != Tid.init)
+ if (me.ident != Tid.init)
{
synchronized (registryLock)
{
- if (auto allNames = me in namesByTid)
+ if (auto allNames = me.ident in namesByTid)
{
foreach (name; *allNames)
tidByName.remove(name);
- namesByTid.remove(me);
+ namesByTid.remove(me.ident);
}
}
}
@@ -949,6 +1132,7 @@ private void unregisterMe()
* defunct thread.
*/
bool register(string name, Tid tid)
+in (tid.mbox !is null)
{
synchronized (registryLock)
{
@@ -1050,7 +1234,18 @@ struct ThreadInfo
_send(MsgType.linkDead, tid, ident);
if (owner != Tid.init)
_send(MsgType.linkDead, owner, ident);
- unregisterMe(); // clean up registry entries
+ unregisterMe(this); // clean up registry entries
+ }
+
+ // https://issues.dlang.org/show_bug.cgi?id=20160
+ @system unittest
+ {
+ register("main_thread", thisTid());
+
+ ThreadInfo t;
+ t.cleanup();
+
+ assert(locate("main_thread") == thisTid());
}
}
@@ -1270,13 +1465,45 @@ class FiberScheduler : Scheduler
/**
* Returns a Condition analog that yields when wait or notify is called.
+ *
+ * Bug:
+ * For the default implementation, `notifyAll`will behave like `notify`.
+ *
+ * Params:
+ * m = A `Mutex` to use for locking if the condition needs to be waited on
+ * or notified from multiple `Thread`s.
+ * If `null`, no `Mutex` will be used and it is assumed that the
+ * `Condition` is only waited on/notified from one `Thread`.
*/
Condition newCondition(Mutex m) nothrow
{
return new FiberCondition(m);
}
-private:
+protected:
+ /**
+ * Creates a new Fiber which calls the given delegate.
+ *
+ * Params:
+ * op = The delegate the fiber should call
+ */
+ void create(void delegate() op) nothrow
+ {
+ void wrap()
+ {
+ scope (exit)
+ {
+ thisInfo.cleanup();
+ }
+ op();
+ }
+
+ m_fibers ~= new InfoFiber(&wrap);
+ }
+
+ /**
+ * Fiber which embeds a ThreadInfo
+ */
static class InfoFiber : Fiber
{
ThreadInfo info;
@@ -1285,8 +1512,14 @@ private:
{
super(op);
}
+
+ this(void delegate() op, size_t sz) nothrow
+ {
+ super(op, sz);
+ }
}
+private:
class FiberCondition : Condition
{
this(Mutex m) nothrow
@@ -1313,7 +1546,7 @@ private:
!notified && !period.isNegative;
period = limit - MonoTime.currTime)
{
- yield();
+ this.outer.yield();
}
return notified;
}
@@ -1333,9 +1566,11 @@ private:
private:
void switchContext() nothrow
{
- mutex_nothrow.unlock_nothrow();
- scope (exit) mutex_nothrow.lock_nothrow();
- yield();
+ if (mutex_nothrow) mutex_nothrow.unlock_nothrow();
+ scope (exit)
+ if (mutex_nothrow)
+ mutex_nothrow.lock_nothrow();
+ this.outer.yield();
}
private bool notified;
@@ -1365,20 +1600,6 @@ private:
}
}
- void create(void delegate() op) nothrow
- {
- void wrap()
- {
- scope (exit)
- {
- thisInfo.cleanup();
- }
- op();
- }
-
- m_fibers ~= new InfoFiber(&wrap);
- }
-
private:
Fiber[] m_fibers;
size_t m_pos;
@@ -1464,35 +1685,6 @@ private interface IsGenerator {}
/**
* A Generator is a Fiber that periodically returns values of type T to the
* caller via yield. This is represented as an InputRange.
- *
- * Example:
- * ---
- * import std.concurrency;
- * import std.stdio;
- *
- *
- * void main()
- * {
- * auto tid = spawn(
- * {
- * while (true)
- * {
- * writeln(receiveOnly!int());
- * }
- * });
- *
- * auto r = new Generator!int(
- * {
- * foreach (i; 1 .. 10)
- * yield(i);
- * });
- *
- * foreach (e; r)
- * {
- * tid.send(e);
- * }
- * }
- * ---
*/
class Generator(T) :
Fiber, IsGenerator, InputRange!T
@@ -1533,6 +1725,27 @@ class Generator(T) :
}
/**
+ * Initializes a generator object which is associated with a static
+ * D function. The function will be called once to prepare the range
+ * for iteration.
+ *
+ * Params:
+ * fn = The fiber function.
+ * sz = The stack size for this fiber.
+ * guardPageSize = size of the guard page to trap fiber's stack
+ * overflows. Refer to $(REF Fiber, core,thread)'s
+ * documentation for more details.
+ *
+ * In:
+ * fn must not be null.
+ */
+ this(void function() fn, size_t sz, size_t guardPageSize)
+ {
+ super(fn, sz, guardPageSize);
+ call();
+ }
+
+ /**
* Initializes a generator object which is associated with a dynamic
* D function. The function will be called once to prepare the range
* for iteration.
@@ -1568,6 +1781,27 @@ class Generator(T) :
}
/**
+ * Initializes a generator object which is associated with a dynamic
+ * D function. The function will be called once to prepare the range
+ * for iteration.
+ *
+ * Params:
+ * dg = The fiber function.
+ * sz = The stack size for this fiber.
+ * guardPageSize = size of the guard page to trap fiber's stack
+ * overflows. Refer to $(REF Fiber, core,thread)'s
+ * documentation for more details.
+ *
+ * In:
+ * dg must not be null.
+ */
+ this(void delegate() dg, size_t sz, size_t guardPageSize)
+ {
+ super(dg, sz, guardPageSize);
+ call();
+ }
+
+ /**
* Returns true if the generator is empty.
*/
final bool empty() @property
@@ -1634,6 +1868,28 @@ private:
T* m_value;
}
+///
+@system unittest
+{
+ auto tid = spawn({
+ int i;
+ while (i < 9)
+ i = receiveOnly!int;
+
+ ownerTid.send(i * 2);
+ });
+
+ auto r = new Generator!int({
+ foreach (i; 1 .. 10)
+ yield(i);
+ });
+
+ foreach (e; r)
+ tid.send(e);
+
+ assert(receiveOnly!int == 18);
+}
+
/**
* Yields a value of type T to the caller of the currently executing
* generator.
@@ -1865,7 +2121,7 @@ private
{
import std.meta : AliasSeq;
- static assert(T.length);
+ static assert(T.length, "T must not be empty");
static if (isImplicitlyConvertible!(T[0], Duration))
{
@@ -1906,7 +2162,8 @@ private
bool onLinkDeadMsg(ref Message msg)
{
- assert(msg.convertsTo!(Tid));
+ assert(msg.convertsTo!(Tid),
+ "Message could be converted to Tid");
auto tid = msg.get!(Tid);
if (bool* pDepends = tid in thisInfo.links)
@@ -2083,7 +2340,8 @@ private
{
static void onLinkDeadMsg(ref Message msg)
{
- assert(msg.convertsTo!(Tid));
+ assert(msg.convertsTo!(Tid),
+ "Message could be converted to Tid");
auto tid = msg.get!(Tid);
thisInfo.links.remove(tid);
@@ -2228,7 +2486,7 @@ private
{
import std.exception : enforce;
- assert(m_count);
+ assert(m_count, "Can not remove from empty Range");
Node* n = r.m_prev;
enforce(n && n.next, "attempting to remove invalid list node");
@@ -2295,7 +2553,7 @@ private
}
if (n)
{
- import std.conv : emplace;
+ import core.lifetime : emplace;
emplace!Node(n, v);
}
else
@@ -2337,12 +2595,11 @@ private
}
}
-version (unittest)
+@system unittest
{
- import std.stdio;
import std.typecons : tuple, Tuple;
- void testfn(Tid tid)
+ static void testfn(Tid tid)
{
receive((float val) { assert(0); }, (int val, int val2) {
assert(val == 42 && val2 == 86);
@@ -2357,7 +2614,7 @@ version (unittest)
prioritySend(tid, "done");
}
- void runTest(Tid tid)
+ static void runTest(Tid tid)
{
send(tid, 42, 86);
send(tid, tuple(42, 86));
@@ -2366,7 +2623,7 @@ version (unittest)
receive((string val) { assert(val == "done"); });
}
- void simpleTest()
+ static void simpleTest()
{
auto tid = spawn(&testfn, thisTid);
runTest(tid);
@@ -2377,28 +2634,22 @@ version (unittest)
runTest(tid);
}
- @system unittest
- {
- simpleTest();
- }
+ simpleTest();
- @system unittest
- {
- scheduler = new ThreadScheduler;
- simpleTest();
- scheduler = null;
- }
+ scheduler = new ThreadScheduler;
+ simpleTest();
+ scheduler = null;
}
-private @property Mutex initOnceLock()
+private @property shared(Mutex) initOnceLock()
{
- __gshared Mutex lock;
- if (auto mtx = cast() atomicLoad!(MemoryOrder.acq)(*cast(shared)&lock))
+ static shared Mutex lock;
+ if (auto mtx = atomicLoad!(MemoryOrder.acq)(lock))
return mtx;
- auto mtx = new Mutex;
- if (cas(cast(shared)&lock, cast(shared) null, cast(shared) mtx))
+ auto mtx = new shared Mutex;
+ if (cas(&lock, cast(shared) null, mtx))
return mtx;
- return cast() atomicLoad!(MemoryOrder.acq)(*cast(shared)&lock);
+ return atomicLoad!(MemoryOrder.acq)(lock);
}
/**
@@ -2429,7 +2680,7 @@ auto ref initOnce(alias var)(lazy typeof(var) init)
{
static MySingleton instance()
{
- static __gshared MySingleton inst;
+ __gshared MySingleton inst;
return initOnce!inst(new MySingleton);
}
}
@@ -2443,14 +2694,14 @@ auto ref initOnce(alias var)(lazy typeof(var) init)
{
static MySingleton instance()
{
- static __gshared MySingleton inst;
+ __gshared MySingleton inst;
return initOnce!inst(new MySingleton);
}
private:
this() { val = ++cnt; }
size_t val;
- static __gshared size_t cnt;
+ __gshared size_t cnt;
}
foreach (_; 0 .. 10)
@@ -2476,7 +2727,7 @@ auto ref initOnce(alias var)(lazy typeof(var) init)
* Returns:
* A reference to the initialized variable
*/
-auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex)
+auto ref initOnce(alias var)(lazy typeof(var) init, shared Mutex mutex)
{
// check that var is global, can't take address of a TLS variable
static assert(is(typeof({ __gshared p = &var; })),
@@ -2488,7 +2739,7 @@ auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex)
{
synchronized (mutex)
{
- if (!atomicLoad!(MemoryOrder.acq)(flag))
+ if (!atomicLoad!(MemoryOrder.raw)(flag))
{
var = init;
atomicStore!(MemoryOrder.rel)(flag, true);
@@ -2498,14 +2749,20 @@ auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex)
return var;
}
+/// ditto
+auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex)
+{
+ return initOnce!var(init, cast(shared) mutex);
+}
+
/// Use a separate mutex when init blocks on another thread that might also call initOnce.
@system unittest
{
import core.sync.mutex : Mutex;
static shared bool varA, varB;
- __gshared Mutex m;
- m = new Mutex;
+ static shared Mutex m;
+ m = new shared Mutex;
spawn({
// use a different mutex for varB to avoid a dead-lock
@@ -2529,3 +2786,41 @@ auto ref initOnce(alias var)(lazy typeof(var) init, Mutex mutex)
static assert(!__traits(compiles, initOnce!c(true))); // TLS
static assert(!__traits(compiles, initOnce!d(true))); // local variable
}
+
+// test ability to send shared arrays
+@system unittest
+{
+ static shared int[] x = new shared(int)[1];
+ auto tid = spawn({
+ auto arr = receiveOnly!(shared(int)[]);
+ arr[0] = 5;
+ ownerTid.send(true);
+ });
+ tid.send(x);
+ receiveOnly!(bool);
+ assert(x[0] == 5);
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=13930
+@system unittest
+{
+ immutable aa = ["0":0];
+ thisTid.send(aa);
+ receiveOnly!(immutable int[string]); // compile error
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=19345
+@system unittest
+{
+ static struct Aggregate { const int a; const int[5] b; }
+ static void t1(Tid mainTid)
+ {
+ const sendMe = Aggregate(42, [1, 2, 3, 4, 5]);
+ mainTid.send(sendMe);
+ }
+
+ spawn(&t1, thisTid);
+ auto result1 = receiveOnly!(const Aggregate)();
+ immutable expected = Aggregate(42, [1, 2, 3, 4, 5]);
+ assert(result1 == expected);
+}