Compare commits
7 Commits
Author | SHA1 | Date |
---|---|---|
Denis Knauf | f963e41c52 | |
Denis Knauf | 5b390b2083 | |
Denis Knauf | 3542837d7f | |
Denis Knauf | 28936187de | |
Denis Knauf | 486cf53495 | |
Denis Knauf | ae8f1e0a33 | |
Denis Knauf | 3ca2de0cd9 |
|
@ -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
|
||||
|
|
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
|
28
README.md
28
README.md
|
@ -1,11 +1,12 @@
|
|||
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.
|
||||
|
||||
|
||||
Scopes
|
||||
======
|
||||
|
@ -23,27 +24,30 @@ If you want to know, which was raised, you need custom exceptions:
|
|||
|
||||
class CustomErrorWillBeRaised <Exception
|
||||
end
|
||||
class CustomErrorNotRaise <Exception
|
||||
class CustomErrorNotRaised <Exception
|
||||
end
|
||||
include TimeoutInterrupt
|
||||
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
|
||||
========
|
||||
|
||||
Memory-Leaks or no clean up
|
||||
---------------------------
|
||||
Memory-Leaks and missing clean up
|
||||
---------------------------------
|
||||
|
||||
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.
|
||||
If it opens a file, reads it and closes it and while it reads, a time out occurs, the file will not be closed.
|
||||
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, 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.
|
||||
|
||||
|
||||
Exception-handling
|
||||
------------------
|
||||
|
||||
|
@ -60,11 +64,11 @@ Timeouts can break your exception-handling! You should not handling exception wh
|
|||
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`.
|
||||
|
||||
Copyleft
|
||||
=========
|
||||
|
||||
Copyright (c) 2021 Denis Knauf. See LICENSE.txt for further details.
|
||||
Copyright (c) 2021 Denis Knauf. See LICENSE.txt for further details.
|
7
Rakefile
7
Rakefile
|
@ -1,2 +1,9 @@
|
|||
require "bundler/gem_tasks"
|
||||
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]
|
||||
def raise_if_sb_timed_out
|
||||
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
|
||||
raise exception, 'execution expired', bt
|
||||
end
|
||||
|
@ -49,7 +49,7 @@ module TimeoutInterruptSingleton
|
|||
else
|
||||
raise_if_sb_timed_out
|
||||
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
|
||||
end
|
||||
nil
|
||||
|
@ -59,9 +59,9 @@ module TimeoutInterruptSingleton
|
|||
#
|
||||
# @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 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,
|
||||
|
@ -70,7 +70,8 @@ module TimeoutInterruptSingleton
|
|||
# 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.
|
||||
# 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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -4,7 +4,7 @@ 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
|
||||
|
@ -114,7 +114,7 @@ class TestRubyTimeoutInterrupt < Test::Unit::TestCase
|
|||
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 }
|
||||
prepared.call { _ = 2; sleep 2 }
|
||||
end
|
||||
called = true
|
||||
end
|
||||
|
|
|
@ -4,6 +4,8 @@ Gem::Specification.new do |spec|
|
|||
|
||||
spec.authors = ["Denis Knauf"]
|
||||
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.licenses = ["LGPLv3"]
|
||||
|
||||
|
@ -14,8 +16,6 @@ Gem::Specification.new do |spec|
|
|||
spec.metadata["source_code_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.
|
||||
# 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
|
||||
|
@ -24,7 +24,10 @@ Gem::Specification.new do |spec|
|
|||
spec.bindir = "bin"
|
||||
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue