Kernel#sleep swallows InterruptedException #4206

Closed
raelik opened this Issue Oct 5, 2016 · 5 comments

Projects

None yet

2 participants

@raelik
raelik commented Oct 5, 2016 edited

Currently, the Kernel#sleep code silently swallows any InterruptedException thrown by the native Java Thread.sleep(). Presumably, this was done in an attempt to prevent "spurious wakeups" from prematurely waking up sleeping threads. @headius has an old gist out there that was intended to remove this (https://gist.github.com/headius/5393700), but it simply gets rid of the entire spin lock and just calls the native sleep(). I think the spin lock is still necessary to account for spurious wakeups, but it shouldn't catch the InterruptedException, as those are only generated by Thread.interrupt(). According to the Java documentation, a spurious wakeup it truly spurious: it generates no exception or timeout, the sleeping thread simply stops sleeping.

I think a good solution would be to change the spin lock code in RubyKernel#sleep (currently starting at line 660) to this:

        // Spurious wakeup-loop
        do {
            long loopStartTime = System.currentTimeMillis();
            // We break if we know this sleep was NOT explicitly woken up/interrupted
            if (rubyThread.sleep(milliseconds)) break;
            milliseconds -= (System.currentTimeMillis() - loopStartTime);
        }
        while (milliseconds > 0);

The original code wraps a try/catch around the sleep, which is what swallows the InterruptedException. Also, it breaks if sleep returns false... which seems to actually do the opposite of the intended behavior. RubyThread#sleep returns false if the sleep was bounded and did not sleep for the correct amount of time (i.e. spurious wakeup). Breaking from the while loop here when that happens would allow the spurious wakeup to terminate the sleep early. A spurious wakeup should not cause the sleep to terminate early, and an InterruptedException should bubble up, since those are quite intentional.

@headius
Member
headius commented Oct 6, 2016

I think you're right. This also was reported in another bug...I'll try to find that.

@headius
Member
headius commented Oct 6, 2016

The InterruptedException is checked, so it still needs to be caught...but we'll just break out of the spin loop if it is raised. I have a fix coming...writing up a test now.

@headius headius closed this in 51124a6 Oct 6, 2016
@headius headius added this to the JRuby 9.1.6.0 milestone Oct 6, 2016
@raelik
raelik commented Oct 6, 2016

I guess that'll work, instead of trying to rescue InterruptedException, I can just check the native thread for isInterrupted() right after the sleep exits if I need to do something specific in that case.

@headius
Member
headius commented Oct 6, 2016

Yeah sorry...we can't really allow the InterruptedException to get out, and there's no equivalent exception that MRI throws here for interrupted sleeps. The closest equivalent behavior in Ruby would be Thread#wakeup, which interrupts the sleep but does not raise an error.

@raelik
raelik commented Oct 7, 2016

Right, that's exactly what I realized after I read that 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment