Compare commits
7 commits
Author | SHA1 | Date | |
---|---|---|---|
f963e41c52 | |||
|
5b390b2083 | ||
|
3542837d7f | ||
|
28936187de | ||
|
486cf53495 | ||
|
ae8f1e0a33 | ||
|
3ca2de0cd9 |
10 changed files with 44 additions and 105 deletions
|
@ -1,5 +0,0 @@
|
||||||
lib/**/*.rb
|
|
||||||
bin/*
|
|
||||||
-
|
|
||||||
features/**/*.feature
|
|
||||||
LICENSE.txt
|
|
37
.gitignore
vendored
37
.gitignore
vendored
|
@ -1,49 +1,18 @@
|
||||||
# rcov generated
|
|
||||||
coverage
|
coverage
|
||||||
coverage.data
|
coverage.data
|
||||||
|
|
||||||
# rdoc generated
|
|
||||||
rdoc
|
rdoc
|
||||||
|
|
||||||
# yard generated
|
|
||||||
doc
|
doc
|
||||||
.yardoc
|
.yardoc
|
||||||
|
|
||||||
# bundler
|
|
||||||
.bundle
|
.bundle
|
||||||
|
Gemfile.lock
|
||||||
# jeweler generated
|
|
||||||
pkg
|
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
|
.DS_Store
|
||||||
|
|
||||||
# For TextMate
|
|
||||||
*.tmproj
|
*.tmproj
|
||||||
tmtags
|
tmtags
|
||||||
|
|
||||||
# For emacs:
|
|
||||||
*~
|
*~
|
||||||
\#*
|
\#*
|
||||||
.\#*
|
.\#*
|
||||||
|
*.sw[p-n]
|
||||||
# For vim:
|
|
||||||
*.swp
|
|
||||||
|
|
||||||
# For redcar:
|
|
||||||
.redcar
|
.redcar
|
||||||
|
|
||||||
# For rubinius:
|
|
||||||
*.rbc
|
*.rbc
|
||||||
|
.document
|
||||||
|
|
1
Gemfile
1
Gemfile
|
@ -1,3 +1,2 @@
|
||||||
source "https://rubygems.org"
|
source "https://rubygems.org"
|
||||||
gemspec
|
gemspec
|
||||||
gem "rake", "~> 12"
|
|
||||||
|
|
38
Gemfile.lock
38
Gemfile.lock
|
@ -1,38 +0,0 @@
|
||||||
PATH
|
|
||||||
remote: .
|
|
||||||
specs:
|
|
||||||
timeout-interrupt (0.4.0)
|
|
||||||
ffi-libc
|
|
||||||
|
|
||||||
GEM
|
|
||||||
remote: https://rubygems.org/
|
|
||||||
specs:
|
|
||||||
diff-lcs (1.4.4)
|
|
||||||
ffi (1.15.4)
|
|
||||||
ffi-libc (0.1.1)
|
|
||||||
ffi (~> 1.0)
|
|
||||||
rake (12.3.3)
|
|
||||||
rspec (3.10.0)
|
|
||||||
rspec-core (~> 3.10.0)
|
|
||||||
rspec-expectations (~> 3.10.0)
|
|
||||||
rspec-mocks (~> 3.10.0)
|
|
||||||
rspec-core (3.10.1)
|
|
||||||
rspec-support (~> 3.10.0)
|
|
||||||
rspec-expectations (3.10.1)
|
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
|
||||||
rspec-support (~> 3.10.0)
|
|
||||||
rspec-mocks (3.10.2)
|
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
|
||||||
rspec-support (~> 3.10.0)
|
|
||||||
rspec-support (3.10.3)
|
|
||||||
|
|
||||||
PLATFORMS
|
|
||||||
ruby
|
|
||||||
|
|
||||||
DEPENDENCIES
|
|
||||||
rake (~> 12)
|
|
||||||
rspec (~> 3.2)
|
|
||||||
timeout-interrupt!
|
|
||||||
|
|
||||||
BUNDLED WITH
|
|
||||||
2.2.25
|
|
26
README.md
26
README.md
|
@ -1,11 +1,12 @@
|
||||||
timeout-interrupt
|
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.
|
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.
|
||||||
|
|
||||||
|
|
||||||
Scopes
|
Scopes
|
||||||
======
|
======
|
||||||
|
@ -23,27 +24,30 @@ If you want to know, which was raised, you need custom exceptions:
|
||||||
|
|
||||||
class CustomErrorWillBeRaised <Exception
|
class CustomErrorWillBeRaised <Exception
|
||||||
end
|
end
|
||||||
class CustomErrorNotRaise <Exception
|
class CustomErrorNotRaised <Exception
|
||||||
end
|
end
|
||||||
include TimeoutInterrupt
|
include TimeoutInterrupt
|
||||||
timeout( 1, CustomErrorWillBeRaised) { # Will be raised again
|
timeout( 1, CustomErrorWillBeRaised) { # Will be raised again
|
||||||
timeout( 10, CustomErrorNotRaise) { sleep 2 } # Will not be raised
|
timeout( 10, CustomErrorNotRaised) { sleep 2 } # Will not be raised
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Problems
|
Problems
|
||||||
========
|
========
|
||||||
|
|
||||||
Memory-Leaks or no clean up
|
Memory-Leaks and missing clean up
|
||||||
---------------------------
|
---------------------------------
|
||||||
|
|
||||||
Do not forget, syscall can have allocated memory.
|
Syscalls can allocate memory.
|
||||||
If you interrupt a call, which can not free his allocations, you will have a memory leak.
|
If you interrupt a syscall, which then cannot free his allocations, it will result in a memory leak.
|
||||||
If it opens a file, reads it and closes it and while it reads, a time out occurs, the file will not be closed.
|
If it opens a file, while it reads, a time out occurs, the file will not automatically be closed.
|
||||||
|
|
||||||
So, use it only, if your process did not live any longer or if you call something, which never allocate mem or opens a file.
|
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.
|
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
|
Exception-handling
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@ -60,7 +64,7 @@ Timeouts can break your exception-handling! You should not handling exception wh
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
Same happens, if clean\_up will raise an exception.
|
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`.
|
And same problem you have with ruby's `Timeout.timeout`.
|
||||||
|
|
||||||
|
|
7
Rakefile
7
Rakefile
|
@ -1,2 +1,9 @@
|
||||||
require "bundler/gem_tasks"
|
require "bundler/gem_tasks"
|
||||||
task :default => :spec
|
task :default => :spec
|
||||||
|
|
||||||
|
require 'rake/testtask'
|
||||||
|
Rake::TestTask.new :test do |test|
|
||||||
|
test.libs << 'lib' << 'test'
|
||||||
|
test.pattern = 'test/**/test_*.rb'
|
||||||
|
test.verbose = true
|
||||||
|
end
|
||||||
|
|
|
@ -34,7 +34,7 @@ module TimeoutInterruptSingleton
|
||||||
# @return [nil]
|
# @return [nil]
|
||||||
def raise_if_sb_timed_out
|
def raise_if_sb_timed_out
|
||||||
return if self.timeouts.empty?
|
return if self.timeouts.empty?
|
||||||
key, (at, bt, exception) = self.timeouts.min_by {|key,(at,bt,ex)| at }
|
_key, (at, bt, exception) = self.timeouts.min_by {|_key,(at,_bt,_ex)| at }
|
||||||
return if Time.now < at
|
return if Time.now < at
|
||||||
raise exception, 'execution expired', bt
|
raise exception, 'execution expired', bt
|
||||||
end
|
end
|
||||||
|
@ -49,7 +49,7 @@ module TimeoutInterruptSingleton
|
||||||
else
|
else
|
||||||
raise_if_sb_timed_out
|
raise_if_sb_timed_out
|
||||||
Signal.trap 'ALRM', &method( :alarm_trap)
|
Signal.trap 'ALRM', &method( :alarm_trap)
|
||||||
key, (at, bt) = timeouts.min_by {|key,(at,bt)| at }
|
_key, (at, _bt) = timeouts.min_by {|_key,(at,_bt)| at }
|
||||||
FFI::LibC.alarm (at - Time.now).to_i + 1
|
FFI::LibC.alarm (at - Time.now).to_i + 1
|
||||||
end
|
end
|
||||||
nil
|
nil
|
||||||
|
@ -59,9 +59,9 @@ module TimeoutInterruptSingleton
|
||||||
#
|
#
|
||||||
# @param seconds [0] No timeout, so block can take any time.
|
# @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 [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
|
# @param seconds [nil] If this and no block given, it will call {setup} for checking and
|
||||||
# it will call {setup} for checking and preparing next known timeout.
|
# preparing _next_ known timeout.
|
||||||
# @param exception [Exception] which will be raised if timed out.
|
# @param exception [exception] which exception will be raised if timed out?
|
||||||
# @param exception [nil] `TimeoutInterrupt::Error` will be used to raise.
|
# @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 [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,
|
# @param block [nil] Nothing will happen, instead it will return a Proc,
|
||||||
|
@ -70,7 +70,8 @@ module TimeoutInterruptSingleton
|
||||||
# Or if not, it will return a Proc, which will expect a Proc if called.
|
# 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.
|
# 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.
|
# 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.
|
# 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,
|
# If you want to prepare a timeout, which should be used many times,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
require 'bundler'
|
require 'bundler'
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Bundler.setup(:default, :development)
|
Bundler.setup(:default, :development)
|
||||||
rescue Bundler::BundlerError => e
|
rescue Bundler::BundlerError => e
|
||||||
|
@ -7,6 +8,7 @@ rescue Bundler::BundlerError => e
|
||||||
$stderr.puts "Run `bundle install` to install missing gems"
|
$stderr.puts "Run `bundle install` to install missing gems"
|
||||||
exit e.status_code
|
exit e.status_code
|
||||||
end
|
end
|
||||||
|
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require 'shoulda'
|
require 'shoulda'
|
||||||
|
|
||||||
|
@ -14,9 +16,6 @@ require 'timeout'
|
||||||
require 'benchmark'
|
require 'benchmark'
|
||||||
require 'ffi/libc'
|
require 'ffi/libc'
|
||||||
|
|
||||||
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
||||||
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
||||||
|
|
||||||
require 'timeout_interrupt'
|
require 'timeout_interrupt'
|
||||||
|
|
||||||
class Test::Unit::TestCase
|
class Test::Unit::TestCase
|
||||||
|
|
|
@ -4,7 +4,7 @@ class TestRubyTimeoutInterrupt < Test::Unit::TestCase
|
||||||
def blocking
|
def blocking
|
||||||
t = FFI::LibC.fopen '/dev/ptmx', 'r'
|
t = FFI::LibC.fopen '/dev/ptmx', 'r'
|
||||||
b = FFI::LibC.malloc 1025
|
b = FFI::LibC.malloc 1025
|
||||||
s = FFI::LibC.fread b, 1, 1024, t
|
FFI::LibC.fread b, 1, 1024, t
|
||||||
ensure
|
ensure
|
||||||
FFI::LibC.fclose t if t
|
FFI::LibC.fclose t if t
|
||||||
FFI::LibC.free b if b
|
FFI::LibC.free b if b
|
||||||
|
@ -114,7 +114,7 @@ class TestRubyTimeoutInterrupt < Test::Unit::TestCase
|
||||||
called = false
|
called = false
|
||||||
prepared.call do
|
prepared.call do
|
||||||
assert_raise TimeoutInterrupt::Error, 'It should time out after one second, but it did not.' do
|
assert_raise TimeoutInterrupt::Error, 'It should time out after one second, but it did not.' do
|
||||||
prepared.call { 2; sleep 2 }
|
prepared.call { _ = 2; sleep 2 }
|
||||||
end
|
end
|
||||||
called = true
|
called = true
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,8 @@ Gem::Specification.new do |spec|
|
||||||
|
|
||||||
spec.authors = ["Denis Knauf"]
|
spec.authors = ["Denis Knauf"]
|
||||||
spec.description = "Timeout-lib, which interrupts everything, also systemcalls. It uses libc-alarm."
|
spec.description = "Timeout-lib, which interrupts everything, also systemcalls. It uses libc-alarm."
|
||||||
|
spec.email = ["git+timeout-interrupt@denkn.at"]
|
||||||
|
|
||||||
spec.summary = "\"Interrupts systemcalls too.\""
|
spec.summary = "\"Interrupts systemcalls too.\""
|
||||||
spec.licenses = ["LGPLv3"]
|
spec.licenses = ["LGPLv3"]
|
||||||
|
|
||||||
|
@ -14,8 +16,6 @@ Gem::Specification.new do |spec|
|
||||||
spec.metadata["source_code_uri"] = spec.homepage
|
spec.metadata["source_code_uri"] = spec.homepage
|
||||||
spec.metadata["changelog_uri"] = spec.homepage
|
spec.metadata["changelog_uri"] = spec.homepage
|
||||||
|
|
||||||
spec.add_development_dependency "rspec", "~> 3.2"
|
|
||||||
|
|
||||||
# Specify which files should be added to the gem when it is released.
|
# 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.
|
# 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
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
||||||
|
@ -24,7 +24,10 @@ Gem::Specification.new do |spec|
|
||||||
spec.bindir = "bin"
|
spec.bindir = "bin"
|
||||||
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
||||||
spec.require_paths = ["lib"]
|
spec.require_paths = ["lib"]
|
||||||
spec.require_paths = ["lib"]
|
|
||||||
|
|
||||||
spec.add_runtime_dependency %q<ffi-libc>, '>= 0.1.1'
|
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
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue