Skip to content

Commit

Permalink
Create a thread event objects for each ManualEvent.
Browse files Browse the repository at this point in the history
The idea behind using a shared per-thread event object for all ManualEvent instances was to save resources. However, the overhead can become quite excessive in situations where many ManualEvents are in a wait state, because all of them will be resumed whenever one of them gets emitted.

Resource optimization should instead be done in eventcore where necessary.
  • Loading branch information
s-ludwig committed Dec 13, 2023
1 parent fb2023c commit 919dbc5
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 18 deletions.
5 changes: 0 additions & 5 deletions source/vibe/core/core.d
Original file line number Diff line number Diff line change
Expand Up @@ -1927,11 +1927,6 @@ nothrow {

private void shutdownDriver()
{
if (ManualEvent.ms_threadEvent != EventID.init) {
eventDriver.events.releaseRef(ManualEvent.ms_threadEvent);
ManualEvent.ms_threadEvent = EventID.init;
}

static if (is(typeof(tryGetEventDriver()))) {
// avoid creating an event driver on threads that don't actually have one
if (auto drv = tryGetEventDriver())
Expand Down
29 changes: 16 additions & 13 deletions source/vibe/core/sync.d
Original file line number Diff line number Diff line change
Expand Up @@ -1139,9 +1139,6 @@ struct LocalManualEvent {
Waiter m_waiter;
}

// thread destructor in vibe.core.core will decrement the ref. count
package static EventID ms_threadEvent;

private void initialize()
nothrow {
import vibe.internal.allocator : Mallocator, makeGCSafe;
Expand Down Expand Up @@ -1350,16 +1347,26 @@ struct ManualEvent {
Monitor!(Waiters, shared(Mutex)) m_waiters;
}

// thread destructor in vibe.core.core will decrement the ref. count
package static EventID ms_threadEvent;

enum EmitMode {
single,
all
}

@disable this(this);

~this()
nothrow {
// NOTE: being in the destructor means that there are no other references
// left, so that we can safely access waiters without taking the
// lock, which in turn may already have been reaped by the GC at
// this point
auto waiters = () @trusted { return cast()m_waiters.data; } ();
waiters.free.filter((ThreadWaiter w) nothrow @trusted {
destroy(w);
return false;
});
}

private void initialize()
shared nothrow {
m_waiters.initialize(new shared Mutex);
Expand Down Expand Up @@ -1483,11 +1490,6 @@ struct ManualEvent {

() @trusted { logTrace("wait shared %s", cast(void*)&this); } ();

if (ms_threadEvent is EventID.invalid) {
ms_threadEvent = eventDriver.events.create();
assert(ms_threadEvent != EventID.invalid, "Failed to create event!");
}

MonoTime target_timeout, now;
if (timeout != Duration.max) {
try now = MonoTime.currTime();
Expand All @@ -1499,7 +1501,7 @@ struct ManualEvent {

acquireThreadWaiter((scope ThreadWaiter w) {
while (ec - emit_count <= 0) {
w.wait!interruptible(timeout != Duration.max ? target_timeout - now : Duration.max, ms_threadEvent, () => (this.emitCount - emit_count) > 0);
w.wait!interruptible(timeout != Duration.max ? target_timeout - now : Duration.max, w.m_event, () => (this.emitCount - emit_count) > 0);
ec = this.emitCount;

if (timeout != Duration.max) {
Expand Down Expand Up @@ -1545,7 +1547,8 @@ struct ManualEvent {
try {
w = processAllocator.makeGCSafe!ThreadWaiter;
w.m_driver = drv;
w.m_event = ms_threadEvent;
w.m_event = eventDriver.events.create();
assert(w.m_event != EventID.invalid, "Failed to create event!");
} catch (Exception e) {
assert(false, "Failed to allocate thread waiter.");
}
Expand Down

0 comments on commit 919dbc5

Please sign in to comment.