From: Eric Wong Date: 2018-06-22T22:06:18+00:00 Subject: [ruby-core:87599] Re: [Ruby trunk Feature#14859] [PATCH] implement Timeout in VM eregontp@gmail.com wrote: > normalperson (Eric Wong) wrote: > > eregontp@gmail.com wrote: > > > Something else, I would consider Timeout to be fundamentally > > > flawed as long as it relies on Thread#raise, because it can > > > fire in the middle of an ensure block: > > > http://headius.blogspot.com/2008/02/rubys-threadraise-threadkill-timeoutrb.html > > > > We have Thread.handle_interrupt, nowadays, to control when > > interrupts fire. > Right, although it's very difficult to use correctly (for > instance, it's incorrect to use Thread.handle_interrupt inside > the ensure block) and can easily cause hangs or deadlocks. Agreed, it should be easier-to-use; but that's a separate issue and I'm trying to avoid dealing with public API design as much as possible for this. > BTW, it looks like MonitorMixin::ConditionVariable doesn't use > Thread.handle_interrupt and could continue out of #wait (with > an exception thrown by Thread#raise) without reacquiring the > lock. Can you report separately to shugo to be sure he sees it? I've never really understood the point of that module :x > It might be nice to have a Timeout variant that only > interrupts blocking IO, without relying on Thread#raise (but > just SIGVTALRM). I think that would be easier/safer to use > than the current Timeout.timeout(). Not sure how to deal if > there are multiple IO calls inside that Timeout block though. > And there could still be blocking IO in an ensure block, which > would not work as intended. Agreed, I would welcome a "soft" timeout, without using signals/raise at all. I think my work-in-progress "intrusive" [PATCH 2/1] for this will make such a thing easier-to-implement: https://80x24.org/spew/20180622215745.20698-1-e@80x24.org/raw > > I considered that, too, but we'd need to add timeouts to > > every > single method which can block. There are many: File.open, > Queue#pop, SizedQueue#push, Mutex#lock/synchronize, > Process.wait*, IO#gets, IO#write, IO#read, IO#getc, > IO.copy_stream, ... > > It seems fine to me. Other implementations already have a > timeout on Queue#pop IIRC. I'm not sure we need all of them > right now (what use case for Mutex#lock/synchronize ?). I > think the main need would be for standard IO like > IO#read/write, especially on sockets and pipes. The maintenance overhead for adding timeouts to every call would be overwhelming on a human level, especially when 3rd-party libraries need to be considered. I would much rather do the following: Timeout.timeout(30) do foo.read(...) foo.write(...) IO.copy_stream(...) foo.write(...) szqueue.push(...) resultq.pop end Than this: def now Process.clock_gettime(Process::CLOCK_MONOTONIC) end begin @stop = now + 30 ... tout = @stop - now raise Timeout::Error if tout <= 0 foo.read(..., tout) tout = @stop - now raise Timeout::Error if tout <= 0 foo.write(..., tout) tout = @stop - now raise Timeout::Error if tout <= 0 IO.copy_stream(..., tout) tout = @stop - now raise Timeout::Error if tout <= 0 foo.write(..., tout) tout = @stop - now raise Timeout::Error if tout <= 0 szqueue.push(..., tout) tout = @stop - now raise Timeout::Error if tout <= 0 resultq.pop(tout) end Unsubscribe: