From 940c0b3cffa7f691620e7890ada15c7519817307 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 20 Dec 2019 01:39:14 +0000 Subject: provide fiddle-based eventfd implementation sleepy_penguin requires a compiler and development headers to install, so it could be a PITA to install for users on distro-provided Ruby. Allow fiddle since it's part of the Ruby standard library since 1.9.2 and users won't have to install anything else. --- lib/dtas.rb | 8 ++++++++ lib/dtas/sigevent.rb | 7 +++++-- lib/dtas/sigevent/efd.rb | 2 ++ lib/dtas/sigevent/fiddle_efd.rb | 38 ++++++++++++++++++++++++++++++++++++++ lib/dtas/sigevent/pipe.rb | 2 +- test/test_sigevent.rb | 20 ++++++++++++++++++++ 6 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 lib/dtas/sigevent/fiddle_efd.rb create mode 100644 test/test_sigevent.rb diff --git a/lib/dtas.rb b/lib/dtas.rb index 7e9c0d5..ae2f815 100644 --- a/lib/dtas.rb +++ b/lib/dtas.rb @@ -25,6 +25,14 @@ module DTAS @null ||= File.open('/dev/null', 'r+') end + @libc = nil + def self.libc + @libc ||= begin + require 'fiddle' + Fiddle.dlopen(nil) + end + end + # String#-@ will deduplicate strings when Ruby 2.5 is released (Dec 2017) # https://bugs.ruby-lang.org/issues/13077 if RUBY_VERSION.to_f >= 2.5 diff --git a/lib/dtas/sigevent.rb b/lib/dtas/sigevent.rb index d4a96d7..74d22df 100644 --- a/lib/dtas/sigevent.rb +++ b/lib/dtas/sigevent.rb @@ -3,8 +3,11 @@ # frozen_string_literal: true begin raise LoadError, "no eventfd with _DTAS_POSIX" if ENV["_DTAS_POSIX"] - require 'sleepy_penguin' - require_relative 'sigevent/efd' + begin + require_relative 'sigevent/efd' + rescue LoadError + require_relative 'sigevent/fiddle_efd' + end rescue LoadError require_relative 'sigevent/pipe' end diff --git a/lib/dtas/sigevent/efd.rb b/lib/dtas/sigevent/efd.rb index 4be2c84..22772ee 100644 --- a/lib/dtas/sigevent/efd.rb +++ b/lib/dtas/sigevent/efd.rb @@ -3,6 +3,8 @@ # used in various places for safe wakeups from IO.select via signals # This requires a modern Linux system and the "sleepy_penguin" RubyGem +require 'sleepy_penguin' + class DTAS::Sigevent < SleepyPenguin::EventFD # :nodoc: def self.new super(0, :CLOEXEC) diff --git a/lib/dtas/sigevent/fiddle_efd.rb b/lib/dtas/sigevent/fiddle_efd.rb new file mode 100644 index 0000000..e29f6ca --- /dev/null +++ b/lib/dtas/sigevent/fiddle_efd.rb @@ -0,0 +1,38 @@ +# Copyright (C) 2013-2019 all contributors +# License: GPL-3.0+ +# frozen_string_literal: true + +# used in various places for safe wakeups from IO.select via signals +# This requires a modern GNU/Linux system with eventfd(2) support +require_relative '../nonblock' +require 'fiddle' +class DTAS::Sigevent # :nodoc: + + EventFD = Fiddle::Function.new(DTAS.libc['eventfd'], + [ Fiddle::TYPE_INT, Fiddle::TYPE_INT ], # initval, flags + Fiddle::TYPE_INT) # fd + + attr_reader :to_io + ONE = [ 1 ].pack('Q').freeze + + def initialize + fd = EventFD.call(0, 02000000|00004000) # EFD_CLOEXEC|EFD_NONBLOCK + raise "eventfd failed: #{Fiddle.last_error}" if fd < 0 + @to_io = DTAS::Nonblock.for_fd(fd) + @buf = ''.b + end + + def signal + @to_io.syswrite(ONE) + end + + def readable_iter + @to_io.read_nonblock(8, @buf, exception: false) + yield self, nil # calls DTAS::Process.reaper + :wait_readable + end + + def close + @to_io.close + end +end diff --git a/lib/dtas/sigevent/pipe.rb b/lib/dtas/sigevent/pipe.rb index 921a5b3..0ea9d31 100644 --- a/lib/dtas/sigevent/pipe.rb +++ b/lib/dtas/sigevent/pipe.rb @@ -3,7 +3,7 @@ # frozen_string_literal: true # used in various places for safe wakeups from IO.select via signals -# A fallback for non-Linux systems lacking the "sleepy_penguin" RubyGem +# A fallback for non-Linux systems lacking the "splice" syscall require_relative '../nonblock' class DTAS::Sigevent # :nodoc: attr_reader :to_io diff --git a/test/test_sigevent.rb b/test/test_sigevent.rb new file mode 100644 index 0000000..6cfe528 --- /dev/null +++ b/test/test_sigevent.rb @@ -0,0 +1,20 @@ +# Copyright (C) 2019 all contributors +# License: GPL-3.0+ +# frozen_string_literal: true +require_relative 'helper' +require 'dtas' +require 'dtas/sigevent' + +class TestSigevent < Testcase + def test_sigevent + io = DTAS::Sigevent.new + io.signal + assert IO.select([io]), 'IO.select returns' + res = io.readable_iter do |f,arg| + assert_same io, f + assert_nil arg + end + assert_equal :wait_readable, res + assert_nil io.close + end +end -- cgit v1.2.3-24-ge0c7