From 4f59ece52318ea5c1eb0321f29fd3a399a7e531a Mon Sep 17 00:00:00 2001 From: Denis Feklushkin Date: Sun, 24 Mar 2024 08:13:17 +0300 Subject: [PATCH 1/8] m_initalized flag moved to core.sync.event.Event This flag now is redundant for Windows platform --- druntime/src/core/sync/event.d | 17 ++++++++-- druntime/src/rt/sys/posix/osevent.d | 45 +++++++++------------------ druntime/src/rt/sys/windows/osevent.d | 23 +++++--------- 3 files changed, 35 insertions(+), 50 deletions(-) diff --git a/druntime/src/core/sync/event.d b/druntime/src/core/sync/event.d index fa3937a157..d276b96a93 100644 --- a/druntime/src/core/sync/event.d +++ b/druntime/src/core/sync/event.d @@ -75,7 +75,8 @@ nothrow @nogc: */ void initialize(bool manualReset, bool initialState) { - osEvent.create(manualReset, initialState); + osEvent = OsEvent(manualReset, initialState); + m_initalized = true; } // copying not allowed, can produce resource leaks @@ -85,6 +86,7 @@ nothrow @nogc: ~this() { terminate(); + m_initalized = false; } /** @@ -104,13 +106,15 @@ nothrow @nogc: /// Set the event to "signaled", so that waiting clients are resumed void setIfInitialized() { - osEvent.setIfInitialized(); + if(m_initalized) + osEvent.set(); } /// Reset the event manually void reset() { - osEvent.reset(); + if(m_initalized) + osEvent.reset(); } /** @@ -121,6 +125,9 @@ nothrow @nogc: */ bool wait() { + if (!m_initalized) + return false; + return osEvent.wait(); } @@ -135,12 +142,16 @@ nothrow @nogc: */ bool wait(Duration tmout) { + if (!m_initalized) + return false; + return osEvent.wait(tmout); } private: mixin("import " ~ osEventImport ~ ";"); OsEvent osEvent; + bool m_initalized; } // Test single-thread (non-shared) use. diff --git a/druntime/src/rt/sys/posix/osevent.d b/druntime/src/rt/sys/posix/osevent.d index 2d28a4a38c..d6b8f1f938 100644 --- a/druntime/src/rt/sys/posix/osevent.d +++ b/druntime/src/rt/sys/posix/osevent.d @@ -11,10 +11,8 @@ import core.internal.abort : abort; struct OsEvent { - void create(bool manualReset, bool initialState) nothrow @trusted @nogc + this(bool manualReset, bool initialState) nothrow @trusted @nogc { - if (m_initalized) - return; pthread_mutex_init(cast(pthread_mutex_t*) &m_mutex, null) == 0 || abort("Error: pthread_mutex_init failed."); static if ( is( typeof( pthread_condattr_setclock ) ) ) @@ -37,40 +35,29 @@ struct OsEvent m_state = initialState; m_manualReset = manualReset; - m_initalized = true; } - void destroy() nothrow @trusted @nogc + ~this() nothrow @trusted @nogc { - if (m_initalized) - { - pthread_mutex_destroy(&m_mutex) == 0 || - abort("Error: pthread_mutex_destroy failed."); - pthread_cond_destroy(&m_cond) == 0 || - abort("Error: pthread_cond_destroy failed."); - m_initalized = false; - } + pthread_mutex_destroy(&m_mutex) == 0 || + abort("Error: pthread_mutex_destroy failed."); + pthread_cond_destroy(&m_cond) == 0 || + abort("Error: pthread_cond_destroy failed."); } - void setIfInitialized() nothrow @trusted @nogc + void set() nothrow @trusted @nogc { - if (m_initalized) - { - pthread_mutex_lock(&m_mutex); - m_state = true; - pthread_cond_broadcast(&m_cond); - pthread_mutex_unlock(&m_mutex); - } + pthread_mutex_lock(&m_mutex); + m_state = true; + pthread_cond_broadcast(&m_cond); + pthread_mutex_unlock(&m_mutex); } void reset() nothrow @trusted @nogc { - if (m_initalized) - { - pthread_mutex_lock(&m_mutex); - m_state = false; - pthread_mutex_unlock(&m_mutex); - } + pthread_mutex_lock(&m_mutex); + m_state = false; + pthread_mutex_unlock(&m_mutex); } bool wait() nothrow @trusted @nogc @@ -80,9 +67,6 @@ struct OsEvent bool wait(Duration tmout) nothrow @trusted @nogc { - if (!m_initalized) - return false; - pthread_mutex_lock(&m_mutex); int result = 0; @@ -114,7 +98,6 @@ private: pthread_mutex_t m_mutex; pthread_cond_t m_cond; - bool m_initalized; bool m_state; bool m_manualReset; } \ No newline at end of file diff --git a/druntime/src/rt/sys/windows/osevent.d b/druntime/src/rt/sys/windows/osevent.d index e5536a81df..83ef4da79d 100644 --- a/druntime/src/rt/sys/windows/osevent.d +++ b/druntime/src/rt/sys/windows/osevent.d @@ -12,43 +12,34 @@ import core.internal.abort : abort; struct OsEvent { - void create(bool manualReset, bool initialState) nothrow @trusted @nogc + this(bool manualReset, bool initialState) nothrow @trusted @nogc { - if (m_event) - return; m_event = CreateEvent(null, manualReset, initialState, null); m_event || abort("Error: CreateEvent failed."); } - void destroy() nothrow @trusted @nogc + ~this() nothrow @trusted @nogc { - if (m_event) - CloseHandle(m_event); - m_event = null; + CloseHandle(m_event); } - void setIfInitialized() nothrow @trusted @nogc + void set() nothrow @trusted @nogc { - if (m_event) - SetEvent(m_event); + SetEvent(m_event); } void reset() nothrow @trusted @nogc { - if (m_event) - ResetEvent(m_event); + ResetEvent(m_event); } bool wait() nothrow @trusted @nogc { - return m_event && WaitForSingleObject(m_event, INFINITE) == WAIT_OBJECT_0; + return WaitForSingleObject(m_event, INFINITE) == WAIT_OBJECT_0; } bool wait(Duration tmout) nothrow @trusted @nogc { - if (!m_event) - return false; - auto maxWaitMillis = dur!("msecs")(uint.max - 1); while (tmout > maxWaitMillis) From 0f2c83553356de9fb1537eb7f39a31c7dfa06b5a Mon Sep 17 00:00:00 2001 From: Denis Feklushkin Date: Sun, 24 Mar 2024 08:30:45 +0300 Subject: [PATCH 2/8] module core.sync.event2 added --- druntime/src/core/sync/event2.d | 123 ++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 druntime/src/core/sync/event2.d diff --git a/druntime/src/core/sync/event2.d b/druntime/src/core/sync/event2.d new file mode 100644 index 0000000000..eca32b9fef --- /dev/null +++ b/druntime/src/core/sync/event2.d @@ -0,0 +1,123 @@ +/** + * The event module provides a primitive for lightweight signaling of other threads + * (emulating Windows events on Posix) + * + * Copyright: Copyright (c) 2019 D Language Foundation + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) + */ +module core.sync.event2; + +import core.time; + +struct Event2 +{ +nothrow @nogc: + /** + * Creates an event object. + * + * Params: + * manualReset = the state of the event is not reset automatically after resuming waiting clients + * initialState = initial state of the signal + */ + this(bool manualReset, bool initialState) + { + osEvent = OsEvent(manualReset, initialState); + } + + // copying not allowed, can produce resource leaks + @disable this(this); + @disable void opAssign(Event2); + + /// Set the event to "signaled", so that waiting clients are resumed + void set() + { + osEvent.set(); + } + + /// Reset the event manually + void reset() + { + osEvent.reset(); + } + + /** + * Wait for the event to be signaled without timeout. + * + * Returns: + * `true` if the event is in signaled state, `false` if the event is uninitialized or another error occured + */ + bool wait() + { + return osEvent.wait(); + } + + /** + * Wait for the event to be signaled with timeout. + * + * Params: + * tmout = the maximum time to wait + * Returns: + * `true` if the event is in signaled state, `false` if the event was nonsignaled for the given time or + * the event is uninitialized or another error occured + */ + bool wait(Duration tmout) + { + return osEvent.wait(tmout); + } + +private: + import rt.sys.config; + + mixin("import " ~ osEventImport ~ ";"); + OsEvent osEvent; +} + +// Test single-thread (non-shared) use. +@nogc nothrow unittest +{ + // auto-reset, initial state false + Event2 ev1 = Event2(false, false); + assert(!ev1.wait(1.dur!"msecs")); + ev1.set(); + assert(ev1.wait()); + assert(!ev1.wait(1.dur!"msecs")); + + // manual-reset, initial state true + Event ev2 = Event(true, true); + assert(ev2.wait()); + assert(ev2.wait()); + ev2.reset(); + assert(!ev2.wait(1.dur!"msecs")); +} + +unittest +{ + import core.thread, core.atomic; + + scope event = new Event2(true, false); + int numThreads = 10; + shared int numRunning = 0; + + void testFn() + { + event.wait(8.dur!"seconds"); // timeout below limit for druntime test_runner + numRunning.atomicOp!"+="(1); + } + + auto group = new ThreadGroup; + + for (int i = 0; i < numThreads; ++i) + group.create(&testFn); + + auto start = MonoTime.currTime; + assert(numRunning == 0); + + event.set(); + group.joinAll(); + + assert(numRunning == numThreads); + + assert(MonoTime.currTime - start < 5.dur!"seconds"); +} From 3a0aa4c49ea7d6b6a5fb6519b16d35e4d157a29b Mon Sep 17 00:00:00 2001 From: Denis Feklushkin Date: Sun, 24 Mar 2024 09:32:59 +0300 Subject: [PATCH 3/8] mixin removed from legacy Event struct --- druntime/src/core/sync/event.d | 9 ++++----- druntime/src/core/sync/event2.d | 6 +++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/druntime/src/core/sync/event.d b/druntime/src/core/sync/event.d index d276b96a93..4a8ca7bf73 100644 --- a/druntime/src/core/sync/event.d +++ b/druntime/src/core/sync/event.d @@ -13,7 +13,7 @@ module core.sync.event; import core.time; -import rt.sys.config; +import core.sync.event2; /** * represents an event. Clients of an event are suspended while waiting @@ -73,9 +73,9 @@ nothrow @nogc: * manualReset = the state of the event is not reset automatically after resuming waiting clients * initialState = initial state of the signal */ - void initialize(bool manualReset, bool initialState) + void initialize(bool manualReset, bool initialState) @live { - osEvent = OsEvent(manualReset, initialState); + osEvent = Event2(manualReset, initialState); m_initalized = true; } @@ -149,8 +149,7 @@ nothrow @nogc: } private: - mixin("import " ~ osEventImport ~ ";"); - OsEvent osEvent; + Event2 osEvent; bool m_initalized; } diff --git a/druntime/src/core/sync/event2.d b/druntime/src/core/sync/event2.d index eca32b9fef..dd954790ce 100644 --- a/druntime/src/core/sync/event2.d +++ b/druntime/src/core/sync/event2.d @@ -28,7 +28,11 @@ nothrow @nogc: // copying not allowed, can produce resource leaks @disable this(this); - @disable void opAssign(Event2); + + void opAssign(return scope Event2 s) @live + { + this.osEvent = s.osEvent; + } /// Set the event to "signaled", so that waiting clients are resumed void set() From 1c5f80e4e51227a4604840197b8d38fe55f67135 Mon Sep 17 00:00:00 2001 From: Denis Feklushkin Date: Sun, 24 Mar 2024 11:00:59 +0300 Subject: [PATCH 4/8] Move GC to Event2 --- .../core/internal/gc/impl/conservative/gc.d | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/druntime/src/core/internal/gc/impl/conservative/gc.d b/druntime/src/core/internal/gc/impl/conservative/gc.d index 4ba458783e..2e918ab1c6 100644 --- a/druntime/src/core/internal/gc/impl/conservative/gc.d +++ b/druntime/src/core/internal/gc/impl/conservative/gc.d @@ -3269,8 +3269,7 @@ Lmark: Gcx.instance.scanThreadData = null; Gcx.instance.busyThreads = 0; - memset(&Gcx.instance.evStart, 0, Gcx.instance.evStart.sizeof); - memset(&Gcx.instance.evDone, 0, Gcx.instance.evDone.sizeof); + memset(&Gcx.instance.ev, 0, Gcx.instance.ev.sizeof); } } } @@ -3282,7 +3281,7 @@ Lmark: import core.atomic; import core.cpuid; - import core.sync.event; + import core.sync.event2 : Event2; private: // disable invariants for background threads @@ -3293,8 +3292,12 @@ Lmark: uint numScanThreads; ScanThreadData* scanThreadData; - Event evStart; - Event evDone; + static struct EvStartDone + { + Event2 start; + Event2 done; + } + EvStartDone ev; shared uint busyThreads; shared uint stoppedThreads; @@ -3336,7 +3339,7 @@ Lmark: busyThreads.atomicOp!"+="(1); // main thread is busy - evStart.setIfInitialized(); + ev.start.set(); debug(PARALLEL_PRINTF) printf("mark %lld roots\n", cast(ulong)(ptop - pbot)); @@ -3396,8 +3399,8 @@ Lmark: if (!scanThreadData) onOutOfMemoryError(); - evStart.initialize(false, false); - evDone.initialize(false, false); + ev.start = Event2(false, false); + ev.done = Event2(false, false); version (Posix) { @@ -3438,8 +3441,8 @@ Lmark: stopGC = true; while (atomicLoad(stoppedThreads) < startedThreads && !allThreadsDead) { - evStart.setIfInitialized(); - evDone.wait(dur!"msecs"(1)); + ev.start.set(); + ev.done.wait(dur!"msecs"(1)); } for (int idx = 0; idx < numScanThreads; idx++) @@ -3451,8 +3454,8 @@ Lmark: } } - evDone.terminate(); - evStart.terminate(); + destroy(ev.start); + destroy(ev.done); cstdlib.free(scanThreadData); // scanThreadData = null; // keep non-null to not start again after shutdown @@ -3465,9 +3468,9 @@ Lmark: { while (!stopGC) { - evStart.wait(); + ev.start.wait(); pullFromScanStack(); - evDone.setIfInitialized(); + ev.done.set(); } stoppedThreads.atomicOp!"+="(1); } @@ -3496,7 +3499,7 @@ Lmark: { if (toscan.empty) { - evDone.wait(dur!"msecs"(1)); + ev.done.wait(dur!"msecs"(1)); continue; } From cdc8eed8117917a64996c3b1dd2741952f36a104 Mon Sep 17 00:00:00 2001 From: Denis Feklushkin Date: Sun, 24 Mar 2024 11:26:16 +0300 Subject: [PATCH 5/8] Event2: opAssign return value added --- druntime/src/core/sync/event2.d | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/druntime/src/core/sync/event2.d b/druntime/src/core/sync/event2.d index dd954790ce..49f28804f6 100644 --- a/druntime/src/core/sync/event2.d +++ b/druntime/src/core/sync/event2.d @@ -29,9 +29,11 @@ nothrow @nogc: // copying not allowed, can produce resource leaks @disable this(this); - void opAssign(return scope Event2 s) @live + ref Event2 opAssign(return scope Event2 s) @live { this.osEvent = s.osEvent; + + return this; } /// Set the event to "signaled", so that waiting clients are resumed From a2fbb6c24bad1b20ecaeabf49519b29cbe38f15f Mon Sep 17 00:00:00 2001 From: Denis Feklushkin Date: Sun, 24 Mar 2024 11:40:07 +0300 Subject: [PATCH 6/8] Event: deprecation message added --- druntime/src/core/sync/event.d | 1 + 1 file changed, 1 insertion(+) diff --git a/druntime/src/core/sync/event.d b/druntime/src/core/sync/event.d index 4a8ca7bf73..049471f798 100644 --- a/druntime/src/core/sync/event.d +++ b/druntime/src/core/sync/event.d @@ -51,6 +51,7 @@ struct ProcessFile } --- */ +deprecated("Please use core.sync.event2.Event2 instead") struct Event { nothrow @nogc: From 937b5300b8750152a1f3b95f39713d249afe231a Mon Sep 17 00:00:00 2001 From: Denis Feklushkin Date: Sun, 24 Mar 2024 11:47:35 +0300 Subject: [PATCH 7/8] Event2 unittest fix --- druntime/src/core/sync/event2.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/druntime/src/core/sync/event2.d b/druntime/src/core/sync/event2.d index 49f28804f6..d22dbd2c5e 100644 --- a/druntime/src/core/sync/event2.d +++ b/druntime/src/core/sync/event2.d @@ -91,7 +91,7 @@ private: assert(!ev1.wait(1.dur!"msecs")); // manual-reset, initial state true - Event ev2 = Event(true, true); + Event2 ev2 = Event2(true, true); assert(ev2.wait()); assert(ev2.wait()); ev2.reset(); From 4e5cd94845fd464977ff87ddfb6764732dcffbb6 Mon Sep 17 00:00:00 2001 From: Denis Feklushkin Date: Mon, 25 Mar 2024 20:45:47 +0300 Subject: [PATCH 8/8] Event2 code moved to module core.sync.event and renamed to EventAwaiter --- .../core/internal/gc/impl/conservative/gc.d | 10 +- druntime/src/core/sync/event.d | 123 ++++++++++++++++- druntime/src/core/sync/event2.d | 129 ------------------ 3 files changed, 124 insertions(+), 138 deletions(-) delete mode 100644 druntime/src/core/sync/event2.d diff --git a/druntime/src/core/internal/gc/impl/conservative/gc.d b/druntime/src/core/internal/gc/impl/conservative/gc.d index 2e918ab1c6..ce0c789714 100644 --- a/druntime/src/core/internal/gc/impl/conservative/gc.d +++ b/druntime/src/core/internal/gc/impl/conservative/gc.d @@ -3281,7 +3281,7 @@ Lmark: import core.atomic; import core.cpuid; - import core.sync.event2 : Event2; + import core.sync.event : EventAwaiter; private: // disable invariants for background threads @@ -3294,8 +3294,8 @@ Lmark: static struct EvStartDone { - Event2 start; - Event2 done; + EventAwaiter start; + EventAwaiter done; } EvStartDone ev; @@ -3399,8 +3399,8 @@ Lmark: if (!scanThreadData) onOutOfMemoryError(); - ev.start = Event2(false, false); - ev.done = Event2(false, false); + ev.start = EventAwaiter(false, false); + ev.done = EventAwaiter(false, false); version (Posix) { diff --git a/druntime/src/core/sync/event.d b/druntime/src/core/sync/event.d index 049471f798..1de9ac9e71 100644 --- a/druntime/src/core/sync/event.d +++ b/druntime/src/core/sync/event.d @@ -13,7 +13,122 @@ module core.sync.event; import core.time; -import core.sync.event2; +struct EventAwaiter +{ +nothrow @nogc: + /** + * Creates an event object. + * + * Params: + * manualReset = the state of the event is not reset automatically after resuming waiting clients + * initialState = initial state of the signal + */ + this(bool manualReset, bool initialState) + { + osEvent = OsEvent(manualReset, initialState); + } + + // copying not allowed, can produce resource leaks + @disable this(this); + + ref EventAwaiter opAssign(return scope EventAwaiter s) @live + { + this.osEvent = s.osEvent; + + return this; + } + + /// Set the event to "signaled", so that waiting clients are resumed + void set() + { + osEvent.set(); + } + + /// Reset the event manually + void reset() + { + osEvent.reset(); + } + + /** + * Wait for the event to be signaled without timeout. + * + * Returns: + * `true` if the event is in signaled state, `false` if the event is uninitialized or another error occured + */ + bool wait() + { + return osEvent.wait(); + } + + /** + * Wait for the event to be signaled with timeout. + * + * Params: + * tmout = the maximum time to wait + * Returns: + * `true` if the event is in signaled state, `false` if the event was nonsignaled for the given time or + * the event is uninitialized or another error occured + */ + bool wait(Duration tmout) + { + return osEvent.wait(tmout); + } + +private: + import rt.sys.config; + + mixin("import " ~ osEventImport ~ ";"); + OsEvent osEvent; +} + +// Test single-thread (non-shared) use. +@nogc nothrow unittest +{ + // auto-reset, initial state false + EventAwaiter ev1 = EventAwaiter(false, false); + assert(!ev1.wait(1.dur!"msecs")); + ev1.set(); + assert(ev1.wait()); + assert(!ev1.wait(1.dur!"msecs")); + + // manual-reset, initial state true + EventAwaiter ev2 = EventAwaiter(true, true); + assert(ev2.wait()); + assert(ev2.wait()); + ev2.reset(); + assert(!ev2.wait(1.dur!"msecs")); +} + +unittest +{ + import core.thread, core.atomic; + + scope event = new EventAwaiter(true, false); + int numThreads = 10; + shared int numRunning = 0; + + void testFn() + { + event.wait(8.dur!"seconds"); // timeout below limit for druntime test_runner + numRunning.atomicOp!"+="(1); + } + + auto group = new ThreadGroup; + + for (int i = 0; i < numThreads; ++i) + group.create(&testFn); + + auto start = MonoTime.currTime; + assert(numRunning == 0); + + event.set(); + group.joinAll(); + + assert(numRunning == numThreads); + + assert(MonoTime.currTime - start < 5.dur!"seconds"); +} /** * represents an event. Clients of an event are suspended while waiting @@ -51,7 +166,7 @@ struct ProcessFile } --- */ -deprecated("Please use core.sync.event2.Event2 instead") +deprecated("Please use core.sync.event.EventAwaiter instead") struct Event { nothrow @nogc: @@ -76,7 +191,7 @@ nothrow @nogc: */ void initialize(bool manualReset, bool initialState) @live { - osEvent = Event2(manualReset, initialState); + osEvent = EventAwaiter(manualReset, initialState); m_initalized = true; } @@ -150,7 +265,7 @@ nothrow @nogc: } private: - Event2 osEvent; + EventAwaiter osEvent; bool m_initalized; } diff --git a/druntime/src/core/sync/event2.d b/druntime/src/core/sync/event2.d deleted file mode 100644 index d22dbd2c5e..0000000000 --- a/druntime/src/core/sync/event2.d +++ /dev/null @@ -1,129 +0,0 @@ -/** - * The event module provides a primitive for lightweight signaling of other threads - * (emulating Windows events on Posix) - * - * Copyright: Copyright (c) 2019 D Language Foundation - * License: Distributed under the - * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). - * (See accompanying file LICENSE) - */ -module core.sync.event2; - -import core.time; - -struct Event2 -{ -nothrow @nogc: - /** - * Creates an event object. - * - * Params: - * manualReset = the state of the event is not reset automatically after resuming waiting clients - * initialState = initial state of the signal - */ - this(bool manualReset, bool initialState) - { - osEvent = OsEvent(manualReset, initialState); - } - - // copying not allowed, can produce resource leaks - @disable this(this); - - ref Event2 opAssign(return scope Event2 s) @live - { - this.osEvent = s.osEvent; - - return this; - } - - /// Set the event to "signaled", so that waiting clients are resumed - void set() - { - osEvent.set(); - } - - /// Reset the event manually - void reset() - { - osEvent.reset(); - } - - /** - * Wait for the event to be signaled without timeout. - * - * Returns: - * `true` if the event is in signaled state, `false` if the event is uninitialized or another error occured - */ - bool wait() - { - return osEvent.wait(); - } - - /** - * Wait for the event to be signaled with timeout. - * - * Params: - * tmout = the maximum time to wait - * Returns: - * `true` if the event is in signaled state, `false` if the event was nonsignaled for the given time or - * the event is uninitialized or another error occured - */ - bool wait(Duration tmout) - { - return osEvent.wait(tmout); - } - -private: - import rt.sys.config; - - mixin("import " ~ osEventImport ~ ";"); - OsEvent osEvent; -} - -// Test single-thread (non-shared) use. -@nogc nothrow unittest -{ - // auto-reset, initial state false - Event2 ev1 = Event2(false, false); - assert(!ev1.wait(1.dur!"msecs")); - ev1.set(); - assert(ev1.wait()); - assert(!ev1.wait(1.dur!"msecs")); - - // manual-reset, initial state true - Event2 ev2 = Event2(true, true); - assert(ev2.wait()); - assert(ev2.wait()); - ev2.reset(); - assert(!ev2.wait(1.dur!"msecs")); -} - -unittest -{ - import core.thread, core.atomic; - - scope event = new Event2(true, false); - int numThreads = 10; - shared int numRunning = 0; - - void testFn() - { - event.wait(8.dur!"seconds"); // timeout below limit for druntime test_runner - numRunning.atomicOp!"+="(1); - } - - auto group = new ThreadGroup; - - for (int i = 0; i < numThreads; ++i) - group.create(&testFn); - - auto start = MonoTime.currTime; - assert(numRunning == 0); - - event.set(); - group.joinAll(); - - assert(numRunning == numThreads); - - assert(MonoTime.currTime - start < 5.dur!"seconds"); -}