From 662a705e2780ed698847ea0eaa9a14ac94f28e28 Mon Sep 17 00:00:00 2001 From: Matthias Goldhoorn Date: Mon, 27 Apr 2015 11:32:20 +0200 Subject: [PATCH 01/67] Removed readline --- Manifest.txt | 1 - ext/utilrb/extconf.rb | 1 - ext/utilrb/readline.c | 52 ------------------------------------------ ext/utilrb/utilrb.cc | 2 -- lib/utilrb/common.rb | 2 -- lib/utilrb/readline.rb | 9 -------- manifest.xml | 1 - package.xml | 2 -- 8 files changed, 70 deletions(-) delete mode 100644 ext/utilrb/readline.c delete mode 100644 lib/utilrb/readline.rb diff --git a/Manifest.txt b/Manifest.txt index 768c665..a33e117 100644 --- a/Manifest.txt +++ b/Manifest.txt @@ -8,7 +8,6 @@ bm/allocation.rb bm/speed.rb ext/utilrb/extconf.rb ext/utilrb/proc.c -ext/utilrb/readline.c ext/utilrb/ruby_allocator.hh ext/utilrb/utilrb.cc ext/utilrb/value_set.cc diff --git a/ext/utilrb/extconf.rb b/ext/utilrb/extconf.rb index 5cc9f61..3f923d7 100644 --- a/ext/utilrb/extconf.rb +++ b/ext/utilrb/extconf.rb @@ -5,7 +5,6 @@ $LDFLAGS += " -module" end -$LDFLAGS += " -lreadline" if RUBY_VERSION < "1.9" $CFLAGS += " -DRUBY_IS_18" puts "not building with core source" diff --git a/ext/utilrb/readline.c b/ext/utilrb/readline.c deleted file mode 100644 index 8b037e5..0000000 --- a/ext/utilrb/readline.c +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include - -static VALUE readline_save_prompt(VALUE self) -{ - rl_save_prompt(); - return Qnil; -} - -static VALUE readline_message(VALUE self, VALUE msg) -{ - rl_message("%s", StringValuePtr(msg)); - rl_redisplay(); - return Qnil; -} - -static VALUE readline_print(VALUE self, VALUE msg) -{ - int need_hack = (rl_readline_state & RL_STATE_READCMD) > 0; - char *saved_line; - int saved_point; - if (need_hack) - { - saved_point = rl_point; - saved_line = rl_copy_text(0, rl_end); - rl_save_prompt(); - rl_replace_line("", 0); - rl_redisplay(); - } - - printf("%s", StringValuePtr(msg)); - - if (need_hack) - { - rl_restore_prompt(); - rl_replace_line(saved_line, 0); - rl_point = saved_point; - rl_redisplay(); - free(saved_line); - } - return Qnil; -} - -extern void Init_utilrb_readline() -{ - VALUE mReadline = rb_define_module("Readline"); - rb_define_singleton_method(mReadline, "save_prompt", readline_save_prompt, 0); - rb_define_singleton_method(mReadline, "message", readline_message, 1); - rb_define_singleton_method(mReadline, "print", readline_print, 1); -} - diff --git a/ext/utilrb/utilrb.cc b/ext/utilrb/utilrb.cc index c575b1a..815e6b7 100644 --- a/ext/utilrb/utilrb.cc +++ b/ext/utilrb/utilrb.cc @@ -55,7 +55,6 @@ static VALUE kernel_crash(VALUE klass) extern "C" void Init_value_set(); extern "C" void Init_weakref(VALUE mUtilrb); extern "C" void Init_proc(); -extern "C" void Init_utilrb_readline(); extern "C" void Init_utilrb() { @@ -75,6 +74,5 @@ extern "C" void Init_utilrb() Init_proc(); Init_value_set(); - Init_utilrb_readline(); } diff --git a/lib/utilrb/common.rb b/lib/utilrb/common.rb index 37ee091..3f54d98 100644 --- a/lib/utilrb/common.rb +++ b/lib/utilrb/common.rb @@ -15,8 +15,6 @@ module Utilrb STDERR.puts "Utilrb: not loading the C extension" else begin - # We need readline - require 'readline' require 'utilrb/utilrb' UTILRB_EXT_MODE = true STDERR.puts "Utilrb: loaded C extension" if ENV['UTILRB_EXT_MODE'] diff --git a/lib/utilrb/readline.rb b/lib/utilrb/readline.rb deleted file mode 100644 index c32f657..0000000 --- a/lib/utilrb/readline.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'utilrb/common' -Utilrb.require_ext("Readline#puts") do - module Readline - def self.puts(msg) - print("#{msg}\n") - end - end -end - diff --git a/manifest.xml b/manifest.xml index acdf5f2..4a81720 100644 --- a/manifest.xml +++ b/manifest.xml @@ -16,6 +16,5 @@ - diff --git a/package.xml b/package.xml index d5598aa..c20107f 100644 --- a/package.xml +++ b/package.xml @@ -15,12 +15,10 @@ facets hoe rake-compiler - libreadline-dev ruby facets hoe - libreadline From 01660daf9e3fab3d58bb6de854dcb6e50900908b Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Wed, 10 Jun 2015 22:26:34 -0300 Subject: [PATCH 02/67] more robust implementation of TCPServer#bound_addr and #port --- lib/utilrb/socket/tcp_server.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utilrb/socket/tcp_server.rb b/lib/utilrb/socket/tcp_server.rb index 4c97e3a..df63cde 100644 --- a/lib/utilrb/socket/tcp_server.rb +++ b/lib/utilrb/socket/tcp_server.rb @@ -1,7 +1,7 @@ require 'socket' class TCPServer - def bound_addr; Socket::getnameinfo(getsockname)[0] end - def port; Socket::getnameinfo(getsockname)[1].to_i end + def bound_addr; Socket.unpack_sockaddr_in(getsockname)[1] end + def port; Socket.unpack_sockaddr_in(getsockname)[0] end end From 6fb0b0c1a03b1d82cccb29991a6498701611a8ae Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 11 Jun 2015 13:03:07 -0300 Subject: [PATCH 03/67] define ValueSet#substract and ValueSet#add to help switching to Set ValueSet should really go away. The main interest of the class was to avoid garbage collections, and since 2.1+ it has become part of the problem (since it is not compatible with Ruby's generational GC) This defines two methods that are implemented in ValueSet and Set but have a different name in both, so that ValueSet-using code can switch to them. --- lib/utilrb/value_set.rb | 11 ++++++- test/test_enumerable.rb | 55 ------------------------------- test/test_value_set.rb | 72 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 56 deletions(-) create mode 100644 test/test_value_set.rb diff --git a/lib/utilrb/value_set.rb b/lib/utilrb/value_set.rb index 15805d7..9c7ae4a 100644 --- a/lib/utilrb/value_set.rb +++ b/lib/utilrb/value_set.rb @@ -9,7 +9,16 @@ def <<(obj); insert(obj) ; self end alias :- :difference include Enumerable - def to_s + def substract(other_set) + difference!(other_set.to_value_set) + end + + def add(value) + insert(value) + self + end + + def to_s elements = EnumerableToString.to_s_helper(self, '{', '}') do |obj| obj.to_s end diff --git a/test/test_enumerable.rb b/test/test_enumerable.rb index 458aaf8..0fa484e 100644 --- a/test/test_enumerable.rb +++ b/test/test_enumerable.rb @@ -60,60 +60,5 @@ def test_random_element assert_equal(nil, {}.random_element) end - Utilrb.require_ext('test_value_set') do - def test_value_set - a = [1, 3, 3, 4, 6, 8].to_value_set - b = [1, 2, 4, 3, 11, 11].to_value_set - assert_equal(5, a.size) - assert_equal([1, 3, 4, 6, 8], a.to_a) - assert(a.include?(1)) - assert(a.include_all?([4, 1, 8].to_value_set)) - assert(!a.include_all?(b)) - - assert(a.intersects?(b)) - assert(b.intersects?(a)) - assert(!a.intersects?([2, 9, 12].to_value_set)) - - assert(a.object_id == a.to_value_set.object_id) - - assert_equal([1, 2, 3, 4, 6, 8, 11], (a.union(b)).to_a) - assert_equal([1, 3, 4], (a.intersection(b)).to_a) - assert_equal([6, 8], (a.difference(b)).to_a) - assert(! (a == :bla)) # check #== behaves correctly with a non-enumerable - - a.delete(1) - assert(! a.include?(1)) - a.merge(b); - assert_equal([1, 2, 3, 4, 6, 8, 11].to_value_set, a) - - assert([].to_value_set.empty?) - - assert([1, 2, 4, 3].to_value_set.clear.empty?) - - assert_equal([1,3,5].to_value_set, [1, 2, 3, 4, 5, 6].to_value_set.delete_if { |v| v % 2 == 0 }) - end - - def test_value_set_hash - a = [(obj = Object.new), 3, 4, [(obj2 = Object.new), Hash.new]].to_value_set - b = [obj, 3, 4, [obj2, Hash.new]].to_value_set - assert_equal a.hash, b.hash - end - - def test_value_set_to_s - obj = ValueSet.new - obj << 1 - obj << 2 - assert(obj.to_s =~ /\{(.*)\}/) - values = $1.split(", ") - assert_equal(["1", "2"].to_set, values.to_set) - - obj << obj - assert(obj.to_s =~ /^(.+)\{(.*)\}>$/) - - base_s = $1 - values = $2.split(", ") - assert_equal(["1", "2", "#{base_s}...>"].to_set, values.to_set) - end - end end diff --git a/test/test_value_set.rb b/test/test_value_set.rb new file mode 100644 index 0000000..0542c96 --- /dev/null +++ b/test/test_value_set.rb @@ -0,0 +1,72 @@ +require 'utilrb/test' +require 'utilrb/value_set' + +class TC_ValueSet < Minitest::Test + Utilrb.require_ext('test_value_set') do + def test_value_set + a = [1, 3, 3, 4, 6, 8].to_value_set + b = [1, 2, 4, 3, 11, 11].to_value_set + assert_equal(5, a.size) + assert_equal([1, 3, 4, 6, 8], a.to_a) + assert(a.include?(1)) + assert(a.include_all?([4, 1, 8].to_value_set)) + assert(!a.include_all?(b)) + + assert(a.intersects?(b)) + assert(b.intersects?(a)) + assert(!a.intersects?([2, 9, 12].to_value_set)) + + assert(a.object_id == a.to_value_set.object_id) + + assert_equal([1, 2, 3, 4, 6, 8, 11], (a.union(b)).to_a) + assert_equal([1, 3, 4], (a.intersection(b)).to_a) + assert_equal([6, 8], (a.difference(b)).to_a) + assert(! (a == :bla)) # check #== behaves correctly with a non-enumerable + + a.delete(1) + assert(! a.include?(1)) + a.merge(b); + assert_equal([1, 2, 3, 4, 6, 8, 11].to_value_set, a) + + assert([].to_value_set.empty?) + + assert([1, 2, 4, 3].to_value_set.clear.empty?) + + assert_equal([1,3,5].to_value_set, [1, 2, 3, 4, 5, 6].to_value_set.delete_if { |v| v % 2 == 0 }) + end + + def test_value_set_hash + a = [(obj = Object.new), 3, 4, [(obj2 = Object.new), Hash.new]].to_value_set + b = [obj, 3, 4, [obj2, Hash.new]].to_value_set + assert_equal a.hash, b.hash + end + + def test_value_set_to_s + obj = ValueSet.new + obj << 1 + obj << 2 + assert(obj.to_s =~ /\{(.*)\}/) + values = $1.split(", ") + assert_equal(["1", "2"].to_set, values.to_set) + + obj << obj + assert(obj.to_s =~ /^(.+)\{(.*)\}>$/) + + base_s = $1 + values = $2.split(", ") + assert_equal(["1", "2", "#{base_s}...>"].to_set, values.to_set) + end + + def test_value_set_add + a = [1, 3, 3, 4, 6, 8].to_value_set + assert_same a, a.add(10) + assert a.include?(10) + end + + def test_value_set_substract + a = [1, 3, 3, 4, 6, 8].to_value_set + a.substract([3,4]) + assert_equal [1, 6, 8].to_value_set, a + end + end +end From d857f921e5ab597094a6fdfff884f1bfeb418697 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 27 Jul 2015 13:51:05 -0300 Subject: [PATCH 04/67] define yard-utilrb so that one can specify --plugin utilrb to get the utilrb/yard plugin --- lib/yard-utilrb.rb | 1 + 1 file changed, 1 insertion(+) create mode 100644 lib/yard-utilrb.rb diff --git a/lib/yard-utilrb.rb b/lib/yard-utilrb.rb new file mode 100644 index 0000000..c320d3b --- /dev/null +++ b/lib/yard-utilrb.rb @@ -0,0 +1 @@ +require 'utilrb/yard' From 28c58e546aaadfed0a32abe0c8dbc392e09be7d6 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 3 Aug 2015 16:41:21 -0300 Subject: [PATCH 05/67] inherited_enumerable is not part of utilrb anymore, remove from the yard plugin --- lib/utilrb/yard.rb | 111 --------------------------------------------- 1 file changed, 111 deletions(-) diff --git a/lib/utilrb/yard.rb b/lib/utilrb/yard.rb index 314abb2..19f338b 100644 --- a/lib/utilrb/yard.rb +++ b/lib/utilrb/yard.rb @@ -2,117 +2,6 @@ module Utilrb module YARD include ::YARD - class InheritedAttributeHandler < YARD::Handlers::Ruby::AttributeHandler - handles method_call(:inherited_attribute) - namespace_only - - def self.process(handler, name, attr_name, is_map, key_name = nil, return_type = nil) - end - - def process - name = statement.parameters[0].jump(:tstring_content, :ident).source - if statement.parameters.size == 4 - attr_name = statement.parameters[1].jump(:tstring_content, :ident).source - else - attr_name = name - end - options = statement.parameters.jump(:assoc) - - is_map = false - if options != statement.parameters - key = options[0].jump(:ident).source - value = options[1].jump(:ident).source - if key == "map" && value == "true" - is_map = true - end - end - - key_type, value_type = nil - - object = YARD::CodeObjects::MethodObject.new(namespace, attr_name, scope) do |o| - o.dynamic = true - o.aliases << "self_#{name}" - end - register(object) - key_name ||= - if object.docstring.has_tag?('key_name') - object.docstring.tag('key_name').text - else - 'key' - end - return_type ||= - if object.docstring.has_tag?('return') - object.docstring.tag('return').types.first - elsif is_map - 'Hash' - else - 'Array' - end - if return_type =~ /^\w+\<(.*)\>$/ - if is_map - key_type, value_type = $1.split(',') - else - value_type = $1 - end - else - key_type = "Object" - value_type = "Object" - end - - object = YARD::CodeObjects::MethodObject.new(namespace, "all_#{name}", scope) - object.dynamic = true - register(object) - object.docstring.replace("The union, along the class hierarchy, of all the values stored in #{name}\n@return [Array<#{value_type}>]") - - if is_map - object = YARD::CodeObjects::MethodObject.new(namespace, "find_#{name}", scope) - object.dynamic = true - register(object) - object.parameters << [key_name] - object.docstring.replace(" -Looks for objects registered in #{name} under the given key, and returns the first one in the ancestor chain -(i.e. the one tha thas been registered in the most specialized class) - -@return [#{value_type},nil] the found object, or nil if none is registered under that key") - - object = YARD::CodeObjects::MethodObject.new(namespace, "has_#{name}?", scope) - object.dynamic = true - register(object) - object.parameters << [key_name] - object.docstring.replace("Returns true if an object is registered in #{name} anywhere in the class hierarchy\n@return [Boolean]") - object.signature = "def has_#{name}?(key)" - - object = YARD::CodeObjects::MethodObject.new(namespace, "each_#{name}", scope) - object.dynamic = true - register(object) - object.parameters << [key_name, "nil"] << ["uniq", "true"] - object.docstring.replace(" -@overload each_#{name}(#{key_name}, uniq = true) - Enumerates all objects registered in #{name} under the given key - @yield [element] - @yieldparam [#{value_type}] element -@overload each_#{name}(nil, uniq = true) - Enumerates all objects registered in #{name} - @yield [#{key_name}, element] - @yieldparam [#{key_type}] #{key_name} - @yieldparam [#{value_type}] element - ") - else - object = YARD::CodeObjects::MethodObject.new(namespace, "each_#{name}", scope) - object.dynamic = true - register(object) - object.docstring.replace("Enumerates all objects registered in #{name}\n@return []\n@yield [element]\n@yieldparam [#{value_type}] element") - end - - if is_map - return key_type, value_type - else - return value_type - end - end - end - YARD::Tags::Library.define_tag("Key for inherited_attribute(_, :map => true)", :key_name) - class AttrEnumerableHandler < YARD::Handlers::Ruby::AttributeHandler handles method_call(:attr_enumerable) namespace_only From 6421ffe1f3809916f4de6b1fe3eb7ba0b0a26edb Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Tue, 18 Aug 2015 10:35:57 -0300 Subject: [PATCH 06/67] make flexmock a test dependency --- manifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.xml b/manifest.xml index 4a81720..b8d5577 100644 --- a/manifest.xml +++ b/manifest.xml @@ -15,6 +15,6 @@ - + From 91c637769a23f96cba687fe50046f25d76b98f9a Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Fri, 4 Sep 2015 16:19:48 -0300 Subject: [PATCH 07/67] logger: allow calling IO#puts and IO#print without arguments --- lib/utilrb/logger/io.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/utilrb/logger/io.rb b/lib/utilrb/logger/io.rb index cd1b67f..19f26dd 100644 --- a/lib/utilrb/logger/io.rb +++ b/lib/utilrb/logger/io.rb @@ -8,13 +8,13 @@ def initialize(logger, level) @logger, @level = logger, level @buffer = '' end - def puts(msg) + def puts(*msg) print msg logger.send(level, @buffer) @buffer = '' end - def print(msg) - @buffer << msg + def print(*msg) + @buffer << msg.join("") end end From 4135eb101982f37db838ce7dda0b51f4478adb98 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 21:08:36 -0300 Subject: [PATCH 08/67] move to flexmock 2.0 --- lib/utilrb/test.rb | 12 ------------ test/test_exception.rb | 2 +- test/test_kernel.rb | 28 +--------------------------- test/test_logger.rb | 2 +- test/test_module.rb | 2 +- test/test_unbound_method.rb | 2 +- 6 files changed, 5 insertions(+), 43 deletions(-) diff --git a/lib/utilrb/test.rb b/lib/utilrb/test.rb index b88ed9b..2ab5d39 100644 --- a/lib/utilrb/test.rb +++ b/lib/utilrb/test.rb @@ -16,7 +16,6 @@ require 'utilrb' require 'minitest/autorun' require 'minitest/spec' -require 'flexmock/test_unit' if ENV['TEST_ENABLE_PRY'] != '0' begin @@ -40,19 +39,11 @@ module Utilrb # end # module SelfTest - if defined? FlexMock - include FlexMock::ArgumentTypes - include FlexMock::MockContainer - end - def setup # Setup code for all the tests end def teardown - if defined? FlexMock - flexmock_teardown - end super # Teardown code for all the tests end @@ -60,9 +51,6 @@ def teardown end module Minitest - class Spec - include Utilrb::SelfTest - end class Test include Utilrb::SelfTest end diff --git a/test/test_exception.rb b/test/test_exception.rb index c279dd6..2d8f471 100644 --- a/test/test_exception.rb +++ b/test/test_exception.rb @@ -1,6 +1,6 @@ require 'utilrb/test' require 'utilrb/exception' -require 'flexmock' +require 'flexmock/minitest' class TC_Exception < Minitest::Test def test_full_message diff --git a/test/test_kernel.rb b/test/test_kernel.rb index 1ec2da0..5ce8d97 100644 --- a/test/test_kernel.rb +++ b/test/test_kernel.rb @@ -1,5 +1,5 @@ require 'utilrb/test' -require 'flexmock/test_unit' +require 'flexmock/minitest' require 'tempfile' require 'utilrb/kernel' @@ -201,32 +201,6 @@ def my_method end end - if Utilrb::RUBY_IS_18 - def test_eval_dsl_file_does_not_allow_class_definition - obj = Class.new do - def real_method - @real_method_called = true - end - end.new - - Tempfile.open('test_eval_dsl_file') do |io| - io.puts <<-EOD - class NewClass - end - EOD - io.flush - - begin - eval_dsl_file(io.path, obj, [], false) - flunk("NewClass has been defined") - rescue NameError => e - assert e.message =~ /NewClass/, e.message - assert e.backtrace.first =~ /#{io.path}:1/, "wrong backtrace when checking constant definition detection: #{e.backtrace[0]}, expected #{io.path}:1" - end - end - end - end - def test_dsl_exec obj = Class.new do def real_method_called?; !!@real_method_called end diff --git a/test/test_logger.rb b/test/test_logger.rb index 632485f..cf0eed2 100644 --- a/test/test_logger.rb +++ b/test/test_logger.rb @@ -1,6 +1,6 @@ require 'utilrb/test' require 'utilrb/logger' -require 'flexmock/test_unit' +require 'flexmock/minitest' class TC_Logger < Minitest::Test module Root diff --git a/test/test_module.rb b/test/test_module.rb index 6d69eeb..f9bd99a 100644 --- a/test/test_module.rb +++ b/test/test_module.rb @@ -1,6 +1,6 @@ require 'utilrb/test' -require 'flexmock/test_unit' +require 'flexmock/minitest' require 'set' require 'enumerator' require 'utilrb/module' diff --git a/test/test_unbound_method.rb b/test/test_unbound_method.rb index 8bde303..88114cd 100644 --- a/test/test_unbound_method.rb +++ b/test/test_unbound_method.rb @@ -1,6 +1,6 @@ require 'utilrb/test' require 'utilrb/unbound_method' -require 'flexmock' +require 'flexmock/minitest' class TC_UnboundMethod < Minitest::Test def test_call From aa71fdc1d6ac914cfc9e57e1901c9f42e537cc44 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 21:08:55 -0300 Subject: [PATCH 09/67] move the VERSION constant to the de-facto standard utilrb/version --- lib/utilrb/common.rb | 6 +----- lib/utilrb/version.rb | 4 ++++ 2 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 lib/utilrb/version.rb diff --git a/lib/utilrb/common.rb b/lib/utilrb/common.rb index 3f54d98..ecedbe0 100644 --- a/lib/utilrb/common.rb +++ b/lib/utilrb/common.rb @@ -1,12 +1,8 @@ +require 'utilrb/version' # Utilrb is yet another Ruby toolkit, in the spirit of facets. It includes all # the standard class extensions used by www.rock-robotics.org projects. module Utilrb - unless defined? Utilrb::VERSION - VERSION = "2.0.1" - RUBY_IS_18 = (RUBY_VERSION < "1.9.0") - end - LIB_DIR = File.expand_path(File.dirname(__FILE__)) unless defined? UTILRB_EXT_MODE diff --git a/lib/utilrb/version.rb b/lib/utilrb/version.rb new file mode 100644 index 0000000..b8b55f6 --- /dev/null +++ b/lib/utilrb/version.rb @@ -0,0 +1,4 @@ +module Utilrb + VERSION = "2.1.0" +end + From 74371790121bd37ab231639040f203e8c7d6b9c9 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 21:09:15 -0300 Subject: [PATCH 10/67] remove 1.8-specific handlers --- ext/utilrb/weakref.cc | 4 ---- lib/utilrb/module/const_defined_here_p.rb | 6 ------ 2 files changed, 10 deletions(-) diff --git a/ext/utilrb/weakref.cc b/ext/utilrb/weakref.cc index d25d792..f248619 100644 --- a/ext/utilrb/weakref.cc +++ b/ext/utilrb/weakref.cc @@ -1,11 +1,7 @@ #include #include #include -#ifdef RUBY_IS_18 -#include -#else #include -#endif using std::set; using std::map; diff --git a/lib/utilrb/module/const_defined_here_p.rb b/lib/utilrb/module/const_defined_here_p.rb index fd9da2d..e4a9a13 100644 --- a/lib/utilrb/module/const_defined_here_p.rb +++ b/lib/utilrb/module/const_defined_here_p.rb @@ -1,12 +1,6 @@ require 'utilrb/common' class Module - if Utilrb::RUBY_IS_18 - def const_defined_here?(name) - const_defined?(name) - end - else def const_defined_here?(name) const_defined?(name, false) end - end end From 4f1ce83e18a6fcde97d00f13654fdd9468fa03eb Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 22:04:39 -0300 Subject: [PATCH 11/67] remove Proc extensions 2.1+ has built-in equivalent functionality, and this is not used nowadays ... just remove it. --- ext/utilrb/extconf.rb | 17 +---------------- ext/utilrb/proc.c | 39 --------------------------------------- ext/utilrb/utilrb.cc | 3 --- 3 files changed, 1 insertion(+), 58 deletions(-) delete mode 100644 ext/utilrb/proc.c diff --git a/ext/utilrb/extconf.rb b/ext/utilrb/extconf.rb index 3f923d7..85c7046 100644 --- a/ext/utilrb/extconf.rb +++ b/ext/utilrb/extconf.rb @@ -5,22 +5,7 @@ $LDFLAGS += " -module" end -if RUBY_VERSION < "1.9" - $CFLAGS += " -DRUBY_IS_18" - puts "not building with core source" - create_makefile("utilrb/utilrb") -else - begin - require 'debugger/ruby_core_source' - hdrs = lambda { try_compile("#include ") } - $CFLAGS += " -DHAS_RUBY_SOURCE" - Debugger::RubyCoreSource.create_makefile_with_core(hdrs, "utilrb/utilrb") - rescue Exception - $CFLAGS.gsub!(/ -DHAS_RUBY_SOURCE/, '') - puts "not building with core source" - create_makefile("utilrb/utilrb") - end -end +create_makefile("utilrb/utilrb") ## WORKAROUND a problem with mkmf.rb # It seems that the newest version do define an 'install' target. However, that diff --git a/ext/utilrb/proc.c b/ext/utilrb/proc.c deleted file mode 100644 index 17118d0..0000000 --- a/ext/utilrb/proc.c +++ /dev/null @@ -1,39 +0,0 @@ -#ifdef HAS_RUBY_SOURCE -#include - -static VALUE env_references(VALUE rbenv) -{ - rb_env_t* env; - - VALUE result = rb_ary_new(); - GetEnvPtr(rbenv, env); - if (env->env) - { - int i; - for (i = 0; i < env->env_size; ++i) - rb_ary_push(result, rb_obj_id(env->env[i])); - } - return result; -} - -static VALUE proc_references(VALUE rbproc) -{ - rb_proc_t* proc; - GetProcPtr(rbproc, proc); - - if (!NIL_P(proc->envval)) - return env_references(proc->envval); - return rb_ary_new(); -} -#elif RUBY_IS_18 -#warning "compiling on Ruby 1.8, Proc#references will not be available" -#else -#warning "Ruby core sources cannot be found, Proc#references will not be available. Install the debugger-ruby_core_source gem to enable" -#endif - -void Init_proc() -{ -#ifdef HAS_RUBY_SOURCE - rb_define_method(rb_cProc, "references", RUBY_METHOD_FUNC(proc_references), 0); -#endif -} diff --git a/ext/utilrb/utilrb.cc b/ext/utilrb/utilrb.cc index 815e6b7..7416d20 100644 --- a/ext/utilrb/utilrb.cc +++ b/ext/utilrb/utilrb.cc @@ -54,7 +54,6 @@ static VALUE kernel_crash(VALUE klass) extern "C" void Init_value_set(); extern "C" void Init_weakref(VALUE mUtilrb); -extern "C" void Init_proc(); extern "C" void Init_utilrb() { @@ -71,8 +70,6 @@ extern "C" void Init_utilrb() Init_weakref(mUtilrb); #endif - Init_proc(); - Init_value_set(); } From da6bfd42c6fa65b3d78e409a9fad8fa75750ed24 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 22:11:27 -0300 Subject: [PATCH 12/67] fix races in thread pool trimming First, a mutex lock is (obviously ...) not inherited by a spawned thread. Decrementing @spawned needs therefore to be done under a @mutex lock. Second, simplify the auto-trimming logic by testing for the need to trim directly in the "management" part of the main loop instead of going indirectly through #trim and @trim_requests --- lib/utilrb/thread_pool.rb | 22 ++++++++++------------ test/test_thread_pool.rb | 29 +++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/lib/utilrb/thread_pool.rb b/lib/utilrb/thread_pool.rb index 0f4d1de..cf0b2ad 100644 --- a/lib/utilrb/thread_pool.rb +++ b/lib/utilrb/thread_pool.rb @@ -506,10 +506,8 @@ def <<(task) # @param [boolean] force Trim even if no thread is waiting. def trim (force = false) @mutex.synchronize do - if (force || @waiting > 0) && @spawned - @trim_requests > @min - @trim_requests += 1 - @cond.signal - end + @trim_requests += 1 + @cond.signal end self end @@ -571,8 +569,10 @@ def spawn_thread end break task unless task.is_a? Array - if @trim_requests > 0 - @trim_requests -= 1 + if @spawned > @min && (auto_trim || @trim_requests > 0) + if @trim_requests > 0 + @trim_requests -= 1 + end break end @waiting += 1 @@ -597,14 +597,12 @@ def spawn_thread current_task.finalize # propagate state after it was deleted from the internal lists @callback_on_task_finished.call(current_task) if @callback_on_task_finished end - trim if auto_trim end - # we do not have to lock here - # because spawn_thread must be called from - # a synchronized block - @spawned -= 1 - @workers.delete thread + @mutex.synchronize do + @spawned -= 1 + @workers.delete thread + end end @spawned += 1 @workers << thread diff --git a/test/test_thread_pool.rb b/test/test_thread_pool.rb index 1e4aa9e..82cd0f2 100644 --- a/test/test_thread_pool.rb +++ b/test/test_thread_pool.rb @@ -70,15 +70,32 @@ it "must reduce its number of threads after the work is done if auto_trim == true." do pool = Utilrb::ThreadPool.new(5,20) pool.auto_trim = true - 0.upto 19 do + + sync = Mutex.new + cond = ConditionVariable.new + waiting = 0 + start = Time.now + 0.upto 19 do |i| pool.process do - sleep 0.15 + sync.synchronize do + waiting += 1 + cond.wait(sync) + end end end - sleep 0.13 - assert_equal 0,pool.waiting - assert_equal 20,pool.spawned - sleep 0.4 + while waiting != 20 && (Time.now - start) < 5 + Thread.pass + end + sync.synchronize do + assert_equal 0,pool.waiting + assert_equal 20,pool.spawned + cond.broadcast + end + + start = Time.now + while (pool.waiting != 5 || pool.spawned != 5) && (Time.now - start) < 2 + Thread.pass + end assert_equal 5,pool.waiting assert_equal 5,pool.spawned pool.shutdown From 4bdfe11e869e6cbd6a77519ef2a4a0e574522cbd Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 22:12:28 -0300 Subject: [PATCH 13/67] fix auto-spawning in ThreadPool#<< The issue here is that @waiting might be non-zero even though we saturated all our threads if the number of tasks waiting are equal to the number of threads waiting. This was caught by the test suite. --- lib/utilrb/thread_pool.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utilrb/thread_pool.rb b/lib/utilrb/thread_pool.rb index cf0b2ad..feada11 100644 --- a/lib/utilrb/thread_pool.rb +++ b/lib/utilrb/thread_pool.rb @@ -492,7 +492,7 @@ def <<(task) end task.queued_at = Time.now @tasks_waiting << task - if @waiting == 0 && @spawned < @max + if @waiting <= @tasks_waiting.size && @spawned < @max spawn_thread end @cond.signal From f6e623e96a463a6ded572b7f4d8f5108e563f2bf Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 22:13:13 -0300 Subject: [PATCH 14/67] fix broken EventLoop#once(non_zero_delay) The timer was called instantly, which meant that once(0.1) was actually strictly equivalent to just once() --- lib/utilrb/event_loop.rb | 2 +- test/test_event_loop.rb | 54 ++++++++++++++++++++++------------------ 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/lib/utilrb/event_loop.rb b/lib/utilrb/event_loop.rb index 6c0d928..bfac746 100644 --- a/lib/utilrb/event_loop.rb +++ b/lib/utilrb/event_loop.rb @@ -360,7 +360,7 @@ def once(delay=nil,&block) raise ArgumentError "no block given" unless block if delay && delay > 0 timer = Timer.new(self,delay,true,&block) - timer.start + timer.start(timer.period, false) else add_event(Event.new(block)) end diff --git a/test/test_event_loop.rb b/test/test_event_loop.rb index 00976fe..fd08d0c 100644 --- a/test/test_event_loop.rb +++ b/test/test_event_loop.rb @@ -138,42 +138,48 @@ def test_filter(result) describe "when executed" do it "must call the timers at the right point in time." do event_loop = Utilrb::EventLoop.new - val1 = nil - val2 = nil - val3 = nil + vals = [] event_loop.every 0.1 do - val1 = 123 + vals << 123 end event_loop.every 0.2 do - val2 = 345 + vals << 345 end - event_loop.once 0.3 do - val3 = 444 + event_loop.every 0.3 do + vals << 444 end time = Time.now - while Time.now - time < 0.101 - event_loop.step - end + flexmock(Time).should_receive(:now).and_return { time } event_loop.steps - assert_equal 123,val1 - assert_equal nil,val2 - assert_equal nil,val3 - val1 = nil + # Timers are called once at add time + assert_equal [123,345,444],vals + + vals.clear + time += 0.101; event_loop.steps + assert_equal [123],vals + time += 0.101; event_loop.steps + assert_equal [123,123,345],vals + time += 0.101; event_loop.steps + assert_equal [123,123,345,123,444],vals + event_loop.clear + end + it "calls a delayed once timer only once" do + event_loop = Utilrb::EventLoop.new + vals = Array.new time = Time.now - while Time.now - time < 0.101 - event_loop.step + flexmock(Time).should_receive(:now).and_return { time } + event_loop.once 0.1 do + vals << 123 end - assert_equal 123,val1 - assert_equal 345,val2 - assert_equal nil,val3 - time = Time.now - while Time.now - time < 0.101 - event_loop.step - end - assert_equal 444,val3 + event_loop.steps + assert_equal [], vals + time += 0.101; event_loop.steps + assert_equal [123], vals + time += 0.101; event_loop.steps + assert_equal [123],vals event_loop.clear end From b1c6277f8519aba64907e702aefd16a1dcdf113f Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 22:13:29 -0300 Subject: [PATCH 15/67] fix EventLoop#reraise_error with an error that has no backtrace --- lib/utilrb/event_loop.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utilrb/event_loop.rb b/lib/utilrb/event_loop.rb index bfac746..36746ff 100644 --- a/lib/utilrb/event_loop.rb +++ b/lib/utilrb/event_loop.rb @@ -575,7 +575,7 @@ def shutdown() end def reraise_error(error) - raise error, error.message, error.backtrace + caller(1) + raise error, error.message, (error.backtrace || []) + caller(1) end # Handles all current events and timers. If a code From b4c4c9433adae5264c984937457c54d079715f5c Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 22:13:48 -0300 Subject: [PATCH 16/67] remove now-obsolete inherited_enumerable It has been moved to metaruby --- lib/utilrb/module/inherited_enumerable.rb | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 lib/utilrb/module/inherited_enumerable.rb diff --git a/lib/utilrb/module/inherited_enumerable.rb b/lib/utilrb/module/inherited_enumerable.rb deleted file mode 100644 index abb6582..0000000 --- a/lib/utilrb/module/inherited_enumerable.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'utilrb/models/inherited_enumerable' - -class Module - include Utilrb::Models -end - From 31669312c1577e6cddc57b13b1e29981b1dbce5d Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 21:10:02 -0300 Subject: [PATCH 17/67] remove usage of hoe, replaced by bundler This is really the de-facto standard infrastructure for ruby packages nowadays --- .gitignore | 1 + Gemfile | 3 +++ Rakefile | 70 +++++++++++--------------------------------------- manifest.xml | 2 -- utilrb.gemspec | 26 +++++++++++++++++++ 5 files changed, 45 insertions(+), 57 deletions(-) create mode 100644 Gemfile create mode 100644 utilrb.gemspec diff --git a/.gitignore b/.gitignore index f804262..72d5c88 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ tmp/ .yardoc lib/utilrb/utilrb.so pkg/ +/vendor/ diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..fa75df1 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gemspec diff --git a/Rakefile b/Rakefile index 56f0276..731b69e 100644 --- a/Rakefile +++ b/Rakefile @@ -1,59 +1,19 @@ -$LOAD_PATH.unshift File.expand_path('lib', File.dirname(__FILE__)) -require 'rake' - -begin - require 'hoe' - - Hoe::plugin :yard - - hoe_spec = Hoe.spec 'utilrb' do - developer "Sylvain Joyeux", "sylvain.joyeux@m4x.org" - self.readme_file = 'README.rd' - - extra_deps << - ['facets', '>= 2.4.0'] << - ['rake', '>= 0.9'] << - ["rake-compiler", "~> 0.8.0"] << - ["hoe", ">= 3.7.1"] << - ["hoe-yard", ">= 0.1.2"] - - extra_dev_deps << - ['flexmock', '>= 0.8.6'] << - ['debugger-ruby_core_source', '>= 0'] - - licenses << 'BSD' - - spec_extras[:extensions] = FileList["ext/**/extconf.rb"] - end - - require 'rubygems/package_task' - Gem::PackageTask.new(hoe_spec.spec) do |pkg| - pkg.need_zip = true - pkg.need_tar = true - end - - require 'rake/extensiontask' - utilrb_task = Rake::ExtensionTask.new('utilrb', hoe_spec.spec) do |ext| - ext.name = 'utilrb' - ext.ext_dir = 'ext/utilrb' - ext.lib_dir = 'lib/utilrb' - ext.source_pattern ="*.{c,cc,cpp}" - end - - Rake.clear_tasks(/^default$/) - task :default => :compile - - task :docs => :yard - task :redocs => :yard +require "bundler/gem_tasks" +require "rake/testtask" + +require 'rake/extensiontask' +Rake::ExtensionTask.new('utilrb') do |ext| + ext.name = 'utilrb' + ext.ext_dir = 'ext/utilrb' + ext.lib_dir = 'lib/utilrb' + ext.source_pattern ="*.{c,cc,cpp}" +end +task :default => :compile -rescue LoadError => e - puts "'utilrb' cannot be build -- loading gem failed: #{e}" +Rake::TestTask.new(:test) do |t| + t.libs << "test" + t.libs << "lib" + t.test_files = FileList['test/**/test_*.rb'] end -task :full_test do - ENV.delete_if { |name,val| name == "UTILRB_EXT_MODE" } - system('testrb -I. test') - ENV['UTILRB_EXT_MODE'] = 'yes' - system('testrb -I. test') -end diff --git a/manifest.xml b/manifest.xml index 4a81720..945575c 100644 --- a/manifest.xml +++ b/manifest.xml @@ -12,8 +12,6 @@ - - diff --git a/utilrb.gemspec b/utilrb.gemspec new file mode 100644 index 0000000..7cb1fc4 --- /dev/null +++ b/utilrb.gemspec @@ -0,0 +1,26 @@ +# coding: utf-8 +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'utilrb/version' + +Gem::Specification.new do |s| + s.name = "utilrb" + s.version = Utilrb::VERSION + s.authors = ["Sylvain Joyeux"] + s.email = "sylvain.joyeux@m4x.org" + s.summary = "Utilrb is yet another Ruby toolkit, in the spirit of facets" + s.description = "Utilrb is yet another Ruby toolkit, in the spirit of facets. It includes all\nthe standard class extensions I use in other projects." + s.homepage = "http://rock-robotics.org" + s.licenses = ["BSD"] + + s.require_paths = ["lib"] + s.extensions = [] + s.extra_rdoc_files = ["History.txt", "License.txt", "Manifest.txt", "History.txt"] + s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + + s.add_runtime_dependency "facets", ">= 2.4.0" + s.add_runtime_dependency "rake", ">= 0.9" + s.add_runtime_dependency "rake-compiler", "~> 0.8.0" + s.add_development_dependency "flexmock", ">= 2.0.0" + s.add_development_dependency "minitest", ">= 5.0", "~> 5.0" +end From 845089e8a8dcf5f404f4c606d0e0daa5e1066279 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 22:14:02 -0300 Subject: [PATCH 18/67] use flexmock from git until flexmock 2.0 is released --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index fa75df1..5b09bf2 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,4 @@ source 'https://rubygems.org' +gem 'flexmock', github: 'doudou/flexmock' gemspec From 51c7d0b396aaf9d0fd54c575b206ecbe620def73 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 22:21:28 -0300 Subject: [PATCH 19/67] alias Bundler's build target to gem to be compatible with Rock's packaging scripts They assume hoe behaviour, that is 'gem'. Note that both generate the gem in the pkg/ directory --- Rakefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Rakefile b/Rakefile index 731b69e..29bdd96 100644 --- a/Rakefile +++ b/Rakefile @@ -17,3 +17,4 @@ Rake::TestTask.new(:test) do |t| t.test_files = FileList['test/**/test_*.rb'] end +task :gem => :build From 1595794d9cb4267e14f4e28e81cf2ac59048d2c1 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 22:21:47 -0300 Subject: [PATCH 20/67] add travis file --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ce0c05a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: ruby +rvm: + - 2.0.0 + - 2.1.6 + - 2.2.2 From 34d4515ae29d7fef10e8bcef970e849c01d37213 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 22:27:46 -0300 Subject: [PATCH 21/67] generate coverage by default, and enable the coveralls formatter --- lib/utilrb/test.rb | 11 +++++++++-- utilrb.gemspec | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/utilrb/test.rb b/lib/utilrb/test.rb index 2ab5d39..0785def 100644 --- a/lib/utilrb/test.rb +++ b/lib/utilrb/test.rb @@ -1,9 +1,16 @@ # simplecov must be loaded FIRST. Only the files required after it gets loaded # will be profiled !!! -if ENV['TEST_ENABLE_COVERAGE'] == '1' +if ENV['TEST_ENABLE_COVERAGE'] != '0' begin require 'simplecov' - SimpleCov.start + require 'coveralls' + SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ + SimpleCov::Formatter::HTMLFormatter, + Coveralls::SimpleCov::Formatter + ] + SimpleCov.start do + add_filter "/test/" + end rescue LoadError require 'utilrb' Utilrb.warn "coverage is disabled because the 'simplecov' gem cannot be loaded" diff --git a/utilrb.gemspec b/utilrb.gemspec index 7cb1fc4..9ad6d4d 100644 --- a/utilrb.gemspec +++ b/utilrb.gemspec @@ -23,4 +23,5 @@ Gem::Specification.new do |s| s.add_runtime_dependency "rake-compiler", "~> 0.8.0" s.add_development_dependency "flexmock", ">= 2.0.0" s.add_development_dependency "minitest", ">= 5.0", "~> 5.0" + s.add_development_dependency "coveralls" end From d1a8608fd518917ed1fa81b67428895f3d6de7e2 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 12 Sep 2015 22:33:23 -0300 Subject: [PATCH 22/67] give a path forward to is_singleton? => singleton_class? rename --- lib/utilrb/module/is_singleton.rb | 7 +++---- lib/utilrb/module/singleton_class_p.rb | 5 +++++ 2 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 lib/utilrb/module/singleton_class_p.rb diff --git a/lib/utilrb/module/is_singleton.rb b/lib/utilrb/module/is_singleton.rb index 3af628d..2f2e011 100644 --- a/lib/utilrb/module/is_singleton.rb +++ b/lib/utilrb/module/is_singleton.rb @@ -1,7 +1,6 @@ +require 'utilrb/module/singleton_class_p' +puts "WARN Module#is_singleton? has been renamed to #singleton_class? to match the built-in method in Ruby 2.1+" +puts "WARN require 'utilrb/module/singleton_class_p instead of is_singleton?" class Module - if !method_defined?(:singleton_class?) - require 'utilrb/utilrb' - end - alias :is_singleton? :singleton_class? end diff --git a/lib/utilrb/module/singleton_class_p.rb b/lib/utilrb/module/singleton_class_p.rb new file mode 100644 index 0000000..48cfe66 --- /dev/null +++ b/lib/utilrb/module/singleton_class_p.rb @@ -0,0 +1,5 @@ +class Module + if !method_defined?(:singleton_class?) + require 'utilrb/utilrb' + end +end From 8a69b88cf849b594fbbbe1fbf3df1f632f298cd7 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sun, 13 Sep 2015 15:26:02 -0300 Subject: [PATCH 23/67] pkgconfig: fix some corner cases --- lib/utilrb/pkgconfig.rb | 19 +++++-- test/data/test_pkgconfig.pc | 2 +- ...t_pkgconfig_var_with_multiple_arguments.pc | 10 ++++ test/data/test_pkgconfig_with_require.pc | 11 ++++ test/test_module.rb | 19 ------- test/test_pkgconfig.rb | 57 ++++++++++++++++--- 6 files changed, 84 insertions(+), 34 deletions(-) create mode 100644 test/data/test_pkgconfig_var_with_multiple_arguments.pc create mode 100644 test/data/test_pkgconfig_with_require.pc diff --git a/lib/utilrb/pkgconfig.rb b/lib/utilrb/pkgconfig.rb index 8ffcbb6..f6fe59e 100644 --- a/lib/utilrb/pkgconfig.rb +++ b/lib/utilrb/pkgconfig.rb @@ -261,14 +261,23 @@ def load(path, preset_variables = Hash.new) raw_fields.each do |name, value| if SHELL_VARS.include?(name) value = Shellwords.shellsplit(value) - value.map! do |v| - expand_variables(v, variables, name) + resolved = Array.new + while !value.empty? + value = value.flat_map do |v| + expanded = expand_variables(v, variables, name) + if expanded == v + resolved << v + nil + else + Shellwords.shellsplit(expanded) + end + end.compact end + fields[name] = resolved else - value = expand_variables(value, variables, name) + fields[name] = expand_variables(value, variables, name) end - fields[name] = value end # Initialize the main flags @@ -390,7 +399,7 @@ def libs_only_l(static = false) end def libs_only_other(static = false) - @ldflags[static].find_all { |s| s !~ /^-[lL]/ }.join(" ") + @ldflags_with_requires[static].find_all { |s| s !~ /^-[lL]/ }.join(" ") end def method_missing(varname, *args, &proc) # :nodoc: diff --git a/test/data/test_pkgconfig.pc b/test/data/test_pkgconfig.pc index b8e1f70..b7bff42 100644 --- a/test/data/test_pkgconfig.pc +++ b/test/data/test_pkgconfig.pc @@ -5,5 +5,5 @@ Description: Version: 4.2 Cflags: -I${prefix}/include -O3 -Libs: -ltest -lother -L${prefix}/lib +Libs: -ltest -lother -L${prefix}/lib -Wopt diff --git a/test/data/test_pkgconfig_var_with_multiple_arguments.pc b/test/data/test_pkgconfig_var_with_multiple_arguments.pc new file mode 100644 index 0000000..0098924 --- /dev/null +++ b/test/data/test_pkgconfig_var_with_multiple_arguments.pc @@ -0,0 +1,10 @@ +prefix=a_prefix +VAR=-I/path -O3 + +Name: test_pkgconfig +Description: +Version: 4.2 + +Cflags: ${VAR} +Libs: + diff --git a/test/data/test_pkgconfig_with_require.pc b/test/data/test_pkgconfig_with_require.pc new file mode 100644 index 0000000..43cb08b --- /dev/null +++ b/test/data/test_pkgconfig_with_require.pc @@ -0,0 +1,11 @@ +prefix=a_prefix + +Name: test_pkgconfig +Description: +Version: 4.2 + +Requires: test_pkgconfig +Cflags: -Owith_requires -Iwith_requires +Libs: -lwith_requires -Lwith_requires -Wwith_requires + + diff --git a/test/test_module.rb b/test/test_module.rb index f9bd99a..b5537f4 100644 --- a/test/test_module.rb +++ b/test/test_module.rb @@ -104,25 +104,6 @@ def test_dsl_attribute_with_filter assert_equal 20, obj.value end - def test_define_inherited_enumerable_usable_on_extended_modules - obj = Array.new - defmod = Module.new do - define_inherited_enumerable(:object, :objects) { obj } - end - mod = Module.new { extend defmod } - assert_same obj, mod.objects - end - - def test_define_inherited_enumerable_usable_through_inclusion - obj = Array.new - defmod = Module.new do - define_inherited_enumerable(:object, :objects) { obj } - end - intermediate = Module.new { include defmod } - mod = Module.new { extend intermediate } - assert_same obj, mod.objects - end - def test_is_singleton m = Module.new assert !m.is_singleton? diff --git a/test/test_pkgconfig.rb b/test/test_pkgconfig.rb index 5793bb6..12d708c 100644 --- a/test/test_pkgconfig.rb +++ b/test/test_pkgconfig.rb @@ -9,6 +9,7 @@ def setup end def teardown ENV['PKG_CONFIG_PATH'] = @old_pkg_config_path + PkgConfig.clear_cache end PkgConfig = Utilrb::PkgConfig @@ -29,7 +30,7 @@ def test_load assert_equal('-O3', pkg.cflags_only_other) assert_equal(['a_prefix/include'], pkg.include_dirs) - assert_equal(%w{-ltest -lother -La_prefix/lib}.to_set, pkg.libs.split.to_set) + assert_equal(%w{-ltest -lother -Wopt -La_prefix/lib}.to_set, pkg.libs.split.to_set) assert_equal('-La_prefix/lib', pkg.libs_only_L) assert_equal(%w{-ltest -lother}.to_set, pkg.libs_only_l.split.to_set) assert_equal(['a_prefix/lib'], pkg.library_dirs) @@ -48,6 +49,7 @@ def test_load end def test_comparison_with_cpkgconfig + failed = false PkgConfig.each_package do |name| pkg = begin PkgConfig.new(name) rescue PkgConfig::NotFound @@ -68,18 +70,19 @@ def test_comparison_with_cpkgconfig cpkgconfig = Shellwords.shellsplit(pkg.send("pkgconfig_#{method_name}")).to_set default_paths = Utilrb::PkgConfig.default_search_path.map { |p| Regexp.quote(p.gsub(/\/pkgconfig/, '')) }.join("|") pure_ruby.delete_if { |f| f=~/-[IL](#{default_paths}|\/lib)$/ } + cpkgconfig.delete_if { |f| f=~/-[IL](#{default_paths}|\/lib)$/ } if pure_ruby != cpkgconfig failed = true puts "#{name} #{action_name}" puts " pure ruby: #{pure_ruby.inspect}" puts " cpkgconfig: #{cpkgconfig.inspect}" + puts "contents:" + puts pkg.file.join("\n") end end - if failed - puts "contents:" - puts pkg.file.join("\n") - assert(false, "result from pkg-config and the PkgConfig class differ") - end + end + if failed + assert(false, "result from pkg-config and the PkgConfig class differ") end end @@ -100,10 +103,46 @@ def test_missing_package_version end def test_missing_dependency - Utilrb::PkgConfig.get 'test_pkgconfig_missing_dependency' - flunk("Utilrb::PkgConfig.get did not raise on a missing dependency") - rescue Utilrb::PkgConfig::NotFound => e + e = assert_raises(Utilrb::PkgConfig::NotFound) do + Utilrb::PkgConfig.get 'test_pkgconfig_missing_dependency' + end assert e.name == "missing_dependency" assert e.message =~ /missing_dependency/ end + + def test_recursively_resolves_variables_in_shell_fields + pkg = Utilrb::PkgConfig.get('test_pkgconfig_var_with_multiple_arguments') + assert_equal '-I/path', pkg.cflags_only_I + assert_equal '-O3', pkg.cflags_only_other + end + + def test_dependencies_require_cflags_only_I + pkg = Utilrb::PkgConfig.get('test_pkgconfig_with_require') + assert_equal '-Iwith_requires -Ia_prefix/include', + pkg.cflags_only_I + end + + def test_dependencies_require_cflags_only_other + pkg = Utilrb::PkgConfig.get('test_pkgconfig_with_require') + assert_equal '-Owith_requires -O3', + pkg.cflags_only_other + end + + def test_dependencies_require_libs_only_l + pkg = Utilrb::PkgConfig.get('test_pkgconfig_with_require') + assert_equal '-lwith_requires -ltest -lother', + pkg.libs_only_l + end + + def test_dependencies_require_libs_only_L + pkg = Utilrb::PkgConfig.get('test_pkgconfig_with_require') + assert_equal '-Lwith_requires -La_prefix/lib', + pkg.libs_only_L + end + + def test_dependencies_require_libs_only_other + pkg = Utilrb::PkgConfig.get('test_pkgconfig_with_require') + assert_equal '-Wwith_requires -Wopt', + pkg.libs_only_other + end end From 9b516a02d1111076b3621a10ed1b3749705be87a Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sun, 13 Sep 2015 15:44:21 -0300 Subject: [PATCH 24/67] kernel: remove swap.rb, it has been removed from the extension a while back --- lib/utilrb/kernel/swap.rb | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 lib/utilrb/kernel/swap.rb diff --git a/lib/utilrb/kernel/swap.rb b/lib/utilrb/kernel/swap.rb deleted file mode 100644 index 34671f0..0000000 --- a/lib/utilrb/kernel/swap.rb +++ /dev/null @@ -1,2 +0,0 @@ -require 'utilrb/common' -Utilrb.require_ext('Kernel#swap!') From cf923d595f961949b273e93b6012b0822213ccd9 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sun, 13 Sep 2015 15:47:14 -0300 Subject: [PATCH 25/67] module: we can make Module#has_ancestor? an alias for #< now Module#< now has uniform behaviour for singleton and non-singleton classes in 2.0+. Older versions of ruby had different behaviours for <= when the receiver was a singleton class. Seems to be fixed in 2.0+. --- lib/utilrb/module/ancestor_p.rb | 12 ------------ test/test_module.rb | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/utilrb/module/ancestor_p.rb b/lib/utilrb/module/ancestor_p.rb index 027d3cb..2b0e9db 100644 --- a/lib/utilrb/module/ancestor_p.rb +++ b/lib/utilrb/module/ancestor_p.rb @@ -1,19 +1,7 @@ -require 'utilrb/common' class Module def has_ancestor?(klass) # :nodoc: self <= klass end end -class Class - def has_ancestor?(klass) # :nodoc: - # We first test - # self <= class - # as self.superclass goes to the next *CLASS* in the chain, i.e. skips - # included modules - # - # Then, the superclass test is used in case +self+ is a singleton - self <= klass || (superclass <= klass) - end -end diff --git a/test/test_module.rb b/test/test_module.rb index b5537f4..cacaac0 100644 --- a/test/test_module.rb +++ b/test/test_module.rb @@ -84,6 +84,24 @@ def test_has_ancestor assert(!parent.has_ancestor?(child)) end + def test_has_ancestor_for_singleton_classes + mod = Module.new + parent = Class.new do + include mod + end + child = Class.new(parent) + n = Module.new + + assert(child.has_ancestor?(parent)) + assert(child.has_ancestor?(mod)) + assert(parent.has_ancestor?(mod)) + assert(!parent.has_ancestor?(child)) + + obj_class = child.new.singleton_class + obj_class.include(n = Module.new) + assert obj_class.has_ancestor?(n) + end + def test_dsl_attribute_without_filter obj = Class.new do dsl_attribute :value From 21308898842fc8e8e19681c7282d6de14443166f Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 14 Sep 2015 08:20:10 -0300 Subject: [PATCH 26/67] implement a Utilrb::WeakRef compatible API using Ruby's built-in WeakRef This was a leftover from 1.8's broken WeakRef --- ext/utilrb/utilrb.cc | 3 - ext/utilrb/weakref.cc | 139 ------------------------------------------ lib/utilrb/weakref.rb | 27 ++++---- test/test_weakref.rb | 83 +++---------------------- 4 files changed, 21 insertions(+), 231 deletions(-) delete mode 100644 ext/utilrb/weakref.cc diff --git a/ext/utilrb/utilrb.cc b/ext/utilrb/utilrb.cc index 7416d20..b9f6084 100644 --- a/ext/utilrb/utilrb.cc +++ b/ext/utilrb/utilrb.cc @@ -53,7 +53,6 @@ static VALUE kernel_crash(VALUE klass) } extern "C" void Init_value_set(); -extern "C" void Init_weakref(VALUE mUtilrb); extern "C" void Init_utilrb() { @@ -66,8 +65,6 @@ extern "C" void Init_utilrb() rb_define_singleton_method(rb_mKernel, "crash!", RUBY_METHOD_FUNC(kernel_crash), 0); rb_define_singleton_method(rb_mKernel, "immediate?", RUBY_METHOD_FUNC(kernel_is_immediate), 1); - - Init_weakref(mUtilrb); #endif Init_value_set(); diff --git a/ext/utilrb/weakref.cc b/ext/utilrb/weakref.cc deleted file mode 100644 index f248619..0000000 --- a/ext/utilrb/weakref.cc +++ /dev/null @@ -1,139 +0,0 @@ -#include -#include -#include -#include - -using std::set; -using std::map; - -static VALUE cWeakRef; -static VALUE cRefError; - -/* Weakref internal structure. +obj+ is Qnil before initialization and Qundef - * after finalization */ -struct WeakRef { - VALUE ruby_ref; - VALUE obj; -}; - -// Map from real objects to the set of associated WeakRef objects -typedef set ObjSet; -typedef map< VALUE, ObjSet > RefFromObjID; -typedef map< VALUE, VALUE > ObjFromRefID; -RefFromObjID from_obj_id; -ObjFromRefID from_ref_id; - -static void weakref_free(WeakRef const* ref) -{ - VALUE ref_id = rb_obj_id(ref->ruby_ref); - ObjFromRefID::iterator obj_it = from_ref_id.find(ref_id); - if (obj_it != from_ref_id.end()) - { - VALUE obj_id = rb_obj_id(obj_it->second); - RefFromObjID::iterator ref_set = from_obj_id.find(obj_id); - ref_set->second.erase(ref->ruby_ref); - from_ref_id.erase(obj_it); - } - delete ref; -} -static VALUE weakref_alloc(VALUE klass) -{ - WeakRef* ref = new WeakRef; - ref->obj = Qnil; - ref->ruby_ref = Data_Wrap_Struct(klass, NULL, weakref_free, ref); - return ref->ruby_ref; -} - -static WeakRef& get_weakref(VALUE self) -{ - WeakRef* object = 0; - Data_Get_Struct(self, WeakRef, object); - return *object; -} - -static VALUE do_object_finalize(VALUE mod, VALUE obj_id) -{ - RefFromObjID::iterator ref_set = from_obj_id.find(obj_id); - if (ref_set != from_obj_id.end()) - { - ObjSet::iterator it = ref_set->second.begin(); - ObjSet::iterator const end = ref_set->second.end(); - for (; it != end; ++it) - { - // Do NOT use Data_Wrap_Struct here, it would break on recent Ruby - // interpreters. The reason is that the object type is reset during - // GC -- and the call to free functions is deferred. - // - // So, at this point, we're sure we have a RDATA in *it (otherwise - // weakref_free would have been called), but we can't use - // Data_Wrap_Struct because that would crash. - WeakRef& ref = *reinterpret_cast(RDATA(*it)->data);; - ref.obj = Qundef; - from_ref_id.erase(rb_obj_id(*it)); - } - from_obj_id.erase(obj_id); - } - return Qnil; -} - -// Note: the Ruby code has already registered +do_object_finalize+ as the -// finalizer for +obj+. -// -// It is forbidden to make a weakref-of-weakref or a weakref of an immediate -// object -static VALUE weakref_do_initialize(VALUE self, VALUE obj) -{ - if (!FL_ABLE(obj)) - { - VALUE str = rb_any_to_s(obj); - rb_raise(rb_eArgError, "%s cannot be finalized", StringValuePtr(str)); - } - - WeakRef& ref = get_weakref(self); - ref.obj = obj; - - RefFromObjID::iterator it = from_obj_id.find(rb_obj_id(obj)); - if (it == from_obj_id.end()) - it = from_obj_id.insert( make_pair(rb_obj_id(obj), ObjSet()) ).first; - - it->second.insert(self); - from_ref_id.insert( std::make_pair(rb_obj_id(self), obj) ); - - return Qnil; -} - -static VALUE weakref_get(VALUE self) -{ - WeakRef const& ref = get_weakref(self); - - if (ref.obj == Qnil) - rb_raise(cRefError, "initialized weakref"); - if (ref.obj == Qundef) - rb_raise(cRefError, "finalized object"); - return ref.obj; -} - -static VALUE refcount(VALUE mod, VALUE obj) -{ - if (0 == (obj & FIXNUM_FLAG)) - obj = rb_obj_id(obj); - - RefFromObjID::const_iterator it = from_obj_id.find(obj); - if (it == from_obj_id.end()) - return Qnil; - else - return INT2FIX(it->second.size()); -} - -extern "C" void Init_weakref(VALUE mUtilrb) -{ - cWeakRef = rb_define_class_under(mUtilrb, "WeakRef", rb_cObject); - cRefError = rb_define_class_under(cWeakRef, "RefError", rb_eStandardError); - rb_define_alloc_func(cWeakRef, weakref_alloc); - - rb_define_singleton_method(cWeakRef, "do_object_finalize", RUBY_METHOD_FUNC(do_object_finalize), 1); - rb_define_singleton_method(cWeakRef, "refcount", RUBY_METHOD_FUNC(refcount), 1); - rb_define_method(cWeakRef, "do_initialize", RUBY_METHOD_FUNC(weakref_do_initialize), 1); - rb_define_method(cWeakRef, "get", RUBY_METHOD_FUNC(weakref_get), 0); -} - diff --git a/lib/utilrb/weakref.rb b/lib/utilrb/weakref.rb index 1443877..228f19a 100644 --- a/lib/utilrb/weakref.rb +++ b/lib/utilrb/weakref.rb @@ -1,21 +1,16 @@ -require 'utilrb/common' +require 'weakref' -Utilrb.require_ext("Utilrb::WeakRef") do - module Utilrb - class WeakRef - def initialize(obj) - if obj.kind_of?(WeakRef) - raise ArgumentError, "cannot create a weakref of a weakref" - end - unless WeakRef.refcount(obj) - begin - ObjectSpace.define_finalizer(obj, self.class.method(:do_object_finalize)) - rescue RuntimeError => e - raise ArgumentError, "cannot define finalizer for #{obj}", e.backtrace - end - end - do_initialize(obj) +module Utilrb + class WeakRef < ::WeakRef + def initialize(obj) + if obj.kind_of?(::WeakRef) + raise ArgumentError, "cannot create a weakref of a weakref" end + super + end + + def get + __getobj__ end end end diff --git a/test/test_weakref.rb b/test/test_weakref.rb index 29a3bd9..32ed559 100644 --- a/test/test_weakref.rb +++ b/test/test_weakref.rb @@ -1,81 +1,18 @@ require 'utilrb/test' require 'utilrb/weakref' -Utilrb.require_ext('TC_WeakRef') do - class TC_WeakRef < Minitest::Test - WeakRef = Utilrb::WeakRef - def test_normal - obj = Object.new - ref = Utilrb::WeakRef.new(obj) +class TC_WeakRef < Minitest::Test + WeakRef = Utilrb::WeakRef - assert_equal(obj, ref.get) - end - - def test_initialize_validation - assert_raises(ArgumentError) { Utilrb::WeakRef.new(nil) } - assert_raises(ArgumentError) { Utilrb::WeakRef.new(5) } - - ref = WeakRef.new(Object.new) - assert_raises(ArgumentError) { Utilrb::WeakRef.new(ref) } - end - - def create_deep_pair(lvl) - if lvl == 0 - 100.times do - obj = Object.new - WeakRef.new(obj) - obj = nil - end - else - create_deep_pair(lvl - 1) - end - end - - def create_deep_ref(lvl) - if lvl == 0 - obj = Object.new - refs = (1..100).map { WeakRef.new(obj) } - return refs, obj.object_id - else - create_deep_ref(lvl - 1) - end - end - - def create_deep_obj(lvl) - if lvl == 0 - ref = WeakRef.new(obj = Object.new) - return obj, ref.object_id - else - create_deep_obj(lvl - 1) - end - end - - def test_finalized_objects - refs, obj_id = create_deep_ref(100) - create_deep_ref(200) # erase the stack ... - GC.start - for ref in refs - assert_raises(WeakRef::RefError) { ref.get } - end - assert_equal(nil, WeakRef.refcount(obj_id)) - end - - def test_finalized_refs - _, ref_id = create_deep_obj(100) - create_deep_ref(100) # erase the stack ... - GC.start - assert_raises(RangeError) do - ref = ObjectSpace._id2ref(ref_id) - if !ref.kind_of?(WeakRef) - raise RangeError - end - end - end + def test_normal + obj = Object.new + ref = Utilrb::WeakRef.new(obj) + assert_equal(obj, ref.get) + end - def test_finalization_ordering_crash - create_deep_pair(100) - GC.start - end + def test_initialize_validation + ref = WeakRef.new(Object.new) + assert_raises(ArgumentError) { Utilrb::WeakRef.new(ref) } end end From 03856d387c2c5a77abc0ff3a2489149f89f38d5a Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 14 Sep 2015 08:27:24 -0300 Subject: [PATCH 27/67] object: singleton_class is built-in since 1.9, remove --- lib/utilrb/kernel/load_dsl_file.rb | 1 - lib/utilrb/logger/hierarchy.rb | 1 - lib/utilrb/object/singleton_class.rb | 21 +-------------------- 3 files changed, 1 insertion(+), 22 deletions(-) diff --git a/lib/utilrb/kernel/load_dsl_file.rb b/lib/utilrb/kernel/load_dsl_file.rb index 52af521..5e78a32 100644 --- a/lib/utilrb/kernel/load_dsl_file.rb +++ b/lib/utilrb/kernel/load_dsl_file.rb @@ -1,5 +1,4 @@ require 'utilrb/common' -require 'utilrb/object/singleton_class' require 'utilrb/kernel/with_module' module Kernel diff --git a/lib/utilrb/logger/hierarchy.rb b/lib/utilrb/logger/hierarchy.rb index f8c7ad6..9c23973 100644 --- a/lib/utilrb/logger/hierarchy.rb +++ b/lib/utilrb/logger/hierarchy.rb @@ -1,7 +1,6 @@ require 'facets/module/spacename' require 'facets/kernel/constant' require 'utilrb/object/attribute' -require 'utilrb/object/singleton_class' require 'utilrb/logger/forward' class Logger diff --git a/lib/utilrb/object/singleton_class.rb b/lib/utilrb/object/singleton_class.rb index a630cee..13911de 100644 --- a/lib/utilrb/object/singleton_class.rb +++ b/lib/utilrb/object/singleton_class.rb @@ -1,20 +1 @@ -require 'utilrb/common' -require 'utilrb/object/address' - -class Object - if !Object.new.respond_to?(:singleton_class) - # Returns the singleton class for this object. - # - # In Ruby 1.8, makes sure that the #superclass method of the singleton class - # returns the object's class (instead of Class), as Ruby 1.9 does - # - # The first element of #ancestors on the returned singleton class is - # the singleton class itself. A #singleton_instance accessor is also - # defined, which returns the object instance the class is the singleton - # of. - def singleton_class - class << self; self end - end - end -end - +puts "WARN: no need to require 'object/singleton_class' anymore, it is built-in since Ruby 1.9" From 436b2c4bf44c4942119ba9bb251960ada0359989 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 14 Sep 2015 08:28:02 -0300 Subject: [PATCH 28/67] object: #attribute does not depend on the extension anymore --- lib/utilrb/object/attribute.rb | 98 ++++++++++++---------------------- 1 file changed, 33 insertions(+), 65 deletions(-) diff --git a/lib/utilrb/object/attribute.rb b/lib/utilrb/object/attribute.rb index 88f8cc9..f8520f8 100644 --- a/lib/utilrb/object/attribute.rb +++ b/lib/utilrb/object/attribute.rb @@ -1,68 +1,36 @@ -require 'utilrb/common' -require 'utilrb/object/singleton_class' - -Utilrb.unless_ext do - class Object - # call-seq: - # attribute :name => default_value - # attribute(:name) { default_value } - # - # In the first form, defines a read-write attribute - # named 'name' with default_value for default value. - # In the second form, the block is called if the attribute - # is read before it has been ever written, and its return - # value is used as default value. - def attribute(attr_def, &init) - if Hash === attr_def - name, defval = attr_def.to_a.flatten - else - name = attr_def - end - - class_eval do - attr_writer name - define_method("#{name}_defval") do - defval || (instance_eval(&init) if init) - end - end - - class_eval <<-EOD, __FILE__, __LINE__+1 - def #{name} - if defined? @#{name} then @#{name} - else @#{name} = #{name}_defval - end - end - EOD - end - end -end - -Utilrb.if_ext do - class Object - def attribute(attr_def, &init) # :nodoc: - if Hash === attr_def - name, defval = attr_def.to_a.flatten - else - name = attr_def - end - - class_eval do - attr_writer name - if !defval && init - define_method("#{name}_defval", &init) - else - define_method("#{name}_defval") { defval } - end - end - - class_eval <<-EOD, __FILE__, __LINE__+1 - def #{name} - if instance_variable_defined?(:@#{name}) then @#{name} - else @#{name} = #{name}_defval - end - end - EOD - end +class Object + # call-seq: + # attribute :name => default_value + # attribute(:name) { default_value } + # + # In the first form, defines a read-write attribute + # named 'name' with default_value for default value. + # In the second form, the block is called if the attribute + # is read before it has been ever written, and its return + # value is used as default value. + def attribute(attr_def, &init) # :nodoc: + if Hash === attr_def + name, defval = attr_def.to_a.flatten + else + name = attr_def + end + + class_eval do + attr_writer name + if !defval && init + define_method("#{name}_defval", &init) + else + define_method("#{name}_defval") { defval } + end + end + + class_eval <<-EOD, __FILE__, __LINE__+1 + def #{name} + if instance_variable_defined?(:@#{name}) then @#{name} + else @#{name} = #{name}_defval + end + end + EOD end end From 51b91ecaa84d69d12ad7e7daa1679175674b1c07 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 14 Sep 2015 08:31:05 -0300 Subject: [PATCH 29/67] deprecate ColumnFormatter Use tty-table instead. --- lib/utilrb/column_formatter.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/utilrb/column_formatter.rb b/lib/utilrb/column_formatter.rb index f22bab6..e589e27 100644 --- a/lib/utilrb/column_formatter.rb +++ b/lib/utilrb/column_formatter.rb @@ -1,3 +1,6 @@ +puts "WARN: ColumnFormatter is deprecated, use the tty-table gem which provides" +puts "WARN: more functionality in a much nicer API" + # Displays a set of data into a column-formatted table class ColumnFormatter MARGIN = 1 From 7e2459ea85b5b49791c7a0a3192fc21708a9fa82 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 14 Sep 2015 14:45:27 -0300 Subject: [PATCH 30/67] remove long-unused update_github script --- update_github | 40 ---------------------------------------- 1 file changed, 40 deletions(-) delete mode 100755 update_github diff --git a/update_github b/update_github deleted file mode 100755 index 9df5575..0000000 --- a/update_github +++ /dev/null @@ -1,40 +0,0 @@ -#! /bin/sh - -PAGES_BRANCH=gh-pages -DOC_DIR=doc - -set -e - -git reset -q HEAD - -# Create the tree object -git add -f $DOC_DIR -tree=$(git write-tree --prefix=$DOC_DIR) - -# Get the parent commit for the tree. If the branch does not yet exist, create -# it -msg="generated from $head_sha" -head_sha=$(git show-ref -s -h HEAD) -if git show-ref -q --verify refs/heads/$PAGES_BRANCH; then - parent=$(git show-ref -s refs/heads/$PAGES_BRANCH) - - # Check that some things changes since last time - last_tree=$(git cat-file commit refs/heads/$PAGES_BRANCH | grep tree | awk '{print $2}') - if test "x$last_tree" = "x$tree"; then - echo "no changes to commit" - else - echo "updating $PAGES_BRANCH" - commit=$(echo $msg | git commit-tree $tree -p $parent) - fi -else - echo "creating initial commit on the $PAGES_BRANCH branch" - commit=$(echo $msg | git commit-tree $tree) -fi - -if test -n "$commit"; then - # And finally update the tip of the branch - echo $commit > .git/refs/heads/$PAGES_BRANCH -fi - -git reset -q HEAD - From 81a63766de4ada9745a557ecfac79d6197456cb7 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 17 Sep 2015 13:03:08 -0300 Subject: [PATCH 31/67] Released flexmock 2.0, no need to checkout the git --- Gemfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Gemfile b/Gemfile index 5b09bf2..fa75df1 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,3 @@ source 'https://rubygems.org' -gem 'flexmock', github: 'doudou/flexmock' gemspec From 455c2e580dae54222416f9df97113d26dc7fb48c Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Tue, 22 Sep 2015 16:34:49 -0300 Subject: [PATCH 32/67] we now depend on bundler --- manifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/manifest.xml b/manifest.xml index 945575c..fff85f5 100644 --- a/manifest.xml +++ b/manifest.xml @@ -11,6 +11,7 @@ CeCILL-B (BSD-like) + From 1b9209b7918424525fc721a2b95ac767c300a91b Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Fri, 25 Sep 2015 08:57:28 -0300 Subject: [PATCH 33/67] clean up README --- README.rd => README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) rename README.rd => README.md (51%) diff --git a/README.rd b/README.md similarity index 51% rename from README.rd rename to README.md index 1ae0952..452e2c6 100644 --- a/README.rd +++ b/README.md @@ -1,18 +1,22 @@ -= Utilrb +[![Build Status](https://travis-ci.org/orocos-toolchain/utilrb.svg?branch=master)](https://travis-ci.org/orocos-toolchain/utilrb) +[![Gem Version](https://badge.fury.io/rb/utilrb.svg)](http://badge.fury.io/rb/utilrb) +[![Documentation](http://b.repl.ca/v1/yard-docs-blue.png)](http://rubydoc.info/gems/utilrb/frames) + +Utilrb +------ homepage :: http://rock-robotics.org mailing list :: http://www.dfki.de/mailman/cgi-bin/listinfo/rock-dev -bug tracker :: trac.rock-robotics.org -rubygem page :: http://rubygems.org/gems/utilrb -git repository :: http://gitorious.org/orocos-toolchain/utilrb.git -API documentation :: http://rubydoc.info/gems/utilrb/1.6.6/frames +bug tracker :: https://github.com/orocos-toolchain/utilrb -== DESCRIPTION +DESCRIPTION +----------- Utilrb is yet another Ruby toolkit, in the spirit of facets. It includes all the standard class extensions I use in other projects. -== LICENSE +LICENSE +------- Copyright (c) 2006-2013 Sylvain Joyeux From c8bf58246be75e548f6a7fe83413290fa64539fa Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Fri, 25 Sep 2015 08:50:44 -0300 Subject: [PATCH 34/67] use a pure-ruby implementation of singleton_class? It only applies to ruby 2.0, and I cache the result to avoid the cost of #ancestors anyways. --- ext/utilrb/utilrb.cc | 16 ---------------- lib/utilrb/module/singleton_class_p.rb | 11 ++++++++++- test/test_module.rb | 9 ++++++--- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/ext/utilrb/utilrb.cc b/ext/utilrb/utilrb.cc index b9f6084..2a91e5e 100644 --- a/ext/utilrb/utilrb.cc +++ b/ext/utilrb/utilrb.cc @@ -28,19 +28,6 @@ static VALUE enumerable_each_uniq(VALUE self) return self; } -/* call-seq: - * Kernel.is_singleton?(object) - * - * Returns true if +self+ is a singleton class - */ -static VALUE kernel_is_singleton_p(VALUE self) -{ - if (BUILTIN_TYPE(self) == T_CLASS && FL_TEST(self, FL_SINGLETON)) - return Qtrue; - else - return Qfalse; -} - static VALUE kernel_is_immediate(VALUE klass, VALUE object) { return IMMEDIATE_P(object) ? Qtrue : Qfalse; } #endif @@ -60,9 +47,6 @@ extern "C" void Init_utilrb() #ifndef RUBINIUS rb_define_method(rb_mEnumerable, "each_uniq", RUBY_METHOD_FUNC(enumerable_each_uniq), 0); - if (!rb_respond_to(rb_mEnumerable, rb_intern("singleton_class?"))) - rb_define_method(rb_cModule, "singleton_class?", RUBY_METHOD_FUNC(kernel_is_singleton_p), 0); - rb_define_singleton_method(rb_mKernel, "crash!", RUBY_METHOD_FUNC(kernel_crash), 0); rb_define_singleton_method(rb_mKernel, "immediate?", RUBY_METHOD_FUNC(kernel_is_immediate), 1); #endif diff --git a/lib/utilrb/module/singleton_class_p.rb b/lib/utilrb/module/singleton_class_p.rb index 48cfe66..5893d8e 100644 --- a/lib/utilrb/module/singleton_class_p.rb +++ b/lib/utilrb/module/singleton_class_p.rb @@ -1,5 +1,14 @@ class Module if !method_defined?(:singleton_class?) - require 'utilrb/utilrb' + # It so happens that this method to determine whether a class is a + # singleton class is valid for ruby 2.0 and breaks on 2.1 ... However + # (!) on 2.1 singleton_class? is defined + def singleton_class? + if instance_variable_defined?(:@__utilrb_singleton_class) + @__utilrb_singleton_class + else + @__utilrb_singleton_class = (ancestors.first != self) + end + end end end diff --git a/test/test_module.rb b/test/test_module.rb index cacaac0..6a2fefe 100644 --- a/test/test_module.rb +++ b/test/test_module.rb @@ -122,11 +122,14 @@ def test_dsl_attribute_with_filter assert_equal 20, obj.value end - def test_is_singleton + def test_singleton_class_p m = Module.new - assert !m.is_singleton? + assert !m.singleton_class? + k = Class.new + assert !k.singleton_class? s = Object.new.singleton_class - assert s.is_singleton? + assert s.singleton_class? + assert s.singleton_class? end end From 1eb3b1a601c2516dd253a40591902914977336e4 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Fri, 25 Sep 2015 09:21:06 -0300 Subject: [PATCH 35/67] fix WeakRef#initialize when a dead ref is passed as argument WeakRef is a BasicObject, and as such isolates all constants and methods. We therefore have to resolve constants globally and raise as Kernel.raise, otherwise it tries to go through method_missing and fails. --- lib/utilrb/weakref.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utilrb/weakref.rb b/lib/utilrb/weakref.rb index 228f19a..58abebc 100644 --- a/lib/utilrb/weakref.rb +++ b/lib/utilrb/weakref.rb @@ -4,7 +4,7 @@ module Utilrb class WeakRef < ::WeakRef def initialize(obj) if obj.kind_of?(::WeakRef) - raise ArgumentError, "cannot create a weakref of a weakref" + Kernel.raise ::ArgumentError, "cannot create a weakref of a weakref" end super end From 96ed2fb5c8a30284804cbd4ccd2760f57ed0dd61 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 26 Sep 2015 14:42:11 -0300 Subject: [PATCH 36/67] release 2.1.0.rc1 --- lib/utilrb/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utilrb/version.rb b/lib/utilrb/version.rb index b8b55f6..8be4cb3 100644 --- a/lib/utilrb/version.rb +++ b/lib/utilrb/version.rb @@ -1,4 +1,4 @@ module Utilrb - VERSION = "2.1.0" + VERSION = "2.1.0.rc1" end From e9a0df70215361ec83ee7106ff7883293f92dc0e Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 26 Sep 2015 14:47:45 -0300 Subject: [PATCH 37/67] fix gemspec to build the extension --- lib/utilrb/version.rb | 2 +- utilrb.gemspec | 36 ++++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/utilrb/version.rb b/lib/utilrb/version.rb index 8be4cb3..3968485 100644 --- a/lib/utilrb/version.rb +++ b/lib/utilrb/version.rb @@ -1,4 +1,4 @@ module Utilrb - VERSION = "2.1.0.rc1" + VERSION = "2.1.0.rc2" end diff --git a/utilrb.gemspec b/utilrb.gemspec index 9ad6d4d..09811d5 100644 --- a/utilrb.gemspec +++ b/utilrb.gemspec @@ -4,24 +4,24 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'utilrb/version' Gem::Specification.new do |s| - s.name = "utilrb" - s.version = Utilrb::VERSION - s.authors = ["Sylvain Joyeux"] - s.email = "sylvain.joyeux@m4x.org" - s.summary = "Utilrb is yet another Ruby toolkit, in the spirit of facets" - s.description = "Utilrb is yet another Ruby toolkit, in the spirit of facets. It includes all\nthe standard class extensions I use in other projects." - s.homepage = "http://rock-robotics.org" - s.licenses = ["BSD"] + s.name = "utilrb" + s.version = Utilrb::VERSION + s.authors = ["Sylvain Joyeux"] + s.email = "sylvain.joyeux@m4x.org" + s.summary = "Utilrb is yet another Ruby toolkit, in the spirit of facets" + s.description = "Utilrb is yet another Ruby toolkit, in the spirit of facets. It includes all\nthe standard class extensions I use in other projects." + s.homepage = "http://rock-robotics.org" + s.licenses = ["BSD"] - s.require_paths = ["lib"] - s.extensions = [] - s.extra_rdoc_files = ["History.txt", "License.txt", "Manifest.txt", "History.txt"] - s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + s.require_paths = ["lib"] + s.extensions = ['ext/utilrb/extconf.rb'] + s.extra_rdoc_files = ["History.txt", "License.txt", "Manifest.txt", "History.txt"] + s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } - s.add_runtime_dependency "facets", ">= 2.4.0" - s.add_runtime_dependency "rake", ">= 0.9" - s.add_runtime_dependency "rake-compiler", "~> 0.8.0" - s.add_development_dependency "flexmock", ">= 2.0.0" - s.add_development_dependency "minitest", ">= 5.0", "~> 5.0" - s.add_development_dependency "coveralls" + s.add_runtime_dependency "facets", ">= 2.4.0" + s.add_runtime_dependency "rake", ">= 0.9" + s.add_runtime_dependency "rake-compiler", "~> 0.8.0" + s.add_development_dependency "flexmock", ">= 2.0.0" + s.add_development_dependency "minitest", ">= 5.0", "~> 5.0" + s.add_development_dependency "coveralls" end From c8a62c60d664132c8f29460b834c89044d68c89e Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 26 Sep 2015 16:17:07 -0300 Subject: [PATCH 38/67] travis: migrate to the container-based infrastructure --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ce0c05a..1a91d3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +sudo: false language: ruby rvm: - 2.0.0 From e74a7eaa504d38f0088e94449e3b3b401da4d2da Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sat, 26 Sep 2015 16:20:32 -0300 Subject: [PATCH 39/67] travis: the rake default task does not run tests, update travis conf --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1a91d3f..6efab46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,3 +4,6 @@ rvm: - 2.0.0 - 2.1.6 - 2.2.2 +script: + - bundle exec rake + - bundle exec rake test From 6bee244c6d2d2b2683131afdaee8d9ebdff4fae6 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sun, 27 Sep 2015 15:24:02 -0300 Subject: [PATCH 40/67] make the thread pool test suite timing-clean The current test suite was using timing (!) as a way to check for concurrency, with the horrible result that one would expect. This uses proper synchronization primitives, which helped catch a bug or two in the process. --- lib/utilrb/thread_pool.rb | 24 ++- test/test_thread_pool.rb | 417 ++++++++++++++++++++------------------ 2 files changed, 235 insertions(+), 206 deletions(-) diff --git a/lib/utilrb/thread_pool.rb b/lib/utilrb/thread_pool.rb index feada11..d460564 100644 --- a/lib/utilrb/thread_pool.rb +++ b/lib/utilrb/thread_pool.rb @@ -241,10 +241,10 @@ def callback(&block) # @return[Float] def time_elapsed(time = Time.now) #no need to synchronize here - if running? - (time-@started_at).to_f - elsif finished? + if @stopped_at (@stopped_at-@started_at).to_f + elsif @started_at + (time-@started_at).to_f else 0 end @@ -361,8 +361,8 @@ def resize (min, max = nil) @mutex.synchronize do @min = min @max = max || min - count = [@tasks_waiting.size,@max].min - 0.upto(count) do + count = [@tasks_waiting.size,@max - @spawned].min + count.times do spawn_thread end end @@ -373,7 +373,7 @@ def resize (min, max = nil) # # @return [Fixnum] the number of tasks def backlog - @mutex.synchronize do + @mutex.synchronize do @tasks_waiting.length end end @@ -514,12 +514,12 @@ def trim (force = false) # Shuts down all threads. # - def shutdown() + def shutdown tasks = nil @mutex.synchronize do @shutdown = true + @cond.broadcast end - @cond.broadcast end @@ -527,7 +527,13 @@ def shutdown() # This does not terminate any thread by itself and will block for ever # if shutdown was not called. def join - @workers.first.join until @workers.empty? + while true + if w = @mutex.synchronize { @workers.first } + w.join + else + break + end + end self end diff --git a/test/test_thread_pool.rb b/test/test_thread_pool.rb index 82cd0f2..7a41e85 100644 --- a/test/test_thread_pool.rb +++ b/test/test_thread_pool.rb @@ -1,240 +1,258 @@ require 'utilrb/test' require 'utilrb/thread_pool' require 'minitest/spec' +require 'flexmock/minitest' -describe Utilrb::ThreadPool do - describe "when created" do - it "must create min number of threads." do - pool = Utilrb::ThreadPool.new(5) - sleep 0.1 - assert_equal 5,pool.waiting - assert_equal 5,pool.spawned - assert_equal 5,(pool.instance_variable_get(:@workers)).size - pool.shutdown - pool.join +module AsyncWorkHelper + attr_reader :mutex, :cv, :pool + + def setup + super + @mutex = Mutex.new + @cv = ConditionVariable.new + @active_synchronized_workers_count = 0 + @spawned_synchronized_workers_count = 0 + end + + def teardown + while (current = spawned_synchronized_workers_count) > 0 + wait_until do + release_synchronized_workers + assert current != spawned_synchronized_workers_count + end end - it "must create min number of threads." do - pool = Utilrb::ThreadPool.new(5) - sleep 0.1 - assert_equal 5,pool.waiting - assert_equal 5,pool.spawned - assert_equal 5,(pool.instance_variable_get(:@workers)).size + if pool pool.shutdown pool.join end + super end - describe "when under heavy load" do - it "must spawn upto max threads." do - pool = Utilrb::ThreadPool.new(5,20) - 0.upto 19 do - pool.process do - sleep 0.12 + def active_synchronized_workers_count + mutex.synchronize { @active_synchronized_workers_count } + end + + def spawned_synchronized_workers_count + mutex.synchronize { @spawned_synchronized_workers_count } + end + + def wait_synchronized_workers(count) + wait_until do + assert_equal count, active_synchronized_workers_count + end + end + + def wait_until(timeout: 5, &block) + start = Time.now + while true + begin + block.call + return + rescue Minitest::Assertion + if (Time.now - start > timeout) + raise end end - assert_equal 20,pool.backlog - assert_equal 20,pool.tasks.size - sleep 0.1 - assert_equal 0,pool.backlog - assert_equal 0,pool.waiting - assert_equal 20,pool.spawned - sleep 0.1 - assert_equal 20,pool.waiting - assert_equal 20,pool.spawned - pool.shutdown - pool.join + Thread.pass end + block.call + end - it "must be possible to resize its limit" do - pool = Utilrb::ThreadPool.new(5,5) - 0.upto 19 do - pool.process do - sleep 0.32 - end + def synchronized_work + mutex.synchronize do + @active_synchronized_workers_count += 1 + begin + cv.wait(mutex) + ensure + @active_synchronized_workers_count -= 1 end - sleep 0.1 - assert_equal 0,pool.waiting - assert_equal 5,pool.spawned - pool.resize(5,20) - sleep 0.1 - assert_equal 0,pool.waiting - assert_equal 20,pool.spawned - sleep 0.4 - assert_equal 20,pool.spawned - assert_equal 20,pool.waiting - pool.shutdown - pool.join end + end - it "must reduce its number of threads after the work is done if auto_trim == true." do - pool = Utilrb::ThreadPool.new(5,20) - pool.auto_trim = true + def release_synchronized_workers + cv.broadcast + end +end + +describe Utilrb::ThreadPool do + include AsyncWorkHelper + + let(:spy) { flexmock } - sync = Mutex.new - cond = ConditionVariable.new - waiting = 0 - start = Time.now - 0.upto 19 do |i| - pool.process do - sync.synchronize do - waiting += 1 - cond.wait(sync) - end + def create_pool(*args) + if pool + raise RuntimeError, "pool already created, call #shutdown_pool first" + end + @pool = Utilrb::ThreadPool.new(*args) + end + + def shutdown_pool + pool.shutdown + pool.join + end + + def spawn_synchronized_workers(count, &spawner) + spawner ||= lambda { |p, &b| p.process(&b) } + + count.times do + mutex.synchronize do + @spawned_synchronized_workers_count += 1 + end + spawner.call(pool) do + synchronized_work + mutex.synchronize do + @spawned_synchronized_workers_count -= 1 end end - while waiting != 20 && (Time.now - start) < 5 - Thread.pass - end - sync.synchronize do - assert_equal 0,pool.waiting - assert_equal 20,pool.spawned - cond.broadcast - end + end + end - start = Time.now - while (pool.waiting != 5 || pool.spawned != 5) && (Time.now - start) < 2 - Thread.pass - end - assert_equal 5,pool.waiting - assert_equal 5,pool.spawned - pool.shutdown - pool.join + describe "when created" do + it "must create min number of threads." do + create_pool 5 + wait_until { assert_equal 5, pool.waiting } + assert_equal 5, pool.waiting + assert_equal 5, pool.spawned end + end - it "must not execute tasks with the same sync key in parallel" do - pool = Utilrb::ThreadPool.new(5,10) + describe "when under heavy load" do + it "must spawn max threads." do + create_pool 0, 10 + spawn_synchronized_workers(15) + wait_synchronized_workers(10) + assert_equal 0, pool.waiting + assert_equal 5,pool.backlog + assert_equal 10,pool.spawned + release_synchronized_workers + end + + it "must be possible to resize the max limit" do + create_pool 0, 5 + spawn_synchronized_workers(10) + wait_synchronized_workers(5) + pool.resize(0, 8) + wait_synchronized_workers(8) + assert_equal 2,pool.backlog + assert_equal 0,pool.waiting + assert_equal 8,pool.spawned + end + + it "must reduce its number of threads after the work is done if auto_trim == true." do + create_pool 2, 5 pool.auto_trim = true - time = Time.now - 0.upto 10 do - t = pool.process_with_options :sync_key => time do - sleep 0.1 - end - assert_equal time, t.sync_key + spawn_synchronized_workers(8) + wait_synchronized_workers(5) + release_synchronized_workers + wait_synchronized_workers(3) + assert_equal 3, pool.spawned + release_synchronized_workers + wait_synchronized_workers(0) + assert_equal 2, pool.spawned + end + + it "must not execute tasks with the same sync key in parallel" do + key = Object.new + create_pool 10 + spawn_synchronized_workers 10 do |pool, &w| + w = pool.process_with_options(sync_key: key, &w) + assert_equal key, w.sync_key end - while pool.backlog > 0 - sleep 0.1 + 10.times do |i| + wait_synchronized_workers 1 + wait_until { assert_equal (10 - i), spawned_synchronized_workers_count } + release_synchronized_workers end - pool.shutdown - pool.join - assert Time.now - time >= 1.0 end it "must not execute a task and a sync call in parallel if they have the same sync key" do - pool = Utilrb::ThreadPool.new(5,5) - time = Time.now - pool.process_with_options :sync_key => 1 do - sleep 0.2 - end - pool.sync 1 do - sleep 0.2 - end - while pool.backlog > 0 - sleep 0.1 - end - pool.shutdown - pool.join - assert Time.now - time >= 0.4 + key = Object.new + create_pool 2 + sync_call_thread = Thread.new do + pool.sync(key, &method(:synchronized_work)) + end + wait_synchronized_workers 1 + spawn_synchronized_workers 1 do |pool, &w| + pool.process_with_options(sync_key: key, &w) + end + assert_equal 1, pool.backlog + release_synchronized_workers + wait_synchronized_workers 1 end it "must execute a task and a sync call in parallel if they have different sync keys" do - pool = Utilrb::ThreadPool.new(5,5) - time = Time.now - pool.process_with_options :sync_key => 1 do - sleep 0.2 + create_pool 2 + sync_call_thread = Thread.new do + pool.sync(Object.new, &method(:synchronized_work)) end - pool.sync 2 do - sleep 0.2 + wait_synchronized_workers 1 + spawn_synchronized_workers 1 do |pool, &w| + pool.process_with_options(sync_key: Object.new, &w) end - while pool.backlog > 0 - sleep 0.1 - end - pool.shutdown - pool.join - assert Time.now - time < 0.4 + wait_synchronized_workers 2 + release_synchronized_workers end end describe "when running" do it "must call on_task_finished for each finised task." do - pool = Utilrb::ThreadPool.new(5) + create_pool 2 count = 0 - pool.on_task_finished do |task| - count += 1 + pool.on_task_finished { |task| count += 1 } + pool.process { } + pool.process { raise } + wait_until do + assert_equal 0, pool.backlog + assert_equal 2, pool.waiting end - task = pool.process do - sleep 0.05 - end - task = pool.process do - raise - end - sleep 0.1 - assert_equal 2,count - pool.shutdown - pool.join + assert_equal 2, count end it "must be able to reque a task" do - pool = Utilrb::ThreadPool.new(5) - count = 0 - task = pool.process do - count += 1 - end - while !task.finished? - sleep 0.001 - end - pool << task - while !task.finished? - sleep 0.001 - end - assert_equal 2, count + create_pool 1 + spy.should_receive(:call).twice + task = pool.process { spy.call } + wait_until { assert task.finished? } + pool << task + wait_until { assert task.finished? } end it "must process the next task if thread gets available" do - pool = Utilrb::ThreadPool.new(1) - count = 0 - pool.process do - sleep 0.1 - count +=1 - end - pool.process do - sleep 0.1 - count +=1 + create_pool 1 + spy.should_expect do |r| + r.call(1).once + r.call(2).once + r.call(3).once + r.call(4).once end - sleep 0.25 - assert_equal 2, count - task3 = Utilrb::ThreadPool::Task.new do - count +=1 - sleep 0.1 - end - task4 = Utilrb::ThreadPool::Task.new do - count +=1 - sleep 0.1 + pool.process { spy.call(1) } + pool.process { spy.call(2) } + pool << Utilrb::ThreadPool::Task.new { spy.call(3) } + pool << Utilrb::ThreadPool::Task.new { spy.call(4) } + wait_until do + assert_equal 0, pool.backlog + assert_equal 1, pool.waiting end - pool << task3 - pool << task4 - sleep 0.15 - pool.shutdown - pool.join - assert_equal 4, count end end describe "when shutting down" do - it "must terminate all threads" do - pool = Utilrb::ThreadPool.new(5) - pool.process do - sleep 0.2 - end - pool.shutdown() + it "terminates all threads" do + create_pool 5 + assert_equal 5, pool.spawned + pool.shutdown pool.join + assert_equal 0, pool.spawned end end end describe Utilrb::ThreadPool::Task do + include AsyncWorkHelper + describe "when created" do it "must raise if no block is given." do assert_raises(ArgumentError) do @@ -254,12 +272,12 @@ end it "must raise if wrong option is given." do assert_raises ArgumentError do - Utilrb::ThreadPool::Task.new :bla => 123 do + Utilrb::ThreadPool::Task.new bla: 123 do end end end it "must set its options." do - task = Utilrb::ThreadPool::Task.new :sync_key => 2 do + task = Utilrb::ThreadPool::Task.new sync_key: 2 do 123 end assert_equal 2,task.sync_key @@ -357,35 +375,40 @@ end it "must calculate its elapsed time." do + time = Time.now + flexmock(Time).should_receive(:now).and_return { time } task = Utilrb::ThreadPool::Task.new do - sleep 0.2 + time += 0.1 end - assert_in_delta 0.0,task.time_elapsed,0.0001 - thread = Thread.new do - task.pre_execute - task.execute - task.finalize - end - sleep 0.1 - assert_in_delta 0.1,task.time_elapsed,0.01 - thread.join - assert_in_delta 0.2,task.time_elapsed,0.001 - sleep 0.1 - assert_in_delta 0.2,task.time_elapsed,0.001 + assert_equal 0, task.time_elapsed + + task.pre_execute + time += 0.1 + assert_in_delta 0.1, task.time_elapsed, 0.001 + task.execute + assert_in_delta 0.2, task.time_elapsed, 0.001 + time += 0.1 + assert_in_delta 0.2, task.time_elapsed, 0.001 + task.finalize + time += 0.1 + assert_in_delta 0.2, task.time_elapsed, 0.001 end end describe "when terminated" do - it "it must be in terminated state." do - task = Utilrb::ThreadPool::Task.new do - sleep 10 + it "must be in terminated state." do + m, c = Mutex.new, ConditionVariable.new + task = Utilrb::ThreadPool::Task.new do + m.synchronize { c.wait(m) } end thread = Thread.new do task.pre_execute task.execute task.finalize end - sleep 0.1 + wait_until do + assert task.running? + end task.terminate! thread.join @@ -401,15 +424,15 @@ describe "when terminated" do it "must be in state terminated." do task = Utilrb::ThreadPool::Task.new do - sleep 10 + synchronized_work end thread = Thread.new do task.pre_execute task.execute task.finalize end - sleep 0.1 - task.terminate!() + wait_synchronized_workers 1 + task.terminate! thread.join assert !task.running? From 727459c9d9837dc47102023c4e71f496ff7e9543 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sun, 27 Sep 2015 15:32:38 -0300 Subject: [PATCH 41/67] release 2.1.0.rc3 --- lib/utilrb/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utilrb/version.rb b/lib/utilrb/version.rb index 3968485..f323059 100644 --- a/lib/utilrb/version.rb +++ b/lib/utilrb/version.rb @@ -1,4 +1,4 @@ module Utilrb - VERSION = "2.1.0.rc2" + VERSION = "2.1.0.rc3" end From 418ee801494eeb4fb6e7f0144df49c24c88a920f Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Fri, 25 Sep 2015 09:19:51 -0300 Subject: [PATCH 42/67] extract the C extension into a separate gem --- Rakefile | 10 +- ext/utilrb/extconf.rb | 22 -- ext/utilrb/ruby_allocator.hh | 76 ------ ext/utilrb/utilrb.cc | 56 ----- ext/utilrb/value_set.cc | 431 ---------------------------------- lib/utilrb/common.rb | 45 ---- lib/utilrb/enumerable/uniq.rb | 28 +-- lib/utilrb/value_set.rb | 49 ---- test/test_enumerable.rb | 2 - test/test_proc.rb | 31 --- test/test_value_set.rb | 72 ------ test/test_weakref.rb | 2 +- utilrb.gemspec | 1 - 13 files changed, 15 insertions(+), 810 deletions(-) delete mode 100644 ext/utilrb/extconf.rb delete mode 100644 ext/utilrb/ruby_allocator.hh delete mode 100644 ext/utilrb/utilrb.cc delete mode 100644 ext/utilrb/value_set.cc delete mode 100644 lib/utilrb/value_set.rb delete mode 100644 test/test_proc.rb delete mode 100644 test/test_value_set.rb diff --git a/Rakefile b/Rakefile index 29bdd96..4ce15ad 100644 --- a/Rakefile +++ b/Rakefile @@ -1,15 +1,7 @@ require "bundler/gem_tasks" require "rake/testtask" -require 'rake/extensiontask' -Rake::ExtensionTask.new('utilrb') do |ext| - ext.name = 'utilrb' - ext.ext_dir = 'ext/utilrb' - ext.lib_dir = 'lib/utilrb' - ext.source_pattern ="*.{c,cc,cpp}" -end - -task :default => :compile +task :default Rake::TestTask.new(:test) do |t| t.libs << "test" diff --git a/ext/utilrb/extconf.rb b/ext/utilrb/extconf.rb deleted file mode 100644 index 85c7046..0000000 --- a/ext/utilrb/extconf.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'mkmf' - -CONFIG['LDSHARED'].gsub! '$(CC)', "$(CXX)" -if try_link("int main() { }", "-module") - $LDFLAGS += " -module" -end - -create_makefile("utilrb/utilrb") - -## WORKAROUND a problem with mkmf.rb -# It seems that the newest version do define an 'install' target. However, that -# install target tries to install in the system directories -# -# The issue is that RubyGems *does* call make install. Ergo, gem install utilrb -# is broken right now -#lines = File.readlines("Makefile") -#lines.delete_if { |l| l =~ /^install:/ } -#lines << "install:" -#File.open("Makefile", 'w') do |io| -# io.write lines.join("\n") -#end - diff --git a/ext/utilrb/ruby_allocator.hh b/ext/utilrb/ruby_allocator.hh deleted file mode 100644 index 34c0397..0000000 --- a/ext/utilrb/ruby_allocator.hh +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef RUBY_ALLOCATOR_HH -#define RUBY_ALLOCATOR_HH - -#include - -template class ruby_allocator -{ -public: - typedef T value_type; - typedef value_type* pointer; - typedef const value_type* const_pointer; - typedef value_type& reference; - typedef const value_type& const_reference; - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; - - template - struct rebind { typedef ruby_allocator other; }; - - ruby_allocator() {} - ruby_allocator(const ruby_allocator&) {} - template - ruby_allocator(const ruby_allocator&) {} - ~ruby_allocator() {} - - pointer address(reference x) const { return &x; } - const_pointer address(const_reference x) const { - return x; - } - - pointer allocate(size_type n, const_pointer = 0) { - void* p = ruby_xmalloc(n * sizeof(T)); - if (!p) - throw std::bad_alloc(); - return static_cast(p); - } - - void deallocate(pointer p, size_type) { ruby_xfree(p); } - - size_type max_size() const { - return static_cast(-1) / sizeof(T); - } - - void construct(pointer p, const value_type& x) { - new(p) value_type(x); - } - void destroy(pointer p) { p->~value_type(); } - -private: - void operator=(const ruby_allocator&); -}; - -template<> class ruby_allocator -{ - typedef void value_type; - typedef void* pointer; - typedef const void* const_pointer; - - template - struct rebind { typedef ruby_allocator other; }; -}; - - -template -inline bool operator==(const ruby_allocator&, - const ruby_allocator&) { - return true; -} - -template -inline bool operator!=(const ruby_allocator&, - const ruby_allocator&) { - return false; -} - -#endif diff --git a/ext/utilrb/utilrb.cc b/ext/utilrb/utilrb.cc deleted file mode 100644 index 2a91e5e..0000000 --- a/ext/utilrb/utilrb.cc +++ /dev/null @@ -1,56 +0,0 @@ -#include -#include - -static VALUE mUtilrb; - -using namespace std; - -#ifndef RUBINIUS -static VALUE enumerable_each_uniq_i(VALUE i, VALUE* memo) -{ - set& seen = *reinterpret_cast< set* >(memo); - if (seen.find(i) == seen.end()) - { - seen.insert(i); - return rb_yield(i); - } - else - return Qnil; - -} - -/* :nodoc: */ -static VALUE enumerable_each_uniq(VALUE self) -{ - set seen; - rb_iterate(rb_each, self, - RUBY_METHOD_FUNC(enumerable_each_uniq_i), (VALUE)&seen); - return self; -} - -static VALUE kernel_is_immediate(VALUE klass, VALUE object) -{ return IMMEDIATE_P(object) ? Qtrue : Qfalse; } -#endif - -static VALUE kernel_crash(VALUE klass) -{ - *((int*)0) = 10; - // Return something to shut gcc up - return Qfalse; -} - -extern "C" void Init_value_set(); - -extern "C" void Init_utilrb() -{ - mUtilrb = rb_define_module("Utilrb"); - -#ifndef RUBINIUS - rb_define_method(rb_mEnumerable, "each_uniq", RUBY_METHOD_FUNC(enumerable_each_uniq), 0); - rb_define_singleton_method(rb_mKernel, "crash!", RUBY_METHOD_FUNC(kernel_crash), 0); - rb_define_singleton_method(rb_mKernel, "immediate?", RUBY_METHOD_FUNC(kernel_is_immediate), 1); -#endif - - Init_value_set(); -} - diff --git a/ext/utilrb/value_set.cc b/ext/utilrb/value_set.cc deleted file mode 100644 index 6e3dc5b..0000000 --- a/ext/utilrb/value_set.cc +++ /dev/null @@ -1,431 +0,0 @@ -#include -#include -#include -#include "ruby_allocator.hh" - -using namespace std; - -static VALUE cValueSet; -static ID id_new; - -typedef std::set, ruby_allocator > ValueSet; -static ValueSet& get_wrapped_set(VALUE self) -{ - ValueSet* object = 0; - Data_Get_Struct(self, ValueSet, object); - return *object; -} - -static void value_set_mark(ValueSet const* set) { std::for_each(set->begin(), set->end(), rb_gc_mark); } -static void value_set_free(ValueSet const* set) { delete set; } -static VALUE value_set_alloc(VALUE klass) -{ - ValueSet* cxx_set = new ValueSet; - return Data_Wrap_Struct(klass, value_set_mark, value_set_free, cxx_set); -} -/* call-seq: - * set.empty? => true or false - * - * Checks if this set is empty - */ -static VALUE value_set_empty_p(VALUE self) -{ - ValueSet& set = get_wrapped_set(self); - return set.empty() ? Qtrue : Qfalse; -} - -/* call-seq: - * set.size => size - * - * Returns this set size - */ -static VALUE value_set_size(VALUE self) -{ - ValueSet& set = get_wrapped_set(self); - return INT2NUM(set.size()); -} - - -/* call-seq: - * set.each { |obj| ... } => set - * - */ -static VALUE value_set_each(VALUE self) -{ - ValueSet& set = get_wrapped_set(self); - for (ValueSet::iterator it = set.begin(); it != set.end();) - { - // Increment before calling yield() so that - // the current element can be deleted safely - ValueSet::iterator this_it = it++; - rb_yield(*this_it); - } - return self; -} - -/* call-seq: - * set.delete_if { |obj| ... } => set - * - * Deletes all objects for which the block returns true - */ -static VALUE value_set_delete_if(VALUE self) -{ - ValueSet& set = get_wrapped_set(self); - for (ValueSet::iterator it = set.begin(); it != set.end();) - { - // Increment before calling yield() so that - // the current element can be deleted safely - ValueSet::iterator this_it = it++; - bool do_delete = RTEST(rb_yield(*this_it)); - if (do_delete) - set.erase(this_it); - } - return self; -} - -/* call-seq: - * set.include?(value) => true or false - * - * Checks if +value+ is in +set+ - */ -static VALUE value_set_include_p(VALUE vself, VALUE vother) -{ - ValueSet const& self = get_wrapped_set(vself); - return self.find(vother) == self.end() ? Qfalse : Qtrue; -} - -/* call-seq: - * set.to_value_set => set - */ -static VALUE value_set_to_value_set(VALUE self) { return self; } - -/* call-seq: - * set.dup => other_set - * - * Duplicates this set, without duplicating the pointed-to objects - */ -static VALUE value_set_dup(VALUE vself, VALUE vother) -{ - ValueSet const& self = get_wrapped_set(vself); - VALUE vresult = rb_funcall2(cValueSet, id_new, 0, NULL); - ValueSet& result = get_wrapped_set(vresult); - for (ValueSet::const_iterator it = self.begin(); it != self.end(); ++it) - result.insert(result.end(), *it); - - return vresult; -} - -/* call-seq: - * set.include_all?(other) => true or false - * - * Checks if all elements of +other+ are in +set+ - */ -static VALUE value_set_include_all_p(VALUE vself, VALUE vother) -{ - ValueSet const& self = get_wrapped_set(vself); - if (!RTEST(rb_obj_is_kind_of(vother, cValueSet))) - rb_raise(rb_eArgError, "expected a ValueSet"); - ValueSet const& other = get_wrapped_set(vother); - return std::includes(self.begin(), self.end(), other.begin(), other.end()) ? Qtrue : Qfalse; -} - -/* call-seq: - * set.union(other) => union_set - * set | other => union_set - * - * Computes the union of +set+ and +other+. This operation is O(N + M) - * is +other+ is a ValueSet - */ -static VALUE value_set_union(VALUE vself, VALUE vother) -{ - ValueSet const& self = get_wrapped_set(vself); - if (!RTEST(rb_obj_is_kind_of(vother, cValueSet))) - rb_raise(rb_eArgError, "expected a ValueSet"); - ValueSet const& other = get_wrapped_set(vother); - - VALUE vresult = rb_funcall2(cValueSet, id_new, 0, NULL); - ValueSet& result = get_wrapped_set(vresult); - std::set_union(self.begin(), self.end(), other.begin(), other.end(), - std::inserter(result, result.end())); - return vresult; -} - -/* call-seq: - * set.merge(other) => set - * - * Merges the elements of +other+ into +self+. If +other+ is a ValueSet, the operation is O(N + M) - */ -static VALUE value_set_merge(VALUE vself, VALUE vother) -{ - ValueSet& self = get_wrapped_set(vself); - if (!RTEST(rb_obj_is_kind_of(vother, cValueSet))) - rb_raise(rb_eArgError, "expected a ValueSet"); - ValueSet const& other = get_wrapped_set(vother); - - self.insert(other.begin(), other.end()); - return vself; -} - -/* call-seq: - * set.intersection!(other) => set - * - * Computes the intersection of +set+ and +other+, and modifies +self+ to be - * that interesection. This operation is O(N + M) if +other+ is a ValueSet - */ -static VALUE value_set_intersection_bang(VALUE vself, VALUE vother) -{ - ValueSet& self = get_wrapped_set(vself); - if (!RTEST(rb_obj_is_kind_of(vother, cValueSet))) - rb_raise(rb_eArgError, "expected a ValueSet"); - ValueSet const& other = get_wrapped_set(vother); - - ValueSet result; - std::set_intersection(self.begin(), self.end(), other.begin(), other.end(), - std::inserter(result, result.end())); - self.swap(result); - return vself; -} - -/* call-seq: - * set.intersection(other) => intersection_set - * set & other => intersection_set - * - * Computes the intersection of +set+ and +other+. This operation - * is O(N + M) if +other+ is a ValueSet - */ -static VALUE value_set_intersection(VALUE vself, VALUE vother) -{ - ValueSet const& self = get_wrapped_set(vself); - if (!RTEST(rb_obj_is_kind_of(vother, cValueSet))) - rb_raise(rb_eArgError, "expected a ValueSet"); - ValueSet const& other = get_wrapped_set(vother); - - VALUE vresult = rb_funcall2(cValueSet, id_new, 0, NULL); - ValueSet& result = get_wrapped_set(vresult); - std::set_intersection(self.begin(), self.end(), other.begin(), other.end(), - std::inserter(result, result.end())); - return vresult; -} - -/* call-seq: - * set.intersects?(other) => true or false - * - * Returns true if there is elements in +set+ that are also in +other - */ -static VALUE value_set_intersects(VALUE vself, VALUE vother) -{ - ValueSet const& self = get_wrapped_set(vself); - if (!RTEST(rb_obj_is_kind_of(vother, cValueSet))) - rb_raise(rb_eArgError, "expected a ValueSet"); - ValueSet const& other = get_wrapped_set(vother); - - ValueSet::const_iterator - self_it = self.begin(), - self_end = self.end(), - other_it = other.begin(), - other_end = other.end(); - - while(self_it != self_end && other_it != other_end) - { - if (*self_it < *other_it) - ++self_it; - else if (*other_it < *self_it) - ++other_it; - else - return Qtrue; - } - return Qfalse; -} - -/* call-seq: - * set.difference!(other) => set - * - * Modifies +set+ so that it is the set of all elements of +set+ not in +other+. - * This operation is O(N + M). - */ -static VALUE value_set_difference_bang(VALUE vself, VALUE vother) -{ - ValueSet& self = get_wrapped_set(vself); - if (!RTEST(rb_obj_is_kind_of(vother, cValueSet))) - rb_raise(rb_eArgError, "expected a ValueSet"); - ValueSet const& other = get_wrapped_set(vother); - - ValueSet result; - std::set_difference(self.begin(), self.end(), other.begin(), other.end(), - std::inserter(result, result.end())); - if (result.size() != self.size()) - self.swap(result); - return vself; -} - -/* call-seq: - * set.difference(other) => difference_set - * set - other => difference_set - * - * Computes the set of all elements of +set+ not in +other+. This operation - * is O(N + M). - */ -static VALUE value_set_difference(VALUE vself, VALUE vother) -{ - ValueSet const& self = get_wrapped_set(vself); - if (!RTEST(rb_obj_is_kind_of(vother, cValueSet))) - rb_raise(rb_eArgError, "expected a ValueSet"); - ValueSet const& other = get_wrapped_set(vother); - - VALUE vresult = rb_funcall2(cValueSet, id_new, 0, NULL); - ValueSet& result = get_wrapped_set(vresult); - std::set_difference(self.begin(), self.end(), other.begin(), other.end(), - std::inserter(result, result.end())); - return vresult; -} - -/* call-seq: - * set.insert(value) => true or false - * - * Inserts +value+ into +set+. Returns true if the value did not exist - * in the set yet (it has actually been inserted), and false otherwise. - * This operation is O(log N) - */ -static VALUE value_set_insert(VALUE vself, VALUE v) -{ - ValueSet& self = get_wrapped_set(vself); - bool exists = self.insert(v).second; - return exists ? Qtrue : Qfalse; -} -/* call-seq: - * set.delete(value) => true or false - * - * Removes +value+ from +set+. Returns true if the value did exist - * in the set yet (it has actually been removed), and false otherwise. - */ -static VALUE value_set_delete(VALUE vself, VALUE v) -{ - ValueSet& self = get_wrapped_set(vself); - size_t count = self.erase(v); - return count > 0 ? Qtrue : Qfalse; -} - -/* call-seq: - * set == other => true or false - * - * Equality - */ -static VALUE value_set_equal(VALUE vself, VALUE vother) -{ - ValueSet const& self = get_wrapped_set(vself); - if (!RTEST(rb_obj_is_kind_of(vother, cValueSet))) - return Qfalse; - ValueSet const& other = get_wrapped_set(vother); - return (self == other) ? Qtrue : Qfalse; -} - -/* call-seq: - * set.clear => set - * - * Remove all elements of this set - */ -static VALUE value_set_clear(VALUE self) -{ - get_wrapped_set(self).clear(); - return self; -} - -/* call-seq: - * set.initialize_copy(other) => set - * - * Initializes +set+ with the values in +other+. Needed by #dup - */ -static VALUE value_set_initialize_copy(VALUE vself, VALUE vother) -{ - get_wrapped_set(vself) = get_wrapped_set(vother); - return vself; -} - - - - - - - -/* call-seq: - * to_value_set => value_set - * - * Converts this array into a ValueSet object - */ -static VALUE array_to_value_set(VALUE self) -{ - VALUE vresult = rb_funcall2(cValueSet, id_new, 0, NULL); - ValueSet& result = get_wrapped_set(vresult); - - long size = RARRAY_LEN(self); - for (int i = 0; i < size; ++i) - result.insert(rb_ary_entry(self, i)); - - return vresult; -} - -#ifndef RUBINIUS -static VALUE enumerable_to_value_set_i(VALUE i, VALUE* memo) -{ - ValueSet& result = *reinterpret_cast(memo); - result.insert(i); - return Qnil; -} - -/* call-seq: - * enum.to_value_set => value_set - * - * Builds a ValueSet object from this enumerable - */ -static VALUE enumerable_to_value_set(VALUE self) -{ - VALUE vresult = rb_funcall2(cValueSet, id_new, 0, NULL); - ValueSet& result = get_wrapped_set(vresult); - - rb_iterate(rb_each, self, RUBY_METHOD_FUNC(enumerable_to_value_set_i), reinterpret_cast(&result)); - return vresult; -} -#endif - -/* - * Document-class: ValueSet - * - * ValueSet is a wrapper around the C++ set<> template. set<> is an ordered container, - * for which union(), intersection() and difference() is done in linear time. For performance - * reasons, in the case of ValueSet, the values are ordered by their VALUE, which roughly is - * their object_id. - */ - -extern "C" void Init_value_set() -{ -#ifndef RUBINIUS - rb_define_method(rb_mEnumerable, "to_value_set", RUBY_METHOD_FUNC(enumerable_to_value_set), 0); -#endif - rb_define_method(rb_cArray, "to_value_set", RUBY_METHOD_FUNC(array_to_value_set), 0); - - cValueSet = rb_define_class("ValueSet", rb_cObject); - id_new = rb_intern("new"); - rb_define_alloc_func(cValueSet, value_set_alloc); - rb_define_method(cValueSet, "each", RUBY_METHOD_FUNC(value_set_each), 0); - rb_define_method(cValueSet, "include?", RUBY_METHOD_FUNC(value_set_include_p), 1); - rb_define_method(cValueSet, "include_all?", RUBY_METHOD_FUNC(value_set_include_all_p), 1); - rb_define_method(cValueSet, "union", RUBY_METHOD_FUNC(value_set_union), 1); - rb_define_method(cValueSet, "intersection", RUBY_METHOD_FUNC(value_set_intersection), 1); - rb_define_method(cValueSet, "intersection!", RUBY_METHOD_FUNC(value_set_intersection_bang), 1); - rb_define_method(cValueSet, "intersects?", RUBY_METHOD_FUNC(value_set_intersects), 1); - rb_define_method(cValueSet, "difference", RUBY_METHOD_FUNC(value_set_difference), 1); - rb_define_method(cValueSet, "difference!", RUBY_METHOD_FUNC(value_set_difference_bang), 1); - rb_define_method(cValueSet, "insert", RUBY_METHOD_FUNC(value_set_insert), 1); - rb_define_method(cValueSet, "merge", RUBY_METHOD_FUNC(value_set_merge), 1); - rb_define_method(cValueSet, "delete", RUBY_METHOD_FUNC(value_set_delete), 1); - rb_define_method(cValueSet, "==", RUBY_METHOD_FUNC(value_set_equal), 1); - rb_define_method(cValueSet, "to_value_set", RUBY_METHOD_FUNC(value_set_to_value_set), 0); - rb_define_method(cValueSet, "dup", RUBY_METHOD_FUNC(value_set_dup), 0); - rb_define_method(cValueSet, "empty?", RUBY_METHOD_FUNC(value_set_empty_p), 0); - rb_define_method(cValueSet, "size", RUBY_METHOD_FUNC(value_set_size), 0); - rb_define_method(cValueSet, "clear", RUBY_METHOD_FUNC(value_set_clear), 0); - rb_define_method(cValueSet, "initialize_copy", RUBY_METHOD_FUNC(value_set_initialize_copy), 1); - rb_define_method(cValueSet, "delete_if", RUBY_METHOD_FUNC(value_set_delete_if), 0); -} - - diff --git a/lib/utilrb/common.rb b/lib/utilrb/common.rb index ecedbe0..3389096 100644 --- a/lib/utilrb/common.rb +++ b/lib/utilrb/common.rb @@ -4,50 +4,5 @@ # the standard class extensions used by www.rock-robotics.org projects. module Utilrb LIB_DIR = File.expand_path(File.dirname(__FILE__)) - - unless defined? UTILRB_EXT_MODE - if ENV['UTILRB_EXT_MODE'] == 'no' - UTILRB_EXT_MODE = nil - STDERR.puts "Utilrb: not loading the C extension" - else - begin - require 'utilrb/utilrb' - UTILRB_EXT_MODE = true - STDERR.puts "Utilrb: loaded C extension" if ENV['UTILRB_EXT_MODE'] - rescue LoadError => e - if ENV['UTILRB_EXT_MODE'] == 'yes' - raise LoadError, "unable to load Util.rb C extension: #{e.message}" - else - UTILRB_EXT_MODE = nil - end - end - end - end - - # Yields if the extension is not present - # This is used by Utilrb libraries to provide a - # Ruby version if the C extension is not loaded - def self.unless_ext # :yield: - unless UTILRB_EXT_MODE - return yield if block_given? - end - end - - # Yields if the extension is present. This is used for Ruby code - # which depends on methods in the C extension - def self.if_ext(&block) - require_ext(nil, &block) - end - - # Yields if the extension is present, and - # issue a warning otherwise. This is used for Ruby - # code which depends on methods in the C extension - def self.require_ext(name) - if UTILRB_EXT_MODE - yield if block_given? - elsif name - STDERR.puts "Utilrb: not loading #{name} since the C extension is not available" - end - end end diff --git a/lib/utilrb/enumerable/uniq.rb b/lib/utilrb/enumerable/uniq.rb index 6b2bbb8..ae83a5a 100644 --- a/lib/utilrb/enumerable/uniq.rb +++ b/lib/utilrb/enumerable/uniq.rb @@ -48,21 +48,19 @@ def enum_uniq(enum_with = :each, *args, &filter) end end -Utilrb.unless_ext do - module Enumerable - # call-seq: - # each_uniq { |obj| ... } - # - # Yields all unique values found in +enum+ - def each_uniq - seen = Set.new - each do |obj| - if !seen.include?(obj) - seen << obj - yield(obj) - end - end - end +module Enumerable + # call-seq: + # each_uniq { |obj| ... } + # + # Yields all unique values found in +enum+ + def each_uniq + seen = Set.new + each do |obj| + if !seen.include?(obj) + seen << obj + yield(obj) + end + end end end diff --git a/lib/utilrb/value_set.rb b/lib/utilrb/value_set.rb deleted file mode 100644 index 9c7ae4a..0000000 --- a/lib/utilrb/value_set.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'utilrb/common' -require 'utilrb/enumerable/to_s_helper' - -Utilrb.require_ext("ValueSet") do - class ValueSet - def <<(obj); insert(obj) ; self end - alias :| :union - alias :& :intersection - alias :- :difference - include Enumerable - - def substract(other_set) - difference!(other_set.to_value_set) - end - - def add(value) - insert(value) - self - end - - def to_s - elements = EnumerableToString.to_s_helper(self, '{', '}') do |obj| - obj.to_s - end - base = super[0..-2] - "#{base} #{elements}>" - end - alias :inspect :to_s - - def _dump(lvl = -1) - Marshal.dump(to_a) - end - def self._load(str) - Marshal.load(str).to_value_set - end - - def eql?(obj) - self == obj - end - - def hash - result = ValueSet.hash - for obj in self - result = result ^ obj.hash - end - result - end - end -end diff --git a/test/test_enumerable.rb b/test/test_enumerable.rb index 0fa484e..91dc969 100644 --- a/test/test_enumerable.rb +++ b/test/test_enumerable.rb @@ -1,7 +1,5 @@ require 'utilrb/test' - require 'utilrb/enumerable' -require 'utilrb/value_set' class TC_Enumerable < Minitest::Test diff --git a/test/test_proc.rb b/test/test_proc.rb deleted file mode 100644 index 39a01c5..0000000 --- a/test/test_proc.rb +++ /dev/null @@ -1,31 +0,0 @@ -require 'utilrb/test' - -require 'utilrb' - -Utilrb.require_ext('TC_Proc') do - if RUBY_VERSION =~ /^1\.8/ - class TC_Proc < Minitest::Test - def block_to_proc_helper(&block); block end - def block_to_proc - [block_to_proc_helper { blo }, block_to_proc_helper { bla }] - end - def test_same_body - a1, a2 = block_to_proc - b1, b2 = block_to_proc - assert(a1.same_body?(b1)) - assert(a2.same_body?(b2)) - assert(!a1.same_body?(b2)) - assert(!a2.same_body?(b1)) - end - - def test_line - assert_equal(10, block_to_proc.first.line) - end - - def test_file - assert_equal(File.expand_path(__FILE__), File.expand_path(block_to_proc.first.file)) - end - end - end -end - diff --git a/test/test_value_set.rb b/test/test_value_set.rb deleted file mode 100644 index 0542c96..0000000 --- a/test/test_value_set.rb +++ /dev/null @@ -1,72 +0,0 @@ -require 'utilrb/test' -require 'utilrb/value_set' - -class TC_ValueSet < Minitest::Test - Utilrb.require_ext('test_value_set') do - def test_value_set - a = [1, 3, 3, 4, 6, 8].to_value_set - b = [1, 2, 4, 3, 11, 11].to_value_set - assert_equal(5, a.size) - assert_equal([1, 3, 4, 6, 8], a.to_a) - assert(a.include?(1)) - assert(a.include_all?([4, 1, 8].to_value_set)) - assert(!a.include_all?(b)) - - assert(a.intersects?(b)) - assert(b.intersects?(a)) - assert(!a.intersects?([2, 9, 12].to_value_set)) - - assert(a.object_id == a.to_value_set.object_id) - - assert_equal([1, 2, 3, 4, 6, 8, 11], (a.union(b)).to_a) - assert_equal([1, 3, 4], (a.intersection(b)).to_a) - assert_equal([6, 8], (a.difference(b)).to_a) - assert(! (a == :bla)) # check #== behaves correctly with a non-enumerable - - a.delete(1) - assert(! a.include?(1)) - a.merge(b); - assert_equal([1, 2, 3, 4, 6, 8, 11].to_value_set, a) - - assert([].to_value_set.empty?) - - assert([1, 2, 4, 3].to_value_set.clear.empty?) - - assert_equal([1,3,5].to_value_set, [1, 2, 3, 4, 5, 6].to_value_set.delete_if { |v| v % 2 == 0 }) - end - - def test_value_set_hash - a = [(obj = Object.new), 3, 4, [(obj2 = Object.new), Hash.new]].to_value_set - b = [obj, 3, 4, [obj2, Hash.new]].to_value_set - assert_equal a.hash, b.hash - end - - def test_value_set_to_s - obj = ValueSet.new - obj << 1 - obj << 2 - assert(obj.to_s =~ /\{(.*)\}/) - values = $1.split(", ") - assert_equal(["1", "2"].to_set, values.to_set) - - obj << obj - assert(obj.to_s =~ /^(.+)\{(.*)\}>$/) - - base_s = $1 - values = $2.split(", ") - assert_equal(["1", "2", "#{base_s}...>"].to_set, values.to_set) - end - - def test_value_set_add - a = [1, 3, 3, 4, 6, 8].to_value_set - assert_same a, a.add(10) - assert a.include?(10) - end - - def test_value_set_substract - a = [1, 3, 3, 4, 6, 8].to_value_set - a.substract([3,4]) - assert_equal [1, 6, 8].to_value_set, a - end - end -end diff --git a/test/test_weakref.rb b/test/test_weakref.rb index 32ed559..c147cca 100644 --- a/test/test_weakref.rb +++ b/test/test_weakref.rb @@ -11,7 +11,7 @@ def test_normal end def test_initialize_validation - ref = WeakRef.new(Object.new) + ref = WeakRef.new(obj = Object.new) assert_raises(ArgumentError) { Utilrb::WeakRef.new(ref) } end end diff --git a/utilrb.gemspec b/utilrb.gemspec index 09811d5..1e6ceaf 100644 --- a/utilrb.gemspec +++ b/utilrb.gemspec @@ -20,7 +20,6 @@ Gem::Specification.new do |s| s.add_runtime_dependency "facets", ">= 2.4.0" s.add_runtime_dependency "rake", ">= 0.9" - s.add_runtime_dependency "rake-compiler", "~> 0.8.0" s.add_development_dependency "flexmock", ">= 2.0.0" s.add_development_dependency "minitest", ">= 5.0", "~> 5.0" s.add_development_dependency "coveralls" From 55d176f5293ff8bd8b8a977695f707784e856181 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Fri, 25 Sep 2015 09:35:40 -0300 Subject: [PATCH 43/67] update manifest.xml and package.xml to reflect changes in the distribution method --- manifest.xml | 2 -- package.xml | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/manifest.xml b/manifest.xml index c1f61f9..a607b26 100644 --- a/manifest.xml +++ b/manifest.xml @@ -13,7 +13,5 @@ - - diff --git a/package.xml b/package.xml index c20107f..c811623 100644 --- a/package.xml +++ b/package.xml @@ -12,13 +12,11 @@ catkin ruby + bundler facets - hoe - rake-compiler ruby facets - hoe From 13adb56352c8f7798feeb9305cf4bff2ccc99600 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sun, 27 Sep 2015 20:47:35 -0300 Subject: [PATCH 44/67] remove some obsolete files --- Manifest.txt | 117 ---------------------------------- patches/gc_live_objects.patch | 71 --------------------- 2 files changed, 188 deletions(-) delete mode 100644 Manifest.txt delete mode 100644 patches/gc_live_objects.patch diff --git a/Manifest.txt b/Manifest.txt deleted file mode 100644 index a33e117..0000000 --- a/Manifest.txt +++ /dev/null @@ -1,117 +0,0 @@ -.gemtest -History.txt -License.txt -Manifest.txt -README.rd -Rakefile -bm/allocation.rb -bm/speed.rb -ext/utilrb/extconf.rb -ext/utilrb/proc.c -ext/utilrb/ruby_allocator.hh -ext/utilrb/utilrb.cc -ext/utilrb/value_set.cc -ext/utilrb/weakref.cc -lib/utilrb.rb -lib/utilrb/array.rb -lib/utilrb/array/to_s.rb -lib/utilrb/column_formatter.rb -lib/utilrb/common.rb -lib/utilrb/configsearch.rb -lib/utilrb/configsearch/configuration_finder.rb -lib/utilrb/dir.rb -lib/utilrb/dir/empty.rb -lib/utilrb/doc/rake.rb -lib/utilrb/enumerable.rb -lib/utilrb/enumerable/null.rb -lib/utilrb/enumerable/random_element.rb -lib/utilrb/enumerable/sequence.rb -lib/utilrb/enumerable/to_s_helper.rb -lib/utilrb/enumerable/uniq.rb -lib/utilrb/event_loop.rb -lib/utilrb/exception.rb -lib/utilrb/exception/full_message.rb -lib/utilrb/gc.rb -lib/utilrb/gc/force.rb -lib/utilrb/hash.rb -lib/utilrb/hash/map_key.rb -lib/utilrb/hash/map_value.rb -lib/utilrb/hash/recursive_merge.rb -lib/utilrb/hash/slice.rb -lib/utilrb/hash/to_s.rb -lib/utilrb/hash/to_sym_keys.rb -lib/utilrb/kernel.rb -lib/utilrb/kernel/arity.rb -lib/utilrb/kernel/load_dsl_file.rb -lib/utilrb/kernel/options.rb -lib/utilrb/kernel/poll.rb -lib/utilrb/kernel/require.rb -lib/utilrb/kernel/with_module.rb -lib/utilrb/logger.rb -lib/utilrb/logger/forward.rb -lib/utilrb/logger/hierarchy.rb -lib/utilrb/logger/indent.rb -lib/utilrb/logger/io.rb -lib/utilrb/logger/log_pp.rb -lib/utilrb/logger/root.rb -lib/utilrb/logger/silent.rb -lib/utilrb/marshal/load_with_missing_constants.rb -lib/utilrb/module.rb -lib/utilrb/module/ancestor_p.rb -lib/utilrb/module/attr_enumerable.rb -lib/utilrb/module/attr_predicate.rb -lib/utilrb/module/cached_enum.rb -lib/utilrb/module/const_defined_here_p.rb -lib/utilrb/module/define_method.rb -lib/utilrb/module/define_or_reuse.rb -lib/utilrb/module/dsl_attribute.rb -lib/utilrb/module/include.rb -lib/utilrb/module/inherited_enumerable.rb -lib/utilrb/object.rb -lib/utilrb/object/address.rb -lib/utilrb/object/attribute.rb -lib/utilrb/object/scoped_eval.rb -lib/utilrb/object/singleton_class.rb -lib/utilrb/pathname.rb -lib/utilrb/pathname/find_matching_parent.rb -lib/utilrb/pkgconfig.rb -lib/utilrb/qt/mime_data/mime_data.rb -lib/utilrb/qt/variant/from_ruby.rb -lib/utilrb/rake_common.rb -lib/utilrb/set.rb -lib/utilrb/set/to_s.rb -lib/utilrb/socket/tcp_server.rb -lib/utilrb/socket/tcp_socket.rb -lib/utilrb/spawn.rb -lib/utilrb/symbol/to_str.rb -lib/utilrb/thread_pool.rb -lib/utilrb/time.rb -lib/utilrb/time/to_hms.rb -lib/utilrb/timepoints.rb -lib/utilrb/unbound_method.rb -lib/utilrb/unbound_method/call.rb -lib/utilrb/value_set.rb -lib/utilrb/weakref.rb -lib/utilrb/yard.rb -patches/gc_live_objects.patch -test/data/test_pkgconfig.pc -test/data/test_pkgconfig_empty.pc -test/test_array.rb -test/test_dir.rb -test/test_enumerable.rb -test/test_event_loop.rb -test/test_exception.rb -test/test_gc.rb -test/test_hash.rb -test/test_kernel.rb -test/test_logger.rb -test/test_misc.rb -test/test_module.rb -test/test_object.rb -test/test_pkgconfig.rb -test/test_proc.rb -test/test_set.rb -test/test_thread_pool.rb -test/test_time.rb -test/test_unbound_method.rb -test/test_weakref.rb diff --git a/patches/gc_live_objects.patch b/patches/gc_live_objects.patch deleted file mode 100644 index 9aaca60..0000000 --- a/patches/gc_live_objects.patch +++ /dev/null @@ -1,71 +0,0 @@ ---- gc.c 2006-08-25 10:12:46.000000000 +0200 -+++ gc.c.new 2007-01-13 12:39:38.383681000 +0100 -@@ -88,6 +88,8 @@ static void run_final(); - static VALUE nomem_error; - static void garbage_collect(); - -+static unsigned long live_objects = 0; -+ - void - rb_memerror() - { -@@ -401,6 +398,7 @@ rb_newobj() - RANY(obj)->file = ruby_sourcefile; - RANY(obj)->line = ruby_sourceline; - #endif -+ live_objects++; - return obj; - } - -@@ -1053,8 +1051,8 @@ gc_sweep() - RVALUE *p, *pend, *final_list; - int freed = 0; - int i; -- unsigned long live = 0; - unsigned long free_min = 0; -+ live_objects = 0; - - for (i = 0; i < heaps_used; i++) { - free_min += heaps[i].limit; -@@ -1113,7 +1111,7 @@ gc_sweep() - } - else { - RBASIC(p)->flags &= ~FL_MARK; -- live++; -+ live_objects++; - } - p++; - } -@@ -1131,7 +1129,7 @@ gc_sweep() - } - } - if (malloc_increase > malloc_limit) { -- malloc_limit += (malloc_increase - malloc_limit) * (double)live / (live + freed); -+ malloc_limit += (malloc_increase - malloc_limit) * (double)live_objects / (live_objects + freed); - if (malloc_limit < GC_MALLOC_LIMIT) malloc_limit = GC_MALLOC_LIMIT; - } - malloc_increase = 0; -@@ -2003,6 +2001,15 @@ rb_obj_id(VALUE obj) - return (VALUE)((long)obj|FIXNUM_FLAG); - } - -+/* call-seq: -+ * GC.live_objects => number -+ * -+ * Returns the count of objects currently allocated -+ */ -+static -+VALUE rb_gc_live_objects(VALUE self) -+{ return INT2FIX(live_objects); } -+ - /* - * The GC module provides an interface to Ruby's mark and - * sweep garbage collection mechanism. Some of the underlying methods -@@ -2027,6 +2034,7 @@ Init_GC() - rb_define_module_function(rb_mObSpace, "remove_finalizer", rm_final, 1); - rb_define_module_function(rb_mObSpace, "finalizers", finals, 0); - rb_define_module_function(rb_mObSpace, "call_finalizer", call_final, 1); -+ rb_define_module_function(rb_mObSpace, "live_objects", rb_gc_live_objects, 0); - - rb_define_module_function(rb_mObSpace, "define_finalizer", define_final, -1); - rb_define_module_function(rb_mObSpace, "undefine_finalizer", undefine_final, 1); From ea49443a42bf72ee71c06ddd006a72ea4e47378e Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sun, 27 Sep 2015 20:47:57 -0300 Subject: [PATCH 45/67] update gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 72d5c88..f4063f9 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ tmp/ lib/utilrb/utilrb.so pkg/ /vendor/ +/.bundle/ +/Gemfile.lock + From b51c84f83ed1a854051ed7a5b58e637deb2a72dc Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sun, 27 Sep 2015 20:53:30 -0300 Subject: [PATCH 46/67] release 3.0.0.rc1, without the C extension --- lib/utilrb/version.rb | 2 +- utilrb.gemspec | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/utilrb/version.rb b/lib/utilrb/version.rb index f323059..587f126 100644 --- a/lib/utilrb/version.rb +++ b/lib/utilrb/version.rb @@ -1,4 +1,4 @@ module Utilrb - VERSION = "2.1.0.rc3" + VERSION = "3.0.0.rc1" end diff --git a/utilrb.gemspec b/utilrb.gemspec index 1e6ceaf..89dd604 100644 --- a/utilrb.gemspec +++ b/utilrb.gemspec @@ -14,8 +14,7 @@ Gem::Specification.new do |s| s.licenses = ["BSD"] s.require_paths = ["lib"] - s.extensions = ['ext/utilrb/extconf.rb'] - s.extra_rdoc_files = ["History.txt", "License.txt", "Manifest.txt", "History.txt"] + s.extra_rdoc_files = ["License.txt"] s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } s.add_runtime_dependency "facets", ">= 2.4.0" From 768f18b8560af8c733ec4ff0c6909771f41ed265 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Sun, 27 Sep 2015 21:32:49 -0300 Subject: [PATCH 47/67] thread_pool: fix synchronization in one of the tests It was leading to spurious failures, in particular on travis --- lib/utilrb/thread_pool.rb | 10 +++++----- test/test_thread_pool.rb | 34 ++++++++++++++++++++-------------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/lib/utilrb/thread_pool.rb b/lib/utilrb/thread_pool.rb index d460564..6267dbd 100644 --- a/lib/utilrb/thread_pool.rb +++ b/lib/utilrb/thread_pool.rb @@ -593,13 +593,13 @@ def spawn_thread ensure @mutex.synchronize do @tasks_running.delete current_task - @sync_keys.delete(current_task.sync_key) if current_task.sync_key + if current_task.sync_key + @sync_keys.delete(current_task.sync_key) + @cond_sync_key.signal + @cond.signal # maybe another thread is waiting for a sync key + end @avg_run_time = moving_average(@avg_run_time,(current_task.stopped_at-current_task.started_at)) end - if current_task.sync_key - @cond_sync_key.signal - @cond.signal # maybe another thread is waiting for a sync key - end current_task.finalize # propagate state after it was deleted from the internal lists @callback_on_task_finished.call(current_task) if @callback_on_task_finished end diff --git a/test/test_thread_pool.rb b/test/test_thread_pool.rb index 7a41e85..cdca586 100644 --- a/test/test_thread_pool.rb +++ b/test/test_thread_pool.rb @@ -59,14 +59,16 @@ def wait_until(timeout: 5, &block) block.call end - def synchronized_work - mutex.synchronize do - @active_synchronized_workers_count += 1 - begin - cv.wait(mutex) - ensure - @active_synchronized_workers_count -= 1 - end + def synchronized_work(already_locked: false) + if !already_locked + return mutex.synchronize { synchronized_work(already_locked: true) } + end + + @active_synchronized_workers_count += 1 + begin + cv.wait(mutex) + ensure + @active_synchronized_workers_count -= 1 end end @@ -100,9 +102,12 @@ def spawn_synchronized_workers(count, &spawner) @spawned_synchronized_workers_count += 1 end spawner.call(pool) do - synchronized_work mutex.synchronize do - @spawned_synchronized_workers_count -= 1 + begin + synchronized_work(already_locked: true) + ensure + @spawned_synchronized_workers_count -= 1 + end end end end @@ -155,13 +160,14 @@ def spawn_synchronized_workers(count, &spawner) it "must not execute tasks with the same sync key in parallel" do key = Object.new create_pool 10 - spawn_synchronized_workers 10 do |pool, &w| + spawn_synchronized_workers 1000 do |pool, &w| w = pool.process_with_options(sync_key: key, &w) assert_equal key, w.sync_key end - 10.times do |i| - wait_synchronized_workers 1 - wait_until { assert_equal (10 - i), spawned_synchronized_workers_count } + wait_synchronized_workers(1) + 1000.times do |i| + wait_until { assert_equal (1000 - i), spawned_synchronized_workers_count } + wait_synchronized_workers(1) release_synchronized_workers end end From bcf4e8384dfa608ba68d905b84c1fa877ad09e62 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 12 Nov 2015 17:02:05 -0200 Subject: [PATCH 48/67] remove reimplementations of #to_s for core collection classes It's basically buggy, and not needed since 1.9. It was really a 1.8 thing. --- lib/utilrb/array/to_s.rb | 12 ------------ lib/utilrb/hash/to_s.rb | 12 ------------ lib/utilrb/set/to_s.rb | 11 ----------- 3 files changed, 35 deletions(-) diff --git a/lib/utilrb/array/to_s.rb b/lib/utilrb/array/to_s.rb index 4c13f6a..e69de29 100644 --- a/lib/utilrb/array/to_s.rb +++ b/lib/utilrb/array/to_s.rb @@ -1,12 +0,0 @@ -require 'utilrb/enumerable/to_s_helper' -class Array - # Displays arrays as [ a, b, [c, d], ... ] instead of the standard #join - # Unlike #inspect, it calls #to_s on the elements too - undef_method :to_s - def to_s - EnumerableToString.to_s_helper(self, '[', ']') do |obj| - obj.to_s - end - end -end - diff --git a/lib/utilrb/hash/to_s.rb b/lib/utilrb/hash/to_s.rb index 714b975..e69de29 100644 --- a/lib/utilrb/hash/to_s.rb +++ b/lib/utilrb/hash/to_s.rb @@ -1,12 +0,0 @@ -require 'utilrb/enumerable/to_s_helper' -class Hash - # Displays hashes as { a => A, b => B, ... } instead of the standard #join - # Unlike #inspect, it calls #to_s on the elements too - undef_method :to_s - def to_s - EnumerableToString.to_s_helper(self, '{', '}') do |k, v| - "#{k} => #{v}" - end - end -end - diff --git a/lib/utilrb/set/to_s.rb b/lib/utilrb/set/to_s.rb index cc7c94a..e69de29 100644 --- a/lib/utilrb/set/to_s.rb +++ b/lib/utilrb/set/to_s.rb @@ -1,11 +0,0 @@ -require 'utilrb/enumerable/to_s_helper' -require 'set' -class Set - # Displays the set as {a, b, c, d} - def to_s - EnumerableToString.to_s_helper(self, '{', '}') do |obj| - obj.to_s - end - end -end - From f764866fe86d1684c6b8d492bf23c79fe346a10a Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 12 Nov 2015 17:15:38 -0200 Subject: [PATCH 49/67] fix test after the removal of #to_s --- test/test_array.rb | 15 --------------- test/test_hash.rb | 12 ------------ test/test_set.rb | 19 ------------------- 3 files changed, 46 deletions(-) delete mode 100644 test/test_array.rb delete mode 100644 test/test_set.rb diff --git a/test/test_array.rb b/test/test_array.rb deleted file mode 100644 index 468d215..0000000 --- a/test/test_array.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'utilrb/test' -require 'utilrb/array' - -class TC_Array < Minitest::Test - def test_to_s - assert_equal("[1, 2]", [1, 2].to_s) - end - def test_to_s_recursive - obj = [1, 2] - obj << obj - assert_equal("[1, 2, ...]", obj.to_s) - end - -end - diff --git a/test/test_hash.rb b/test/test_hash.rb index 98897a0..45eee3a 100644 --- a/test/test_hash.rb +++ b/test/test_hash.rb @@ -15,18 +15,6 @@ def test_to_sym_keys assert_equal({ :a => 10, :b => 20, :c => 30 }, { 'a' => 10, 'b' => 20, :c => 30 }.to_sym_keys) end - def test_to_s - obj = { 1 => 2, 2 => 3 } - assert(obj.to_s =~ /^\{(.*)\}$/) - values = $1.split(", ") - assert_equal(["1 => 2", "2 => 3"].to_set, values.to_set) - - obj[3] = obj - assert(obj.to_s =~ /^\{(.*)\}$/) - values = $1.split(", ") - assert_equal(["1 => 2", "2 => 3", "3 => ..."].to_set, values.to_set) - end - def test_map_key base = { 1 => 'a', 2 => 'b' } result = base.map_key { |k, v| k += 1 } diff --git a/test/test_set.rb b/test/test_set.rb deleted file mode 100644 index 3af31c4..0000000 --- a/test/test_set.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'utilrb/test' -require 'utilrb/set' - -class TC_Set < Minitest::Test - def test_to_s - obj = Set.new - obj << 1 - obj << 2 - assert(obj.to_s =~ /^\{(.*)\}$/) - values = $1.split(", ") - assert_equal(["1", "2"].to_set, values.to_set) - - obj << obj - assert(obj.to_s =~ /^\{(.*)\}$/) - values = $1.split(", ") - assert_equal(["1", "2", "..."].to_set, values.to_set) - end -end - From 5c87892476fbcb6e588d0058d61b57acaa354df5 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 9 Jun 2016 09:50:40 -0300 Subject: [PATCH 50/67] pkgconfig: do not store in loaded_packages cache until we resolved all candidates PkgConfig.load might fail, which would cause the cache to be corrupted. --- lib/utilrb/pkgconfig.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/utilrb/pkgconfig.rb b/lib/utilrb/pkgconfig.rb index f6fe59e..760755d 100644 --- a/lib/utilrb/pkgconfig.rb +++ b/lib/utilrb/pkgconfig.rb @@ -66,10 +66,11 @@ def self.get(name, version_spec = nil, preset_variables = Hash.new) raise NotFound.new(name), "cannot find the pkg-config specification for #{name}" end - candidates = loaded_packages[name] = Array.new + candidates = Array.new paths.each do |p| candidates << PkgConfig.load(p, preset_variables) end + loaded_packages[name] = candidates end # Now try to find a matching spec From bb7de3b35e1e3dd2255910144244db414cc0c0e9 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 9 Jun 2016 09:51:03 -0300 Subject: [PATCH 51/67] pkgconfig: raise NotFound if there are candidates, but none matching the version spec --- lib/utilrb/pkgconfig.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/utilrb/pkgconfig.rb b/lib/utilrb/pkgconfig.rb index 760755d..933bd55 100644 --- a/lib/utilrb/pkgconfig.rb +++ b/lib/utilrb/pkgconfig.rb @@ -74,7 +74,11 @@ def self.get(name, version_spec = nil, preset_variables = Hash.new) end # Now try to find a matching spec - find_matching_version(candidates, version_spec) + if version_match = find_matching_version(candidates, version_spec) + version_match + else + raise NotFound, "found #{candidates.size} packages for #{name}, but none match the version specification #{version_spec}" + end end # Finds the provided package and optional version and returns its From 8e0a88b4db1ad2085fd41c7d379526bcbcee0b51 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 9 Jun 2016 09:54:29 -0300 Subject: [PATCH 52/67] pkgconfig: fix default path/suffix autodetection regexp for recent pkg-config --- lib/utilrb/pkgconfig.rb | 4 ++-- test/test_pkgconfig.rb | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/utilrb/pkgconfig.rb b/lib/utilrb/pkgconfig.rb index 933bd55..d87e3f0 100644 --- a/lib/utilrb/pkgconfig.rb +++ b/lib/utilrb/pkgconfig.rb @@ -466,8 +466,8 @@ def self.each_package(regex = nil) end - FOUND_PATH_RX = /Scanning directory '(.*\/)((?:lib|lib64|share)\/.*)'$/ - NONEXISTENT_PATH_RX = /Cannot open directory '.*\/((?:lib|lib64|share)\/.*)' in package search path:.*/ + FOUND_PATH_RX = /Scanning directory (?:#\d+ )?'(.*\/)((?:lib|lib64|share)\/.*)'$/ + NONEXISTENT_PATH_RX = /Cannot open directory (?:#\d+ )?'.*\/((?:lib|lib64|share)\/.*)' in package search path:.*/ # Returns the system-wide search path that is embedded in pkg-config def self.default_search_path diff --git a/test/test_pkgconfig.rb b/test/test_pkgconfig.rb index 12d708c..594b03f 100644 --- a/test/test_pkgconfig.rb +++ b/test/test_pkgconfig.rb @@ -19,6 +19,20 @@ def test_find_package PkgConfig.new('test_pkgconfig') end + def test_path_autodetection_regexp + # PkgConfig 0.26 + assert("Scanning directory '/usr/share/pkgconfig'" =~ Utilrb::PkgConfig::FOUND_PATH_RX) + assert_equal '/usr/share/pkgconfig', "#{$1}#{$2}" + assert("Cannot open directory '/usr/share/pkgconfig' in package search path:" =~ Utilrb::PkgConfig::NONEXISTENT_PATH_RX) + assert_equal 'share/pkgconfig', $1 + + # PkgConfig 0.29.1 + assert("Scanning directory #10 '/usr/share/pkgconfig'" =~ Utilrb::PkgConfig::FOUND_PATH_RX) + assert_equal '/usr/share/pkgconfig', "#{$1}#{$2}" + assert("Cannot open directory #10 '/usr/share/pkgconfig' in package search path:" =~ Utilrb::PkgConfig::NONEXISTENT_PATH_RX) + assert_equal 'share/pkgconfig', $1 + end + def test_load pkg = PkgConfig.new('test_pkgconfig') assert_equal('test_pkgconfig', pkg.name) From a19fd2df396054ef4943648a94bb328a2c483dc2 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 9 Jun 2016 16:28:49 -0300 Subject: [PATCH 53/67] release 3.0.1 --- lib/utilrb/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utilrb/version.rb b/lib/utilrb/version.rb index 587f126..34be37c 100644 --- a/lib/utilrb/version.rb +++ b/lib/utilrb/version.rb @@ -1,4 +1,4 @@ module Utilrb - VERSION = "3.0.0.rc1" + VERSION = "3.0.1" end From 6ab46c1248005483d9cc4d610c8c543c14b51e6a Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Fri, 18 Dec 2015 10:31:48 -0200 Subject: [PATCH 54/67] timepoints: use 'start' and 'done' as keywords at the end of a timepoint name to compute a total duration --- lib/utilrb/timepoints.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/utilrb/timepoints.rb b/lib/utilrb/timepoints.rb index c7fc58f..5597025 100644 --- a/lib/utilrb/timepoints.rb +++ b/lib/utilrb/timepoints.rb @@ -15,8 +15,15 @@ def add_timepoint(*names) end def format_timepoints + start_points = Hash.new result = [] @timepoints.inject(@timepoints.first.first) do |last_t, (t, name)| + if name.last == 'start' + start_points[name[0..-2]] = t + elsif name.last == 'done' + total = t - start_points.delete(name[0..-2]) + name = name + ["total=%.3f" % total] + end result << name + [t - last_t] t end From 6501cf38023e899e4a8f9cd03f11808e712b3343 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 18 Apr 2016 11:04:13 -0300 Subject: [PATCH 55/67] marshal: improve robustness of load_with_missing_constants --- lib/utilrb/marshal.rb | 3 ++ .../marshal/load_with_missing_constants.rb | 39 +++++++-------- test/test_marshal.rb | 47 +++++++++++++++++++ 3 files changed, 68 insertions(+), 21 deletions(-) create mode 100644 lib/utilrb/marshal.rb create mode 100644 test/test_marshal.rb diff --git a/lib/utilrb/marshal.rb b/lib/utilrb/marshal.rb new file mode 100644 index 0000000..738bf8c --- /dev/null +++ b/lib/utilrb/marshal.rb @@ -0,0 +1,3 @@ +require 'utilrb/kernel/require' +require_dir(__FILE__) + diff --git a/lib/utilrb/marshal/load_with_missing_constants.rb b/lib/utilrb/marshal/load_with_missing_constants.rb index f2a7752..39a69fb 100644 --- a/lib/utilrb/marshal/load_with_missing_constants.rb +++ b/lib/utilrb/marshal/load_with_missing_constants.rb @@ -1,26 +1,32 @@ module Marshal - if defined? BasicObject - class BlackHole < BasicObject - end - end - class BlackHole class << self - :name + attr_reader :name end def initialize(*args) end + def hash + __id__ + end + + def eql?(obj) + equal?(obj) + end + attr_reader :__content__ def method_missing(*args) + ::Kernel.puts args.inspect + ::Kernel.puts ::Kernel.caller end def self._load(*args) hole = BlackHole.new hole.instance_variable_set(:@__content__, args) end - def self.method_missing(*args) + def self.method_missing(*args, **options) + BlackHole.new end end @@ -32,20 +38,11 @@ def self.load_with_missing_constants(str_or_io) self.load(str_or_io) rescue Exception => e case e.message - when /undefined class\/module ((?:\w+::)+)$/ - names = $1.split('::') - missing = names.pop - base = names.inject(Object) { |m, n| m.const_get(n) } - base.const_set(missing, Module.new) - - if original_pos - str_or_io.seek(original_pos) - end - retry - when /undefined class\/module ((?:\w+::)+)(\w+)$/ - mod, klass = $1, $2 - full_name = "#{mod}#{klass}" - mod = mod.split('::').inject(Object) { |m, n| m.const_get(n) } + when /undefined class\/module ((?:\w+)(?:::\w+)*)(?:::)?$/ + full_name = $1 + path = $1.split('::') + *path, klass = *path + mod = path.inject(Object) { |m, n| m.const_get(n) } blackhole = Class.new(BlackHole) do @name = full_name diff --git a/test/test_marshal.rb b/test/test_marshal.rb new file mode 100644 index 0000000..97b110e --- /dev/null +++ b/test/test_marshal.rb @@ -0,0 +1,47 @@ +require 'utilrb/test' +require 'utilrb/marshal' + +module MarshalLoadWithMissingConstantsEnv +end + +describe Marshal do + describe "#load_with_missing_constants" do + after do + Object.const_set :MarshalLoadWithMissingConstantsEnv, Module.new + end + + it "resolves existing constants as expected" do + MarshalLoadWithMissingConstantsEnv.const_set 'Test', (klass = Class.new) + dumped = Marshal.dump(klass.new) + obj = Marshal.load_with_missing_constants(dumped) + assert_kind_of klass, obj + end + + it "creates missing classes as needed" do + MarshalLoadWithMissingConstantsEnv.const_set 'Test', (klass = Class.new) + dumped = Marshal.dump(klass.new) + Object.const_set :MarshalLoadWithMissingConstantsEnv, Module.new + obj = Marshal.load_with_missing_constants(dumped) + assert_same obj.class, MarshalLoadWithMissingConstantsEnv::Test + assert_kind_of Marshal::BlackHole, obj + assert_equal "MarshalLoadWithMissingConstantsEnv::Test", obj.class.name + end + + it "resolves missing namespaces recursively" do + MarshalLoadWithMissingConstantsEnv.const_set 'Test', (namespace = Module.new) + MarshalLoadWithMissingConstantsEnv::Test.const_set 'Test', (klass = Class.new) + dumped = Marshal.dump(klass.new) + Object.const_set :MarshalLoadWithMissingConstantsEnv, Module.new + obj = Marshal.load_with_missing_constants(dumped) + + blackhole_namespace = MarshalLoadWithMissingConstantsEnv::Test + assert(blackhole_namespace < Marshal::BlackHole) + assert_equal "MarshalLoadWithMissingConstantsEnv::Test", blackhole_namespace.name + assert_same obj.class, blackhole_namespace::Test + + assert_kind_of Marshal::BlackHole, obj + assert_equal "MarshalLoadWithMissingConstantsEnv::Test::Test", obj.class.name + end + end +end + From a224b5415f8bb6b396a7edcd613a647db9b9d583 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 18 Apr 2016 11:04:31 -0300 Subject: [PATCH 56/67] test: remove usage of simplecov deprecated APIs --- lib/utilrb/test.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/utilrb/test.rb b/lib/utilrb/test.rb index 0785def..64f580a 100644 --- a/lib/utilrb/test.rb +++ b/lib/utilrb/test.rb @@ -4,10 +4,10 @@ begin require 'simplecov' require 'coveralls' - SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ - SimpleCov::Formatter::HTMLFormatter, - Coveralls::SimpleCov::Formatter - ] + SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new( + [SimpleCov::Formatter::HTMLFormatter, + Coveralls::SimpleCov::Formatter] + ) SimpleCov.start do add_filter "/test/" end From 650c6f12bb1d06a7c325d9af9ca90aa85b930004 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 18 Apr 2016 10:14:33 -0300 Subject: [PATCH 57/67] pkgconfig: make each_package without block return an enumerator --- lib/utilrb/pkgconfig.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/utilrb/pkgconfig.rb b/lib/utilrb/pkgconfig.rb index d87e3f0..649cae2 100644 --- a/lib/utilrb/pkgconfig.rb +++ b/lib/utilrb/pkgconfig.rb @@ -452,6 +452,8 @@ def self.has_package?(name) # Yields the package names of available packages. If +regex+ is given, # lists only the names that match the regular expression. def self.each_package(regex = nil) + return enum_for(__method__) if !block_given? + seen = Set.new each_pkgconfig_directory do |dir| Dir.glob(File.join(dir, '*.pc')) do |file| From feb1d948d33ed056502d14de8f6b430b3d395f3b Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 18 Apr 2016 10:25:29 -0300 Subject: [PATCH 58/67] pkgconfig: remove cache The cache was a workaround for the badness of the way orogen was loading packages. Now that this is fixed, it does more harm than good since the pkgconfig objects end in the long-lived objects (something that can end up being up to 50k objects ...) --- lib/utilrb/pkgconfig.rb | 25 ++++++------------------- test/test_pkgconfig.rb | 1 - 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/lib/utilrb/pkgconfig.rb b/lib/utilrb/pkgconfig.rb index 649cae2..ce6ac63 100644 --- a/lib/utilrb/pkgconfig.rb +++ b/lib/utilrb/pkgconfig.rb @@ -41,15 +41,6 @@ class PkgConfig VAR_NAME_RX = /\w+/ FIELD_NAME_RX = /[\w\.\-]+/ - class << self - attr_reader :loaded_packages - - def clear_cache - loaded_packages.clear - end - end - @loaded_packages = Hash.new - def self.load(path, preset_variables) pkg_name = File.basename(path, ".pc") pkg = Class.instance_method(:new).bind(PkgConfig).call(pkg_name) @@ -60,17 +51,13 @@ def self.load(path, preset_variables) # Returns the pkg-config object that matches the given name, and # optionally a version string def self.get(name, version_spec = nil, preset_variables = Hash.new) - if !(candidates = loaded_packages[name]) - paths = find_all_package_files(name) - if paths.empty? - raise NotFound.new(name), "cannot find the pkg-config specification for #{name}" - end + paths = find_all_package_files(name) + if paths.empty? + raise NotFound.new(name), "cannot find the pkg-config specification for #{name}" + end - candidates = Array.new - paths.each do |p| - candidates << PkgConfig.load(p, preset_variables) - end - loaded_packages[name] = candidates + candidates = paths.map do |p| + PkgConfig.load(p, preset_variables) end # Now try to find a matching spec diff --git a/test/test_pkgconfig.rb b/test/test_pkgconfig.rb index 594b03f..dbf43d4 100644 --- a/test/test_pkgconfig.rb +++ b/test/test_pkgconfig.rb @@ -9,7 +9,6 @@ def setup end def teardown ENV['PKG_CONFIG_PATH'] = @old_pkg_config_path - PkgConfig.clear_cache end PkgConfig = Utilrb::PkgConfig From cdf69911ca87c83e8f0319695ed1853002aaba55 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 18 Apr 2016 13:16:19 -0300 Subject: [PATCH 59/67] pkgconfig: refactor #load into smaller parts --- lib/utilrb/pkgconfig.rb | 129 ++++++++++++++++++++++++++++------------ test/test_pkgconfig.rb | 2 - 2 files changed, 91 insertions(+), 40 deletions(-) diff --git a/lib/utilrb/pkgconfig.rb b/lib/utilrb/pkgconfig.rb index ce6ac63..c2a29b4 100644 --- a/lib/utilrb/pkgconfig.rb +++ b/lib/utilrb/pkgconfig.rb @@ -48,24 +48,36 @@ def self.load(path, preset_variables) pkg end + def self.load_minimal(path, preset_variables) + pkg_name = File.basename(path, ".pc") + pkg = Class.instance_method(:new).bind(PkgConfig).call(pkg_name) + pkg.load_minimal(path, preset_variables) + pkg + end + # Returns the pkg-config object that matches the given name, and # optionally a version string - def self.get(name, version_spec = nil, preset_variables = Hash.new) + def self.get(name, version_spec = nil, preset_variables = Hash.new, minimal: false) paths = find_all_package_files(name) if paths.empty? raise NotFound.new(name), "cannot find the pkg-config specification for #{name}" end candidates = paths.map do |p| - PkgConfig.load(p, preset_variables) + PkgConfig.load_minimal(p, preset_variables) end # Now try to find a matching spec - if version_match = find_matching_version(candidates, version_spec) - version_match + if match = find_matching_version(candidates, version_spec) + match else raise NotFound, "found #{candidates.size} packages for #{name}, but none match the version specification #{version_spec}" end + + if !minimal + match.load_fields + end + match end # Finds the provided package and optional version and returns its @@ -127,7 +139,6 @@ def initialize(name); @name = name end end - attr_reader :file attr_reader :path # The module name @@ -138,6 +149,8 @@ def initialize(name); @name = name end # The module version, as an array of integers attr_reader :version + attr_reader :raw_fields + # Information extracted from the file attr_reader :variables attr_reader :fields @@ -156,7 +169,7 @@ def initialize(name) # +current+ is a string that describes what we are expanding. It is used # to detect recursion in expansion of variables, and to give meaningful # errors to the user - def expand_variables(value, variables, current) + def perform_substitution(value, variables, current) value = value.gsub(/\$\{(\w+)\}/) do |rx| expand_name = $1 if expand_name == current @@ -198,16 +211,13 @@ def self.parse_dependencies(string) SHELL_VARS = %w{Cflags Libs Libs.private} - # Loads the information contained in +path+ - def load(path, preset_variables = Hash.new) - @path = path - @file = File.readlines(path).map(&:strip) - - raw_variables = preset_variables.dup - raw_fields = Hash.new - + # Parse a pkg-config field and extracts the raw definition of variables + # and fields + # + # @return [(Hash,Hash)] the set of variables and the set of fields + def parse(path) running_line = nil - @file = file.map do |line| + file = File.readlines(path).map do |line| line = line.gsub(/\s*#.*$/, '') line = line.strip next if line.empty? @@ -227,6 +237,7 @@ def load(path, preset_variables = Hash.new) end.compact + raw_variables, raw_fields = Hash.new, Hash.new file.each do |line| case line when /^(#{VAR_NAME_RX})\s*=(.*)/ @@ -237,44 +248,78 @@ def load(path, preset_variables = Hash.new) raise NotImplementedError, "#{path}: cannot parse pkg-config line #{line.inspect}" end end + return raw_variables, raw_fields + end + + def expand_variables(raw_variables) + raw_variables = raw_variables.dup + variables = Hash.new # Resolve the variables while variables.size != raw_variables.size raw_variables.each do |name, value| - value = expand_variables(value, raw_variables, name) + value = perform_substitution(value, raw_variables, name) raw_variables[name] = value if value !~ /\$\{#{VAR_NAME_RX}\}/ variables[name] = value end end end - - # Shell-split the fields, and expand the variables in them - raw_fields.each do |name, value| - if SHELL_VARS.include?(name) - value = Shellwords.shellsplit(value) - resolved = Array.new - while !value.empty? - value = value.flat_map do |v| - expanded = expand_variables(v, variables, name) - if expanded == v - resolved << v - nil - else - Shellwords.shellsplit(expanded) - end - end.compact - end - fields[name] = resolved - else - fields[name] = expand_variables(value, variables, name) + variables + end + + def expand_field(name, field) + if SHELL_VARS.include?(name) + value = Shellwords.shellsplit(field) + resolved = Array.new + while !value.empty? + value = value.flat_map do |v| + expanded = perform_substitution(v, variables, name) + if expanded == v + resolved << v + nil + else + Shellwords.shellsplit(expanded) + end + end.compact end + resolved + else + perform_substitution(field, variables, name) + end + end + + def load_variables(path, preset_variables = Hash.new) + raw_variables, raw_fields = parse(path) + raw_variables = preset_variables.merge(raw_variables) + expand_variables(raw_variables) + end + + def load_minimal(path, preset_variables = Hash.new) + raw_variables, raw_fields = parse(path) + raw_variables = preset_variables.merge(raw_variables) + + @variables = expand_variables(raw_variables) + if raw_fields['Version'] + @raw_version = expand_field('Version', raw_fields['Version']) + else + @raw_version = '' + end + @version = raw_version.split('.').map { |v| Integer(v) if v =~ /^\d+$/ }.compact + # To be used in the call to #load + @raw_fields = raw_fields + @path = path + end + + def load_fields + fields = Hash.new + @raw_fields.each do |name, value| + fields[name] = expand_field(name, value) end + @fields = fields # Initialize the main flags - @raw_version = (fields['Version'] || '') - @version = raw_version.split('.').map { |v| Integer(v) if v =~ /^\d+$/ }.compact @description = (fields['Description'] || '') # Get the requires/conflicts @@ -313,6 +358,14 @@ def load(path, preset_variables = Hash.new) end end + # Loads the information contained in +path+ + def load(path, preset_variables = Hash.new) + if !@raw_fields + load_minimal(path, preset_variables) + end + load_fields + end + def self.define_pkgconfig_action(action) # :nodoc: class_eval <<-EOD, __FILE__, __LINE__+1 def pkgconfig_#{action.gsub(/-/, '_')}(static = false) diff --git a/test/test_pkgconfig.rb b/test/test_pkgconfig.rb index dbf43d4..e409c01 100644 --- a/test/test_pkgconfig.rb +++ b/test/test_pkgconfig.rb @@ -89,8 +89,6 @@ def test_comparison_with_cpkgconfig puts "#{name} #{action_name}" puts " pure ruby: #{pure_ruby.inspect}" puts " cpkgconfig: #{cpkgconfig.inspect}" - puts "contents:" - puts pkg.file.join("\n") end end end From 6de5188f4061032a31ff3346ce84b924aee16f71 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 2 May 2016 09:56:46 -0300 Subject: [PATCH 60/67] pkgconfig: re-add .clear_cache for backward compatibility --- lib/utilrb/pkgconfig.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/utilrb/pkgconfig.rb b/lib/utilrb/pkgconfig.rb index c2a29b4..20453eb 100644 --- a/lib/utilrb/pkgconfig.rb +++ b/lib/utilrb/pkgconfig.rb @@ -55,6 +55,11 @@ def self.load_minimal(path, preset_variables) pkg end + # @deprecated {PkgConfig} does not cache the packages anymore, so no + # need to call this method + def self.clear_cache + end + # Returns the pkg-config object that matches the given name, and # optionally a version string def self.get(name, version_spec = nil, preset_variables = Hash.new, minimal: false) From 153e0a614ac046129cfba491b2905ebffb6c30cf Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 2 May 2016 09:26:46 -0300 Subject: [PATCH 61/67] pkgconfig: allow to use a non-default path on all search methods --- lib/utilrb/pkgconfig.rb | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/utilrb/pkgconfig.rb b/lib/utilrb/pkgconfig.rb index 20453eb..94f6e1b 100644 --- a/lib/utilrb/pkgconfig.rb +++ b/lib/utilrb/pkgconfig.rb @@ -62,8 +62,8 @@ def self.clear_cache # Returns the pkg-config object that matches the given name, and # optionally a version string - def self.get(name, version_spec = nil, preset_variables = Hash.new, minimal: false) - paths = find_all_package_files(name) + def self.get(name, version_spec = nil, preset_variables = Hash.new, minimal: false, pkg_config_path: self.pkg_config_path) + paths = find_all_package_files(name, pkg_config_path: pkg_config_path) if paths.empty? raise NotFound.new(name), "cannot find the pkg-config specification for #{name}" end @@ -460,17 +460,22 @@ def method_missing(varname, *args, &proc) # :nodoc: end end - def self.each_pkgconfig_directory(&block) - if path = ENV['PKG_CONFIG_PATH'] - path.split(':').each(&block) + def self.pkg_config_path + ENV['PKG_CONFIG_PATH'] + end + + def self.each_pkgconfig_directory(pkg_config_path: self.pkg_config_path, &block) + return enum_for(__method__) if !block_given? + if pkg_config_path + pkg_config_path.split(':').each(&block) end default_search_path.each(&block) end # Returns true if there is a package with this name - def self.find_all_package_files(name) + def self.find_all_package_files(name, pkg_config_path: self.pkg_config_path) result = [] - each_pkgconfig_directory do |dir| + each_pkgconfig_directory(pkg_config_path: pkg_config_path) do |dir| path = File.join(dir, "#{name}.pc") if File.exist?(path) result << path @@ -479,9 +484,9 @@ def self.find_all_package_files(name) result end - def self.available_package_names + def self.available_package_names(pkg_config_path: self.pkg_config_path) result = [] - each_pkgconfig_directory do |dir| + each_pkgconfig_directory(pkg_config_path: pkg_config_path) do |dir| Dir.glob(File.join(dir, "*.pc")) do |path| result << File.basename(path, ".pc") end @@ -490,17 +495,17 @@ def self.available_package_names end # Returns true if there is a package with this name - def self.has_package?(name) - !find_all_package_files(name).empty? + def self.has_package?(name, pkg_config_path: self.pkg_config_path) + !find_all_package_files(name, pkg_config_path: pkg_config_path).empty? end # Yields the package names of available packages. If +regex+ is given, # lists only the names that match the regular expression. - def self.each_package(regex = nil) + def self.each_package(regex = nil, pkg_config_path: self.pkg_config_path) return enum_for(__method__) if !block_given? seen = Set.new - each_pkgconfig_directory do |dir| + each_pkgconfig_directory(pkg_config_path: pkg_config_path) do |dir| Dir.glob(File.join(dir, '*.pc')) do |file| pkg_name = File.basename(file, ".pc") next if seen.include?(pkg_name) From 1da06dc081cb2a468e8c420eee031cbe048a169f Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Tue, 23 Aug 2016 16:08:18 -0300 Subject: [PATCH 62/67] remove the GC tests There's really no way to get robust tests. These fail most of the time on 2.2+, and randomly on 2.0-2.1. --- test/test_gc.rb | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 test/test_gc.rb diff --git a/test/test_gc.rb b/test/test_gc.rb deleted file mode 100644 index b48b903..0000000 --- a/test/test_gc.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'utilrb/test' - -require 'utilrb/gc' -require 'enumerator' - -class TC_GC < Minitest::Test - def allocate(&block) - # Allocate twice since it seems the last object stays on stack - # (and is not GC'ed) - 2.times { ObjectSpace.define_finalizer(Object.new, &block) } - nil - end - - def test_force - finalized = false - allocate { finalized = true } - GC.start - assert( finalized ) - - GC.disable - finalized = false - allocate { finalized = true } - GC.start - assert( !finalized ) - GC.force - assert( finalized ) - assert( GC.disable ) - - GC.enable - GC.force - assert( !GC.enable ) - end -end - From 38ca178828c23b2512f01fa2ec25542f21e12de1 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Wed, 5 Oct 2016 12:35:53 -0300 Subject: [PATCH 63/67] refactor #check_arity This expands the unit tests to cover all cases of all callable objects (methods, procs and lambdas), and fixes inconsistencies. It also adds a 'strict' keyword argument that allows to check against method rules, that is that passing less or more arguments than the object expects is forbidden (unlike with procs). --- lib/utilrb/kernel/arity.rb | 17 ++-- test/test_kernel.rb | 157 +++++++++++++++++++++++-------------- 2 files changed, 111 insertions(+), 63 deletions(-) diff --git a/lib/utilrb/kernel/arity.rb b/lib/utilrb/kernel/arity.rb index 2e30f4d..c4c51b7 100644 --- a/lib/utilrb/kernel/arity.rb +++ b/lib/utilrb/kernel/arity.rb @@ -1,15 +1,20 @@ module Kernel # Raises if +object+ can accept calls with exactly +arity+ arguments. # object should respond to #arity - def check_arity(object, arity) - if object.respond_to?(:lambda?) # For ruby 1.9 compatibility on blocks without arguments - if !object.lambda? && object.arity == 0 - return + def check_arity(object, arity, strict: nil) + if strict.nil? + if object.respond_to?(:lambda?) + strict = object.lambda? + else strict = true end end - unless object.arity == arity || (object.arity < 0 && object.arity > - arity - 2) - raise ArgumentError, "#{object} does not accept to be called with #{arity} argument(s)", caller(2) + if strict + if object.arity >= 0 && object.arity != arity + raise ArgumentError, "#{object} requests #{object.arity} arguments, but #{arity} was requested" + elsif -object.arity-1 > arity + raise ArgumentError, "#{object} requests at least #{object.arity} arguments, but #{arity} was requested" + end end end end diff --git a/test/test_kernel.rb b/test/test_kernel.rb index 5ce8d97..fcbb9a0 100644 --- a/test/test_kernel.rb +++ b/test/test_kernel.rb @@ -62,63 +62,6 @@ def test_filter_options_filters_keys_that_have_a_nil_value assert_equal [Hash[c: 10], Hash.new], filter_options(Hash[c: 10], c: nil) end - def test_arity_of_methods - object = Class.new do - def arity_1(a); end - def arity_any(*a); end - def arity_1_more(a, *b); end - end.new - - # Should not raise - check_arity(object.method(:arity_1), 1) - assert_raises(ArgumentError) { check_arity(object.method(:arity_1), 0) } - assert_raises(ArgumentError) { check_arity(object.method(:arity_1), 2) } - - # Should not raise - check_arity(object.method(:arity_any), 0) - check_arity(object.method(:arity_any), 2) - - check_arity(object.method(:arity_1_more), 1) - assert_raises(ArgumentError) { check_arity(object.method(:arity_1_more), 0) } - check_arity(object.method(:arity_1_more), 2) - end - - def test_arity_of_blocks - check_arity(Proc.new { bla }, 0) - check_arity(Proc.new { bla }, 1) - check_arity(Proc.new { bla }, 2) - - assert_raises(ArgumentError) { check_arity(Proc.new { |arg| bla }, 0) } - check_arity(Proc.new { |arg| bla }, 1) - assert_raises(ArgumentError) { check_arity(Proc.new { |arg| bla }, 2) } - - assert_raises(ArgumentError) { check_arity(Proc.new { |arg, *args| bla }, 0) } - check_arity(Proc.new { |arg, *args| bla }, 1) - check_arity(Proc.new { |arg, *args| bla }, 2) - - check_arity(Proc.new { |*args| bla }, 0) - check_arity(Proc.new { |*args| bla }, 1) - check_arity(Proc.new { |*args| bla }, 2) - end - - def test_arity_of_lambdas - check_arity(lambda { bla }, 0) - assert_raises(ArgumentError) { check_arity(lambda { bla }, 1) } - assert_raises(ArgumentError) { check_arity(lambda { bla }, 2) } - - assert_raises(ArgumentError) { check_arity(lambda { |arg| bla }, 0) } - check_arity(lambda { |arg| bla }, 1) - assert_raises(ArgumentError) { check_arity(lambda { |arg| bla }, 2) } - - assert_raises(ArgumentError) { check_arity(lambda { |arg, *args| bla }, 0) } - check_arity(lambda { |arg, *args| bla }, 1) - check_arity(lambda { |arg, *args| bla }, 2) - - check_arity(lambda { |*args| bla }, 0) - check_arity(lambda { |*args| bla }, 1) - check_arity(lambda { |*args| bla }, 2) - end - def test_with_module obj = Object.new c0, c1 = nil @@ -307,3 +250,103 @@ def test_wait_while end end +describe "Kernel extensions" do + describe "#check_arity" do + def assert_arity_check(obj, *args, strict: nil) + test_obj = nil + if strict + Class.new(Object) do + test_obj = method(:test, &obj) + end + else + test_obj = obj + end + + begin + check_arity(obj, args.size, strict: strict) + rescue ArgumentError + begin + test_obj.call(*args) + flunk("#check_arity(_, #{args.size}) raised but #call passed") + rescue ArgumentError + return false + end + end + + begin test_obj.call(*args) + rescue ArgumentError => e + flunk("#check_arity(_, #{args.size}) passed but #call failed with #{e.message}") + end + true + end + + def assert_arity_check_succeeds(obj, *args, strict: nil) + assert assert_arity_check(obj, *args, strict: strict), "arity check was expected to pass, but failed" + end + + def assert_arity_check_fails(obj, *args, strict: nil) + refute assert_arity_check(obj, *args, strict: strict), "arity check was expected to fail, but succeeded" + end + + describe "of methods" do + attr_reader :object + before do + @object = Class.new do + def arity_1(a); end + def arity_any(*a); end + def arity_1_more(a, *b); end + end.new + end + + it "passes if the method accepts the exact number of arguments" do + assert_arity_check_succeeds(object.method(:arity_1), 1) + end + + it "passes if the method accepts the requested number of arguments through a splat" do + assert_arity_check_succeeds(object.method(:arity_any), 1) + assert_arity_check_succeeds(object.method(:arity_1_more), 1) + end + + it "raises if the method requires more arguments than requested" do + assert_arity_check_fails(object.method(:arity_1_more)) + end + + it "raises if the method cannot accept as many arguments as requested" do + assert_arity_check_fails(object.method(:arity_1), 1, 2) + end + end + + describe "of procs" do + it "accepts more arguments than declared by the proc" do + assert_arity_check_succeeds(proc { }, 1, 2) + assert_arity_check_succeeds(proc { |a| }, 1, 2) + end + + it "passes if the proc requires more arguments than requested" do + assert_arity_check_succeeds(proc { |a, b| }, 1) + end + + it "is evaluated using strict (method) rules if the strict argument is given as true" do + assert_arity_check_fails(proc { |a| }, 1, 2, strict: true) + end + end + + describe "of lambdas" do + it "does not accept more arguments than declared by the lambda" do + assert_arity_check_fails(lambda { }, 1, 2) + assert_arity_check_fails(lambda { |a| }, 1, 2) + end + + it "passes if given less arguments than expected by the lambda" do + assert_arity_check_fails(lambda { |a, b| }, 1) + end + + it "is evaluated using strict (method) rules if the strict argument is given as true" do + assert_arity_check_fails(lambda { |a| }, 1, 2, strict: true) + end + end + end +end + + + From 0aa7571de344611abccb00e8dda9715d7b28a287 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Tue, 11 Oct 2016 15:51:33 -0300 Subject: [PATCH 64/67] travis: update MRI versions --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6efab46..7745f28 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,9 @@ sudo: false language: ruby rvm: - 2.0.0 - - 2.1.6 - - 2.2.2 + - 2.1.9 + - 2.2.5 + - 2.3.1 script: - bundle exec rake - bundle exec rake test From bbd2779984f64b5db94718f85eef45e4a592b690 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Tue, 11 Oct 2016 15:41:04 -0300 Subject: [PATCH 65/67] logger: replace highline by pastel in logger color handling Pastel disables colors automatically if the terminal does not support it, which is a HUGE usability improvement. --- lib/utilrb/logger/root.rb | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/utilrb/logger/root.rb b/lib/utilrb/logger/root.rb index 19521c5..67d7bce 100644 --- a/lib/utilrb/logger/root.rb +++ b/lib/utilrb/logger/root.rb @@ -1,19 +1,19 @@ require 'utilrb/logger/hierarchy' + class Logger - HAS_COLOR = + LEVEL_TO_COLOR = begin - require 'highline' - @console = HighLine.new + require 'pastel' + colorizer = Pastel.new + { 'DEBUG' => ->(t) { t }, + 'INFO' => ->(t) { t }, + 'WARN' => colorizer.magenta.detach, + 'ERROR' => colorizer.red.detach, + 'FATAL' => colorizer.red.bold.detach } rescue LoadError + Hash.new end - LEVEL_TO_COLOR = - { 'DEBUG' => [], - 'INFO' => [], - 'WARN' => [:magenta], - 'ERROR' => [:red], - 'FATAL' => [:red, :bold] } - # Defines a logger on a module, allowing to use that module as a root in a # hierarchy (i.e. having submodules use the Logger::Hierarchy support) # @@ -56,9 +56,9 @@ def self.Root(progname, base_level, &block) console = @console formatter = if block then lambda(&block) - elsif HAS_COLOR + elsif !LEVEL_TO_COLOR.empty? lambda do |severity, time, name, msg| - console.color("#{name}[#{severity}]: #{msg}\n", *LEVEL_TO_COLOR[severity]) + LEVEL_TO_COLOR[severity].call("#{name}[#{severity}]: #{msg}\n") end else lambda { |severity, time, name, msg| "#{name}[#{severity}]: #{msg}\n" } end From b10448bfdede93c66e901841826f114d80abec6b Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Tue, 18 Oct 2016 14:30:08 -0200 Subject: [PATCH 66/67] logger: forward log_level= and log_level to logger.level= and logger.level --- lib/utilrb/logger/forward.rb | 10 ++++++++++ test/test_logger.rb | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/utilrb/logger/forward.rb b/lib/utilrb/logger/forward.rb index 78bc707..3d380cd 100644 --- a/lib/utilrb/logger/forward.rb +++ b/lib/utilrb/logger/forward.rb @@ -15,6 +15,16 @@ def #{level}(*args, &proc); logger.#{level}(*args, &proc) end EOF end + # The logger level + def log_level + logger.level + end + + # Sets the logger's level + def log_level=(level) + logger.level = level + end + # Forwarded to {Logger#silent} def log_silent(&block) logger.silent(&block) diff --git a/test/test_logger.rb b/test/test_logger.rb index cf0eed2..62b3606 100644 --- a/test/test_logger.rb +++ b/test/test_logger.rb @@ -201,4 +201,17 @@ def test_instance_resolves_to_own_logger_if_set a_logger = HierarchyTest::A.make_own_logger assert_same a_logger, HierarchyTest::A::B.logger end + + def test_forwards_log_level_to_level + obj = Class.new do + attr_accessor :logger + include Logger::Forward + end.new + obj.logger = (logger_mock = flexmock) + logger_mock.should_receive(:level=).with(10) + logger_mock.should_receive(:level).and_return(10) + obj.log_level = 10 + assert_equal 10, obj.log_level + end end + From df14da41b03fc717f89dae5813ae795afc36f2ce Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Mon, 12 Dec 2016 14:37:28 -0200 Subject: [PATCH 67/67] logger: include the superclass when resolving the default logger The following setup module WithLogger extend Logger::Root(...) class Base extend Logger::Hierarchy end end module NoLogger class Derived < Base extend Logger::Hierarchy end end was not considering Base's logger, which is really unexpected. It would instead go directly to WithLogging's logger. This commit adds Base in the search order. --- lib/utilrb/logger/hierarchy.rb | 5 +++++ test/test_logger.rb | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/lib/utilrb/logger/hierarchy.rb b/lib/utilrb/logger/hierarchy.rb index 9c23973..9ef75d0 100644 --- a/lib/utilrb/logger/hierarchy.rb +++ b/lib/utilrb/logger/hierarchy.rb @@ -130,8 +130,13 @@ def logger break end end + if m.respond_to?(:superclass) m = m.superclass + if m.respond_to?(:logger) + parent_module = m + break + end else m = nil; break end diff --git a/test/test_logger.rb b/test/test_logger.rb index 62b3606..fadfa84 100644 --- a/test/test_logger.rb +++ b/test/test_logger.rb @@ -178,6 +178,10 @@ def test_hierarchy_can_resolve_parent_logger_with_identical_name def test_hierarchy_can_resolve_parent_logger_in_subclasses_where_the_subclass_parent_module_is_not_providing_a_logger assert_equal "root_logger", NotALoggingModule::HierarchyTest.logger end + def test_hierarchy_resolution_starts_at_superclass_if_enclosing_module_does_not_provide_a_logger + flexmock(HierarchyTest::HierarchyTest).should_receive(logger: "specific_class_logger") + assert_equal "specific_class_logger", NotALoggingModule::HierarchyTest.logger + end def test_hierarchy_resolves_the_parent_module_first_even_in_subclasses assert_equal "other_logger", HierarchyTestForSubclass::HierarchyTest.logger end