Compare commits

...

7 commits

Author SHA1 Message Date
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
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

View file

@ -1,3 +1,2 @@
source "https://rubygems.org" source "https://rubygems.org"
gemspec 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 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,11 +64,11 @@ 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`.
Copyleft 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" 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

View file

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

View file

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

View file

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

View file

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