Compare commits

...

21 Commits

Author SHA1 Message Date
Denis Knauf f963e41c52 „README.md“ ändern 2022-03-24 12:45:07 +01:00
Denis Knauf 5b390b2083 reimplement tests and minor fixes. 2021-12-12 18:19:34 +01:00
Denis Knauf 3542837d7f small minor warnings fixed (unused vars, ...). comments. 2021-12-12 18:18:33 +01:00
Denis Knauf 28936187de dependencies: need should, test-unit for testing. but no rspec. 2021-12-12 18:16:14 +01:00
Denis Knauf 486cf53495 .document removed and ignored 2021-12-12 18:14:31 +01:00
Denis Knauf ae8f1e0a33 Gemfile.lock removed. Ignore this file anyway 2021-12-12 18:13:21 +01:00
Denis Knauf 3ca2de0cd9 email added 2021-12-12 16:11:03 +01:00
Denis Knauf 9fc25c3015 Actiual versions of ffi-libc provide FFI::LibC::alarm. ffi-libc<0.1.1 has a bug with sys_errlist, so 0.1.1 required. 2021-12-12 16:09:49 +01:00
Denis Knauf f27600ce16 Gemfile updated, Rakefile/gemspec modernized. 2021-12-12 15:59:25 +01:00
Denis Knauf 8698a3b01d Regenerate gemspec for version 0.3.0 2013-03-14 16:26:43 +01:00
Denis Knauf 2f108b5724 Version bump to 0.3.0 2013-03-14 16:26:28 +01:00
Denis Knauf 9f993da845 timeout(0) -> never timeout (compatible to Timeout) 2013-03-14 16:26:07 +01:00
Denis Knauf 0a0f47650e Regenerate gemspec for version 0.2.2 2013-03-07 14:50:29 +01:00
Denis Knauf 4c61e68354 Version bump to 0.2.2 2013-03-07 14:50:18 +01:00
Denis Knauf ddee6b499f License changed (LICENSE.txt was LGPLv3, but Gem not) 2013-03-07 14:50:05 +01:00
Denis Knauf 30ec1b45d4 Regenerate gemspec for version 0.2.1 2013-03-07 14:48:18 +01:00
Denis Knauf 6ab9ad2d13 Version bump to 0.2.1 2013-03-07 14:48:03 +01:00
Denis Knauf ae0409111a :doc:. alarm_trap will call raise_if_sb_timed_out and setup. raise_if_sb_timed_out is the old alarm_trap (but alarm_trap should be used like before). setup recognize time outs lesser than one second now. 2013-03-07 14:47:48 +01:00
Denis Knauf 97438aa9ec Regenerate gemspec for version 0.2.0 2013-03-07 12:32:34 +01:00
Denis Knauf 6472fcc07d Version bump to 0.2.0 2013-03-07 12:32:09 +01:00
Denis Knauf 6f57bb4223 Classes
=======

TimeoutInterruptSingleton created for helper methods like setup and so on.

TimeoutInterrupt#timeout and TimeoutInterrupt.timeout are stubs for calling timeout of singleton-class.

Scopeable
=========

`TimeoutInterrupt.timeout` should be scopeable now.
`TimeoutInterrupt.timeout` without args will check timeouts and will raise the next timed out timeout,
needed for scopes and if many time outs at the same time occurs.
2013-03-07 12:30:07 +01:00
11 changed files with 409 additions and 294 deletions

View File

@ -1,5 +0,0 @@
lib/**/*.rb
bin/*
-
features/**/*.feature
LICENSE.txt

37
.gitignore vendored
View File

@ -1,49 +1,18 @@
# rcov generated
coverage
coverage.data
# rdoc generated
rdoc
# yard generated
doc
.yardoc
# bundler
.bundle
# jeweler generated
Gemfile.lock
pkg
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
#
# * Create a file at ~/.gitignore
# * Include files you want ignored
# * Run: git config --global core.excludesfile ~/.gitignore
#
# After doing this, these files will be ignored in all your git projects,
# saving you from having to 'pollute' every project you touch with them
#
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
#
# For MacOS:
#
.DS_Store
# For TextMate
*.tmproj
tmtags
# For emacs:
*~
\#*
.\#*
# For vim:
*.swp
# For redcar:
*.sw[p-n]
.redcar
# For rubinius:
*.rbc
.document

16
Gemfile
View File

@ -1,14 +1,2 @@
source "http://rubygems.org"
gem 'ffi-libc'
# Add dependencies to develop your gem here.
# Include everything needed to run rake, tests, features, etc.
group :development do
gem "shoulda"
gem "yard"
gem "rdoc"
gem "bundler"
gem "jeweler"
gem "simplecov"
end
source "https://rubygems.org"
gemspec

View File

@ -1,50 +0,0 @@
GEM
remote: http://rubygems.org/
specs:
activesupport (3.2.12)
i18n (~> 0.6)
multi_json (~> 1.0)
bourne (1.1.2)
mocha (= 0.10.5)
ffi (1.1.0)
ffi-libc (0.0.5)
ffi (>= 0.6.0, <= 1.1.0)
git (1.2.5)
i18n (0.6.4)
jeweler (1.8.4)
bundler (~> 1.0)
git (>= 1.2.5)
rake
rdoc
json (1.7.7)
metaclass (0.0.1)
mocha (0.10.5)
metaclass (~> 0.0.1)
multi_json (1.6.1)
rake (10.0.3)
rdoc (4.0.0)
json (~> 1.4)
shoulda (3.3.2)
shoulda-context (~> 1.0.1)
shoulda-matchers (~> 1.4.1)
shoulda-context (1.0.2)
shoulda-matchers (1.4.2)
activesupport (>= 3.0.0)
bourne (~> 1.1.2)
simplecov (0.7.1)
multi_json (~> 1.0)
simplecov-html (~> 0.7.1)
simplecov-html (0.7.1)
yard (0.8.5.2)
PLATFORMS
ruby
DEPENDENCIES
bundler
ffi-libc
jeweler
rdoc
shoulda
simplecov
yard

View File

@ -1,17 +1,74 @@
timeout-interrupt
=================
Works like ruby's timeout, but interrupts every call, also syscalls, which blocks the hole ruby-process.
Works like ruby's timeout, but interrupts *every call*, also syscalls, which blocks the hole ruby-process.
It uses POSIX's alarm and traps ALRM-signals.
Known limitations bacause of alarm and ALRM are, that you can not use alarm or trap ALRM.
Known limitations bacause of alarm and ALRM are, that you can not use alarm or trap ALRM in the same time.
Do not forget, syscall can have allocated memory.
If you interrupt a call, which can not free his allocations, you will have a memory leak.
So, use it only, if your process did not live any longer or if you call something, which never allocate mem
Scopes
======
If you need scopes with inner and outer time outs, you should know:
The first timed out Timeout will be raised:
include TimeoutInterrupt
timeout(1) { # Will be raised
timeout(10) { sleep 2 } # Will not be raised
}
If you want to know, which was raised, you need custom exceptions:
class CustomErrorWillBeRaised <Exception
end
class CustomErrorNotRaised <Exception
end
include TimeoutInterrupt
timeout( 1, CustomErrorWillBeRaised) { # Will be raised again
timeout( 10, CustomErrorNotRaised) { sleep 2 } # Will not be raised
}
Problems
========
Memory-Leaks and missing clean up
---------------------------------
Syscalls can allocate memory.
If you interrupt a syscall, which then cannot free his allocations, it will result in a memory leak.
If it opens a file, while it reads, a time out occurs, the file will not automatically be closed.
So, you should only use it, if your process will die after interrupt or if you are sure, that your call never allocate memory or opens a file.
You could also publish informations about opened files, that it could be cleaned up later.
Every time, a process dies, all his memory will be freed and every file will be closed, so let your process die and you should be safe.
Exception-handling
------------------
Timeouts can break your exception-handling! You should not handling exception while you wait for a timeout:
include TimeoutInterrupt
timeout(1) {
begin
transaction_begin
do_something
ensure
clean_up
transaction_end
end
}
Same happens, if clean\_up will raise an exception, so handle it in the same way.
And same problem you have with ruby's `Timeout.timeout`.
Copyleft
=========
Copyright (c) 2013 Denis Knauf. See LICENSE.txt for further details.
Copyright (c) 2021 Denis Knauf. See LICENSE.txt for further details.

View File

@ -1,46 +1,9 @@
# encoding: utf-8
require 'rubygems'
require 'bundler'
begin
Bundler.setup(:default, :development)
rescue Bundler::BundlerError => e
$stderr.puts e.message
$stderr.puts "Run `bundle install` to install missing gems"
exit e.status_code
end
require 'rake'
require 'jeweler'
Jeweler::Tasks.new do |gem|
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
gem.name = "timeout-interrupt"
gem.homepage = "http://github.com/DenisKnauf/ruby-timeout-interrupt"
gem.license = "AGPLv3"
gem.summary = %Q{"Interrupts systemcalls too."}
gem.description = %Q{Timeout-lib, which interrupts everything, also systemcalls. It uses libc-alarm.}
gem.email = "Denis.Knauf@gmail.com"
gem.authors = ["Denis Knauf"]
# dependencies defined in Gemfile
end
Jeweler::RubygemsDotOrgTasks.new
require "bundler/gem_tasks"
task :default => :spec
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
Rake::TestTask.new :test do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/test_*.rb'
test.verbose = true
end
#require 'simplecov'
#Rcov::RcovTask.new do |test|
#test.libs << 'test'
#test.pattern = 'test/**/test_*.rb'
#test.verbose = true
#test.rcov_opts << '--exclude "gems/*"'
#end
task :default => :test
require 'yard'
YARD::Rake::YardocTask.new

View File

@ -1 +1 @@
0.1.1
0.3.0

View File

@ -1,49 +1,195 @@
require 'ffi/libc'
require 'timeout'
module FFI
module LibC
attach_function :alarm, [:uint], :uint unless FFI::LibC.respond_to? :alarm
# Helper module for `TimeoutInterrupt`
# @see TimeoutInterrupt
module TimeoutInterruptSingleton
class <<self
# Stores all timeouts.
#
# @param thread [nil] must be nil! Do not use it yet!
# @return [Hash< key(Integer): [at(Time), backtrace(Array<String>), exception(Exception)] >]
def timeouts thread = nil
@timeouts ||= Hash.new {|h,k| h[k] = {} }
thread = Thread.current unless thread.kind_of? Thread
thread ? @timeouts[thread] : @timeouts
end
# 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.
#
# @return [nil]
def alarm_trap sig
raise_if_sb_timed_out
setup
end
# There is a timed out timeout? It will raise it!
# You need not to check it yourself, it will do it for you.
#
# @return [nil]
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
# Prepares the next timeout. Sets the trap and the shortest timeout as alarm.
#
# @return [nil]
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
# Creates a timeout and calls your block, which has to finish before timeout occurs.
#
# @param seconds [0] No timeout, so block can take any time.
# @param seconds [Integer] In `seconds` Seconds, it should raise a timeout, if not finished.
# @param seconds [nil] If this and no block given, it will call {setup} for checking and
# preparing _next_ known timeout.
# @param exception [exception] which exception will be raised if timed out?
# @param exception [nil] `TimeoutInterrupt::Error` will be used to raise.
# @param block [Proc] Will be called and should finish its work before it timed out.
# @param block [nil] Nothing will happen, instead it will return a Proc,
# which can be called with a block to use the timeout.
# @return 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.
#
# You can rescue `Timeout::Error`, instead `TimeoutInterrupt::Error`,
# it is a subclass of `Timeout::Error`.
#
# 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.
#
# @see TimeoutInterrupt.timeout
# @see TimeoutInterrupt#timeout
# @raise exception
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
end
end
# Can be included, or used directly.
# In both cases, it provides {#timeout}.
#
# @see TimeoutInterruptSingleton
module TimeoutInterrupt
def self.timeouts
@timeouts ||= {}
# The {TimeoutInterrupt::Error} is the default exception, which will be raised,
# if something will time out.
# Its base-class is {Timeout::Error}, so you can replace {Timeout} by {TimeoutInterrupt} without
# replacing your `rescue Timeout::Error`, but you can.
class Error < Timeout::Error
end
def self.alarm_trap sig
key, (at, bt) = TimeoutInterrupt.timeouts.min_by {|key,(at,bt)| at }
return if Time.now < at
raise Timeout::Error, 'execution expired', bt
# Creates a timeout and calls your block, which has to finish before timeout occurs.
#
# @param seconds [0] No timeout, so block can take any time.
# @param seconds [Integer] In `seconds` Seconds, it should raise a timeout, if not finished.
# @param seconds [nil] If also no block given, everything will be ignored and
# it will call {setup} for checking and preparing next known timeout.
# @param exception [Exception] which will be raised if timed out.
# @param exception [nil] `TimeoutInterrupt::Error` will be used to raise.
# @param block [Proc] Will be called and should finish its work before it timed out.
# @param block [nil] Nothing will happen, instead it will return a Proc,
# which can be called with a block to use the timeout.
# @return 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.
#
# 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.
#
# @see TimeoutInterrupt#timeout
# @see TimeoutInterruptSingleton.timeout
# @raise exception
def self.timeout seconds = nil, exception = nil, &block
TimeoutInterruptSingleton.timeout seconds, exception, &block
end
def self.setup_timeout
if TimeoutInterrupt.timeouts.empty?
Signal.trap( 'ALRM') {}
FFI::LibC.alarm 0
else
key, (at, bt) = TimeoutInterrupt.timeouts.min_by {|key,(at,bt)| at }
secs = (at - Time.now).to_i+1
TimeoutInterrupt.alarm_trap if 1 > secs
Signal.trap 'ALRM', &TimeoutInterrupt.method( :alarm_trap)
FFI::LibC.alarm secs
end
end
def self.timeout seconds
seconds = seconds.to_i
raise Timeout::Error, "Timeout must be longer than '0' seconds." unless 0 < seconds
return lambda {|&e| self.timeout seconds, &e } unless block_given?
at = Time.now + seconds
key, bt = Random.rand( 2**64-1), Kernel.caller
begin
TimeoutInterrupt.timeouts[key] = [at, bt]
TimeoutInterrupt.setup_timeout
yield
ensure
TimeoutInterrupt.timeouts.delete key
TimeoutInterrupt.setup_timeout
end
# Creates a timeout and calls your block, which has to finish before timeout occurs.
#
# @param seconds [0] No timeout, so block can take any time.
# @param seconds [Integer] In `seconds` Seconds, it should raise a timeout, if not finished.
# @param seconds [nil] If also no block given, everything will be ignored and
# it will call {setup} for checking and preparing next known timeout.
# @param exception [Exception] which will be raised if timed out.
# @param exception [nil] `TimeoutInterrupt::Error` will be used to raise.
# @param block [Proc] Will be called and should finish its work before it timed out.
# @param block [nil] Nothing will happen, instead it will return a Proc,
# which can be called with a block to use the timeout.
# @return 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.
#
# 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.
#
# @note This method is useful, if you `include TimeoutInterrupt`. You can call it directly.
# @see TimeoutInterrupt.timeout
# @see TimeoutInterruptSingleton.timeout
# @raise exception
def timeout seconds = nil, exception = nil, &block
TimeoutInterruptSingleton.timeout seconds, exception, &block
end
end

View File

@ -1,5 +1,6 @@
require 'rubygems'
require 'bundler'
begin
Bundler.setup(:default, :development)
rescue Bundler::BundlerError => e
@ -7,6 +8,7 @@ rescue Bundler::BundlerError => e
$stderr.puts "Run `bundle install` to install missing gems"
exit e.status_code
end
require 'test/unit'
require 'shoulda'
@ -14,9 +16,6 @@ require 'timeout'
require 'benchmark'
require 'ffi/libc'
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'timeout_interrupt'
class Test::Unit::TestCase

View File

@ -4,77 +4,159 @@ class TestRubyTimeoutInterrupt < Test::Unit::TestCase
def blocking
t = FFI::LibC.fopen '/dev/ptmx', 'r'
b = FFI::LibC.malloc 1025
s = FFI::LibC.fread b, 1, 1024, t
FFI::LibC.fread b, 1, 1024, t
ensure
FFI::LibC.fclose t if t
FFI::LibC.free b if b
end
def assert_no_defined_timeout_yet
assert TimeoutInterrupt.timeouts.empty?, "For testing, no timeout should be defined, yet!"
assert TimeoutInterruptSingleton.timeouts.empty?, "For testing, no timeout should be defined, yet!"
end
should "not interrupt a long blocking call with the old Timeout" do
time = Benchmark.realtime do
begin
TimeoutInterrupt.timeout(5) do
Timeout.timeout(1) do
def print_timeouts pre
puts "#{pre}: < #{TimeoutInterruptSingleton.timeouts.map {|k,(a,_b,_e)| "#{k.inspect}: #{a.strftime '%H:%M:%S'} (#{a-Time.now})" }.join ', '} >"
end
# For testing raising scoped Timeout.
class TimeoutError < Exception
end
# For testing raising scoped TimeoutInterrupt.
class TimeoutInterruptError < Exception
end
context "Long really blocking calls" do
should "not be interrupted by the old Timeout" do
time = Benchmark.realtime do
assert_nothing_raised TimeoutError, "Unexpected time out. Your Ruby implementation can time out with old Timeout? You need not TimeoutInterrupt. But it is ok. You can ignore this Error. :)" do
assert_raise TimeoutInterruptError, "Ohoh. TimeoutInterrupt should be raised." do
TimeoutInterrupt.timeout 5, TimeoutInterruptError do
Timeout.timeout 1, TimeoutError do
blocking
assert false, "Should be unreachable!"
end
end
end
end
end
assert 3 < time, "Did timeout!"
end
should "be interrupted by the new TimeoutInterrupt" do
time = Benchmark.realtime do
assert_raise TimeoutInterrupt::Error, "It should be timed out, why it did not raise TimeoutInterrupt::Error?" do
TimeoutInterrupt.timeout 1 do
blocking
assert false, "Should be unreachable!"
end
end
rescue Timeout::Error
:ok
end
assert 3 > time, "Did not interrupt."
end
assert 3 < time, "Did timeout!"
end
should "interrupt a long blocking call with the new TimeoutInterrupt" do
time = Benchmark.realtime do
begin
TimeoutInterrupt.timeout(1) do
blocking
should "interrupt scoped timeout, but not time out the outer timeout" do
assert_no_defined_timeout_yet
assert_raise TimeoutInterruptError, "It should be timed out, why it did not raise TimeoutInterruptError?" do
assert_nothing_raised Timeout::Error, "Oh, outer timeout was timed out. Your machine must be slow, or there is a bug" do
TimeoutInterrupt.timeout 10 do
TimeoutInterrupt.timeout 1, TimeoutInterruptError do
Kernel.sleep 2
end
assert false, "Should be unreachable!"
end
rescue Timeout::Error
:ok
end
end
assert 3 > time, "Did not interrupt."
end
should "interrupt scoped timeout, but not outer timeout" do
assert_no_defined_timeout_yet
begin
TimeoutInterrupt.timeout(10) do
TimeoutInterrupt.timeout(1) do
Kernel.sleep 2
end
assert false, "Should be unreachable!"
end
rescue Timeout::Error
:ok
end
assert TimeoutInterrupt.timeouts.empty?, "There are timeouts defined, yet!"
assert TimeoutInterruptSingleton.timeouts.empty?, "There are timeouts defined, yet!"
end
should "clear timeouts, if not timed out, too." do
assert_no_defined_timeout_yet
TimeoutInterrupt.timeout(10) {}
assert TimeoutInterrupt.timeouts.empty?, "There are timeouts defined, yet!"
assert TimeoutInterruptSingleton.timeouts.empty?, "There are timeouts defined, yet!"
end
should "return a Proc if now block given, but do not create a timeout." do
assert_no_defined_timeout_yet
assert TimeoutInterrupt.timeout(10).kind_of?( Proc), "Did not return a Proc."
class CustomException <Exception
end
should "run a returned Proc with given timeout." do
assert_no_defined_timeout_yet
to = TimeoutInterrupt.timeout(10)
called = false
to.call { called = true }
assert called, "Did not called."
should "raise custom exception." do
assert_raise CustomException, "Custom exceptions do not work." do
TimeoutInterrupt.timeout 1, CustomException do
sleep 2
end
end
end
context "A prepared timeout (Proc)" do
should "be returned by calling timeout without a block" do
assert_no_defined_timeout_yet
assert TimeoutInterrupt.timeout(10).kind_of?( Proc), "Did not return a Proc."
end
should "run with once given timeout" do
assert_no_defined_timeout_yet
to = TimeoutInterrupt.timeout 10
called = false
to.call { called = true }
assert called, "Did not called."
end
should "raise custom exception" do
assert_raise CustomException, "Custom exceptions do not work." do
prepared = TimeoutInterrupt.timeout 1, CustomException
prepared.call { sleep 2 }
end
end
should "not be scopeable, without manualy setup after rescue and 2 time outs at once" do
prepared = TimeoutInterrupt.timeout 1
assert_no_defined_timeout_yet
called = false
prepared.call do
assert_raise TimeoutInterrupt::Error, 'It should time out after one second, but it did not.' do
prepared.call { _ = 2; sleep 2 }
end
called = true
end
assert called, "It's true, it should be called, also if not expected."
end
should "be scopeable, with manualy setup after rescue, also if 2 time outs at once." do
prepared = TimeoutInterrupt.timeout 1
assert_no_defined_timeout_yet
prepared.call do
assert_raise TimeoutInterrupt::Error, 'It should time out after one second, but it did not.' do
prepared.call { sleep 2 }
end
assert_raise TimeoutInterrupt::Error, 'Manualy called timeout setup did not raise.' do
TimeoutInterrupt.timeout
end
assert true, "Should never be reached."
end
end
end
class IncludeModuleTest
include TimeoutInterrupt
def please_timeout after
timeout after do
sleep after+10
end
end
end
context "Included module" do
should "provide timeout too" do
assert_raise TimeoutInterrupt::Error, "Included timeout can not be used?" do
IncludeModuleTest.new.please_timeout 2
end
end
end
should "not timeout, if timeout is 0" do
assert_nothing_raised TimeoutInterrupt::Error, "Unexpected Timed out." do
# should never timeout (we can not wait infinity seconds, so only 5)
TimeoutInterrupt.timeout( 0) { sleep 5 }
end
end
end

View File

@ -1,67 +1,33 @@
# Generated by jeweler
# DO NOT EDIT THIS FILE DIRECTLY
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
# -*- encoding: utf-8 -*-
Gem::Specification.new do |spec|
spec.name = "timeout-interrupt"
spec.version = "0.4.0"
Gem::Specification.new do |s|
s.name = "timeout-interrupt"
s.version = "0.1.1"
spec.authors = ["Denis Knauf"]
spec.description = "Timeout-lib, which interrupts everything, also systemcalls. It uses libc-alarm."
spec.email = ["git+timeout-interrupt@denkn.at"]
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Denis Knauf"]
s.date = "2013-03-06"
s.description = "Timeout-lib, which interrupts everything, also systemcalls. It uses libc-alarm."
s.email = "Denis.Knauf@gmail.com"
s.extra_rdoc_files = [
"LICENSE.txt",
"README.md"
]
s.files = [
".document",
"Gemfile",
"Gemfile.lock",
"LICENSE.txt",
"README.md",
"Rakefile",
"VERSION",
"lib/timeout_interrupt.rb",
"test/helper.rb",
"test/test_ruby-timeout-interrupt.rb"
]
s.homepage = "http://github.com/DenisKnauf/ruby-timeout-interrupt"
s.licenses = ["AGPLv3"]
s.require_paths = ["lib"]
s.rubygems_version = "1.8.11"
s.summary = "\"Interrupts systemcalls too.\""
spec.summary = "\"Interrupts systemcalls too.\""
spec.licenses = ["LGPLv3"]
if s.respond_to? :specification_version then
s.specification_version = 3
spec.homepage = "https://git.denkn.at/deac/ruby-timeout-interrupt"
spec.required_ruby_version = Gem::Requirement.new(">= 2.1.0")
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<ffi-libc>, [">= 0"])
s.add_development_dependency(%q<shoulda>, [">= 0"])
s.add_development_dependency(%q<yard>, [">= 0"])
s.add_development_dependency(%q<rdoc>, [">= 0"])
s.add_development_dependency(%q<bundler>, [">= 0"])
s.add_development_dependency(%q<jeweler>, [">= 0"])
s.add_development_dependency(%q<simplecov>, [">= 0"])
else
s.add_dependency(%q<ffi-libc>, [">= 0"])
s.add_dependency(%q<shoulda>, [">= 0"])
s.add_dependency(%q<yard>, [">= 0"])
s.add_dependency(%q<rdoc>, [">= 0"])
s.add_dependency(%q<bundler>, [">= 0"])
s.add_dependency(%q<jeweler>, [">= 0"])
s.add_dependency(%q<simplecov>, [">= 0"])
end
else
s.add_dependency(%q<ffi-libc>, [">= 0"])
s.add_dependency(%q<shoulda>, [">= 0"])
s.add_dependency(%q<yard>, [">= 0"])
s.add_dependency(%q<rdoc>, [">= 0"])
s.add_dependency(%q<bundler>, [">= 0"])
s.add_dependency(%q<jeweler>, [">= 0"])
s.add_dependency(%q<simplecov>, [">= 0"])
end
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
spec.metadata["changelog_uri"] = spec.homepage
# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
end
spec.bindir = "bin"
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
spec.add_runtime_dependency 'ffi-libc', '>= 0.1.1'
spec.add_development_dependency 'test-unit'
spec.add_development_dependency 'shoulda'
spec.add_development_dependency 'rake'
spec.add_development_dependency 'bundler'
end