Compare commits

...

12 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
11 changed files with 80 additions and 258 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

17
Gemfile
View File

@ -1,15 +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 "redcarpet"
gem "rdoc"
gem "bundler"
gem "jeweler"
gem "simplecov"
end
source "https://rubygems.org"
gemspec

View File

@ -1,52 +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)
redcarpet (2.2.2)
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
redcarpet
shoulda
simplecov
yard

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) 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 = "LGPLv3"
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.2.2
0.3.0

View File

@ -1,23 +1,6 @@
require 'ffi/libc'
require 'timeout'
# Provided by ffi-libc-lib and extended by this library, if needed.
# Older version of ffi-libc does not provide {FFI::LibC.alarm}
module FFI
module LibC
# @!method alarm(seconds)
# Sets an alarm. After `seconds` it will send an ALRM-signal to this process.
#
# Predefined alarm will be reset and will forget.
# @note Older implementations of ffi-libc does not provide {alarm}, but we need it.
# So we detect, if it is not provided and attach it.
# @param seconds [0] Clears alarm.
# @param seconds [Integer] How many seconds should be waited, before ALRM-signal should be send?
# @return (nil)
attach_function :alarm, [:uint], :uint unless FFI::LibC.respond_to? :alarm
end
end
# Helper module for `TimeoutInterrupt`
# @see TimeoutInterrupt
module TimeoutInterruptSingleton
@ -51,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
@ -66,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
@ -74,10 +57,11 @@ module TimeoutInterruptSingleton
# 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] 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,
@ -86,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,
@ -102,6 +87,7 @@ module TimeoutInterruptSingleton
# @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
@ -117,7 +103,7 @@ module TimeoutInterruptSingleton
begin
self.timeouts[key] = [at, bt, exception]
setup
yield
yield seconds
ensure
self.timeouts.delete key
setup
@ -140,9 +126,10 @@ module TimeoutInterrupt
# 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] Everything will be ignored and
# it will call {TimeoutInterruptSingleton.setup} for checking and preparing next known timeout.
# @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.
@ -173,9 +160,10 @@ module TimeoutInterrupt
# 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] Everything will be ignored and
# it will call {TimeoutInterruptSingleton.setup} for checking and preparing next known timeout.
# @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.

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
@ -152,4 +152,11 @@ class TestRubyTimeoutInterrupt < Test::Unit::TestCase
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,71 +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.2.2"
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-07"
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",
"timeout-interrupt.gemspec"
]
s.homepage = "http://github.com/DenisKnauf/ruby-timeout-interrupt"
s.licenses = ["LGPLv3"]
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<redcarpet>, [">= 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<redcarpet>, [">= 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<redcarpet>, [">= 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