Compare commits

...

7 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
10 changed files with 44 additions and 105 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

View File

@ -1,3 +1,2 @@
source "https://rubygems.org"
gemspec
gem "rake", "~> 12"

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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,

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,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

View File

@ -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