RubyThread.sleep with a Lock currently uses a Condition to release and await that lock's signal:
|
public void sleep(Lock lock, long millis) throws InterruptedException { |
|
assert Thread.currentThread() == getNativeThread(); |
|
executeTaskBlocking(getContext(), lock.newCondition(), Status.NATIVE, new Task<Condition, Object>() { |
|
@Override |
|
public Object run(ThreadContext context, Condition condition) throws InterruptedException { |
|
if (millis == 0) { |
|
condition.await(); |
|
} else { |
|
condition.await(millis, TimeUnit.MILLISECONDS); |
|
} |
|
return null; |
|
} |
|
|
|
@Override |
|
public void wakeup(RubyThread thread, Condition condition) { |
|
thread.getNativeThread().interrupt(); |
|
} |
|
}); |
|
} |
In order to wake up the sleeping thread, this logic calls interrupt. This effectively terminates the sleep, but also raises an InterruptedException, which in turn requires a full stack trace and greatly adds to the overhead of a sleep wake-up.
This logic is used many places, such as for Mutex#sleep and ConditionVariable#await (which uses Mutex#sleep). The result is that the throughput of any sleep/wakeup or await/signal systems has much more overhead than it should. Waking a sleeping thread should require no more than signalling the sleep lock to unpark the sleeping thread and attempt to reacquire the lock.
This is a source of overhead in the Puma thread pool, resulting in excess allocation, GC, and VM synchronization overhead (to build the stack trace). See puma/puma#3788 for some discussion of this.
We need to fix this to do a simple condition-based signal to wake the sleeping thread.
This may also require some rework of how Mutex and ConditionVariable are implemented.
RubyThread.sleep with a Lock currently uses a Condition to release and await that lock's signal:
jruby/core/src/main/java/org/jruby/RubyThread.java
Lines 2548 to 2566 in 2b67dd6
In order to wake up the sleeping thread, this logic calls
interrupt. This effectively terminates the sleep, but also raises anInterruptedException, which in turn requires a full stack trace and greatly adds to the overhead of a sleep wake-up.This logic is used many places, such as for
Mutex#sleepandConditionVariable#await(which usesMutex#sleep). The result is that the throughput of anysleep/wakeuporawait/signalsystems has much more overhead than it should. Waking a sleeping thread should require no more than signalling the sleep lock to unpark the sleeping thread and attempt to reacquire the lock.This is a source of overhead in the Puma thread pool, resulting in excess allocation, GC, and VM synchronization overhead (to build the stack trace). See puma/puma#3788 for some discussion of this.
We need to fix this to do a simple condition-based signal to wake the sleeping thread.
This may also require some rework of how Mutex and ConditionVariable are implemented.