Module: TimeoutInterruptSingleton

Defined in:
lib/timeout_interrupt.rb

Overview

Helper module for ‘TimeoutInterrupt`

See Also:

Class Method Summary collapse

Class Method Details

.alarm_trap(sig) ⇒ nil

If there’s a timed out timeout, it will raise its exception. Can be used for handling ALRM-signal. It will prepare the next timeout, too.

The timeout will not removed from timeouts, because it is timed out, yet. First, if timeout-scope will be exit, it will be removed.

Returns:

  • (nil)


43
44
45
46
# File 'lib/timeout_interrupt.rb', line 43

def alarm_trap sig
	raise_if_sb_timed_out
	setup
end

.raise_if_sb_timed_outnil

There is a timed out timeout? It will raise it! You need not to check it yourself, it will do it for you.

Returns:

  • (nil)

Raises:

  • (exception)


52
53
54
55
56
57
# File 'lib/timeout_interrupt.rb', line 52

def raise_if_sb_timed_out
	return  if self.timeouts.empty?
	key, (at, bt, exception) = self.timeouts.min_by {|key,(at,bt,ex)| at }
	return  if Time.now < at
	raise exception, 'execution expired', bt
end

.setupnil

Prepares the next timeout. Sets the trap and the shortest timeout as alarm.

Returns:

  • (nil)


62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/timeout_interrupt.rb', line 62

def setup
	if timeouts.empty?
		Signal.trap( 'ALRM') {}
		FFI::LibC.alarm 0
	else
		raise_if_sb_timed_out
		Signal.trap 'ALRM', &method( :alarm_trap)
		key, (at, bt) = timeouts.min_by {|key,(at,bt)| at }
		FFI::LibC.alarm (at - Time.now).to_i + 1
	end
	nil
end

.timeout(seconds = nil, exception = nil, &block) ⇒ Object

Creates a timeout and calls your block, which has to finish before timeout occurs.

You can rescue ‘Timeout::Error`, instead `TimeoutInterrupt::Error`, it will work too.

It will call your given block, which has ‘seconds` seconds to end. If you want to prepare a timeout, which should be used many times, without giving `seconds` and `exception`, you can omit the block, so, `TimeoutInterruptSingleton#timeout` will return a `Proc`, which want to have the block.

There is a problem with scoped timeouts. If you rescue a timeout in an other timeout, it’s possible, that the other timeout will never timeout, because both are timed out at once. Than you need to call ‘TimeoutInterruptSingleton#timeout` without arguments. It will prepare the next timeout or it will raise it directy, if timed out.

Parameters:

  • seconds (0) (defaults to: nil)

    No timeout, so block can take any time.

  • seconds (Integer) (defaults to: nil)

    In ‘seconds` Seconds, it should raise a timeout, if not finished.

  • seconds (nil) (defaults to: nil)

    If also no block given, everything will be ignored and it will call setup for checking and preparing next known timeout.

  • exception (Exception) (defaults to: nil)

    which will be raised if timed out.

  • exception (nil) (defaults to: nil)

    ‘TimeoutInterrupt::Error` will be used to raise.

  • block (Proc)

    Will be called and should finish its work before it timed out.

  • block (nil)

    Nothing will happen, instead it will return a Proc, which can be called with a block to use the timeout.

Returns:

  • If block given, the returned value of your block. Or if not, it will return a Proc, which will expect a Proc if called. This Proc has no arguments and will prepare a timeout, like if you had given a block.

Raises:

  • exception

See Also:



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/timeout_interrupt.rb', line 105

def timeout seconds = nil, exception = nil, &block
	return yield( seconds)  if seconds.nil? || 0 == seconds  if block_given?
	return setup  if seconds.nil?
	seconds = seconds.to_i
	exception ||= TimeoutInterrupt::Error
	raise exception, "Timeout must be longer than '0' seconds."  unless 0 < seconds
	unless block_given?
		return lambda {|&e|
			raise exception, "Expect a lambda."  unless e
			timeout seconds, exception, &e
		}
	end
	at = Time.now + seconds
	key, bt = Random.rand( 2**64-1), Kernel.caller
	begin
		self.timeouts[key] = [at, bt, exception]
		setup
		yield seconds
	ensure
		self.timeouts.delete key
		setup
	end
end

.timeouts(thread = nil) ⇒ Hash< key(Integer): [at(Time), backtrace(Array<String>), exception(Exception)] >

Stores all timeouts.

Parameters:

  • thread (nil) (defaults to: nil)

    must be nil! Do not use it yet!

Returns:

  • (Hash< key(Integer): [at(Time), backtrace(Array<String>), exception(Exception)] >)


29
30
31
32
33
# File 'lib/timeout_interrupt.rb', line 29

def timeouts thread = nil
	@timeouts ||= Hash.new {|h,k| h[k] = {} }
	thread = Thread.current  unless thread.kind_of? Thread
	thread ? @timeouts[thread] : @timeouts
end