librelist archives

« back to archive

[PATCH 1/2] remove all signalfd-related files

[PATCH 1/2] remove all signalfd-related files

From:
Eric Wong
Date:
2013-10-19 @ 17:59
They're long disabled, and there's no way for them to work sanely
inside any VM/language which supports signal handlers.  There's
little need, even, as Ruby has a good API for signal handlers and we
have eventfd support.
---
 ext/sleepy_penguin/signalfd.c | 342 ------------------------------------------
 test/test_signalfd.rb         |  94 ------------
 test/test_signalfd_siginfo.rb |  32 ----
 3 files changed, 468 deletions(-)
 delete mode 100644 ext/sleepy_penguin/signalfd.c
 delete mode 100644 test/test_signalfd.rb
 delete mode 100644 test/test_signalfd_siginfo.rb

diff --git a/ext/sleepy_penguin/signalfd.c b/ext/sleepy_penguin/signalfd.c
deleted file mode 100644
index ebd6c86..0000000
--- a/ext/sleepy_penguin/signalfd.c
+++ /dev/null
@@ -1,342 +0,0 @@
-#ifdef HAVE_SYS_SIGNALFD_H
-#include "sleepy_penguin.h"
-#include <signal.h>
-#include <sys/signalfd.h>
-static VALUE ssi_members;
-static VALUE cSigInfo;
-
-/* converts a Symbol, String, or Fixnum to an integer signal */
-static int sig2int(VALUE sig)
-{
-	static VALUE list;
-	const char *ptr;
-	long len;
-
-	if (TYPE(sig) == T_FIXNUM)
-		return FIX2INT(sig);
-
-	sig = rb_obj_as_string(sig);
-	len = RSTRING_LEN(sig);
-	ptr = RSTRING_PTR(sig);
-
-	if (len > 3 && !memcmp("SIG", ptr, 3))
-		sig = rb_str_new(ptr + 3, len - 3);
-
-	if (!list) {
-		VALUE tmp = rb_const_get(rb_cObject, rb_intern("Signal"));
-
-		list = rb_funcall(tmp, rb_intern("list"), 0, 0);
-		rb_global_variable(&list);
-	}
-
-	sig = rb_hash_aref(list, sig);
-	if (NIL_P(sig))
-		rb_raise(rb_eArgError, "invalid signal: %s", ptr);
-
-	return NUM2INT(sig);
-}
-
-/* fills sigset_t with an Array of signals */
-static void value2sigset(sigset_t *mask, VALUE set)
-{
-	sigemptyset(mask);
-
-	switch (TYPE(set)) {
-	case T_NIL: return;
-	case T_ARRAY: {
-		long i;
-		long len = RARRAY_LEN(set);
-
-		for (i = 0; i < len; i++)
-			sigaddset(mask, sig2int(rb_ary_entry(set, i)));
-		}
-		break;
-	default:
-		sigaddset(mask, sig2int(set));
-	}
-}
-
-static int cur_flags(int fd)
-{
-	int rv = 0;
-#ifdef SFD_CLOEXEC
-	{
-		int flags = fcntl(fd, F_GETFD);
-		if (flags == -1) rb_sys_fail("fcntl(F_GETFD)");
-		if (flags & FD_CLOEXEC) rv |= SFD_CLOEXEC;
-	}
-#endif
-#ifdef SFD_NONBLOCK
-	{
-		int flags = fcntl(fd, F_GETFL);
-		if (flags == -1) rb_sys_fail("fcntl(F_GETFL)");
-		if (flags & O_NONBLOCK) rv |= SFD_NONBLOCK;
-	}
-#endif
-	return rv;
-}
-
-/*
- * call-seq:
- *	sfd.update!(signals[, flags])	-> sfd
- *
- * Updates the signal mask watched for by the given +sfd+.
- * Takes the same arguments as SignalFD.new.
- */
-static VALUE update_bang(int argc, VALUE *argv, VALUE self)
-{
-	VALUE vmask, vflags;
-	sigset_t mask;
-	int flags;
-	int fd = rb_sp_fileno(self);
-	int rc;
-
-	rb_scan_args(argc, argv, "02", &vmask, &vflags);
-	flags = NIL_P(vflags) ? cur_flags(fd)
-				: rb_sp_get_flags(self, vflags, 0);
-	value2sigset(&mask, vmask);
-
-	rc = signalfd(fd, &mask, flags);
-	if (rc < 0)
-		rb_sys_fail("signalfd");
-	return self;
-}
-
-/*
- * call-seq:
- *	SignalFD.new(signals[, flags])	-> SignalFD IO object
- *
- * Creates a new SignalFD object to watch given +signals+ with +flags+.
- *
- * +signals+ is an Array of signal names or a single signal name that
- * Signal.trap understands:
- *
- *	signals = [ :USR1, "USR2" ]
- *	signals = :USR1
- *	signals = 15
- *
- * Starting with Linux 2.6.27, +flags+ may be a mask that consists of any
- * of the following:
- *
- * - :CLOEXEC - set the close-on-exec flag on the new object
- * - :NONBLOCK - set the non-blocking I/O flag on the new object
- */
-static VALUE s_new(int argc, VALUE *argv, VALUE klass)
-{
-	VALUE vmask, vflags, rv;
-	sigset_t mask;
-	int flags;
-	int fd;
-
-	rb_scan_args(argc, argv, "02", &vmask, &vflags);
-	flags = rb_sp_get_flags(klass, vflags, RB_SP_CLOEXEC(SFD_CLOEXEC));
-	value2sigset(&mask, vmask);
-
-	fd = signalfd(-1, &mask, flags);
-	if (fd < 0) {
-		if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
-			rb_gc();
-			fd = signalfd(-1, &mask, flags);
-		}
-		if (fd < 0)
-			rb_sys_fail("signalfd");
-	}
-
-
-	rv = INT2FIX(fd);
-	return rb_call_super(1, &rv);
-}
-
-static VALUE ssi_alloc(VALUE klass)
-{
-	struct signalfd_siginfo *ssi = ALLOC(struct signalfd_siginfo);
-
-	return Data_Wrap_Struct(klass, NULL, -1, ssi);
-}
-
-/* :nodoc: */
-static VALUE ssi_init(VALUE self)
-{
-	struct signalfd_siginfo *ssi = DATA_PTR(self);
-
-	memset(ssi, 0, sizeof(struct signalfd_siginfo));
-	return self;
-}
-
-static VALUE sfd_read(void *args)
-{
-	struct signalfd_siginfo *ssi = args;
-	int fd = ssi->ssi_fd;
-	ssize_t r = read(fd, ssi, sizeof(struct signalfd_siginfo));
-
-	return (VALUE)r;
-}
-
-/*
- * call-seq:
- *	sfd.take([nonblock]) -> SignalFD::SigInfo object or +nil+
- *
- * Returns the next SigInfo object representing a received signal.
- * If +nonblock+ is specified and true, this may return +nil+
- */
-static VALUE sfd_take(int argc, VALUE *argv, VALUE self)
-{
-	VALUE rv = ssi_alloc(cSigInfo);
-	struct signalfd_siginfo *ssi = DATA_PTR(rv);
-	ssize_t r;
-	int fd;
-	VALUE nonblock;
-
-	rb_scan_args(argc, argv, "01", &nonblock);
-	fd = rb_sp_fileno(self);
-	if (RTEST(nonblock))
-		rb_sp_set_nonblock(fd);
-	else
-		blocking_io_prepare(fd);
-retry:
-	ssi->ssi_fd = fd;
-	r = (ssize_t)rb_sp_fd_region(sfd_read, ssi, fd);
-	if (r < 0) {
-		if (errno == EAGAIN && RTEST(nonblock))
-			return Qnil;
-		if (rb_sp_wait(rb_io_wait_readable, self, &fd))
-			goto retry;
-		rb_sys_fail("read(signalfd)");
-	}
-	if (r == 0)
-		rb_eof_error(); /* does this ever happen? */
-	return rv;
-}
-
-#define SSI_READER_FUNC(FN, FIELD) \
-	static VALUE ssi_##FIELD(VALUE self) { \
-		struct signalfd_siginfo *ssi = DATA_PTR(self); \
-		return FN(ssi->ssi_##FIELD); \
-	}
-
-SSI_READER_FUNC(UINT2NUM,signo)
-SSI_READER_FUNC(INT2NUM,errno)
-SSI_READER_FUNC(INT2NUM,code)
-SSI_READER_FUNC(UINT2NUM,pid)
-SSI_READER_FUNC(UINT2NUM,uid)
-SSI_READER_FUNC(INT2NUM,fd)
-SSI_READER_FUNC(UINT2NUM,tid)
-SSI_READER_FUNC(UINT2NUM,band)
-SSI_READER_FUNC(UINT2NUM,overrun)
-SSI_READER_FUNC(UINT2NUM,trapno)
-SSI_READER_FUNC(INT2NUM,status)
-SSI_READER_FUNC(INT2NUM,int)
-SSI_READER_FUNC(ULL2NUM,ptr)
-SSI_READER_FUNC(ULL2NUM,utime)
-SSI_READER_FUNC(ULL2NUM,stime)
-SSI_READER_FUNC(ULL2NUM,addr)
-
-void sleepy_penguin_init_signalfd(void)
-{
-	VALUE mSleepyPenguin, cSignalFD;
-
-	mSleepyPenguin = rb_define_module("SleepyPenguin");
-
-	/*
-	 * Document-class: SleepyPenguin::SignalFD
-	 *
-	 * Use of this class is NOT recommended.  Ruby itself has a great
-	 * signal handling API and its implementation conflicts with this.
-	 *
-	 * This class is currently disabled and the documentation is only
-	 * provided to describe what it would look like.
-	 *
-	 * A SignalFD is an IO object for accepting signals.  It provides
-	 * an alternative to Signal.trap that may be monitored using
-	 * IO.select or Epoll.
-	 *
-	 * SignalFD appears interact unpredictably with YARV (Ruby 1.9) signal
-	 * handling and has been unreliable in our testing. Since Ruby has a
-	 * decent signal handling interface anyways, this class is less useful
-	 * than signalfd() in a C-only environment.
-	 *
-	 * It is not supported at all.
-	 */
-	cSignalFD = rb_define_class_under(mSleepyPenguin, "SignalFD", rb_cIO);
-
-	/*
-	 * Document-class: SleepyPenguin::SignalFD::SigInfo
-	 *
-	 * This class is returned by SignalFD#take.  It consists of the
-	 * following read-only members:
-	 *
-	 * - signo - signal number
-	 * - errno - error number
-	 * - code - signal code
-	 * - pid - PID of sender
-	 * - uid - real UID of sender
-	 * - fd - file descriptor (SIGIO)
-	 * - tid - kernel timer ID (POSIX timers)
-	 * - band - band event (SIGIO)
-	 * - overrun - POSIX timer overrun count
-	 * - trapno - trap number that caused hardware-generated signal
-	 * - exit status or signal (SIGCHLD)
-	 * - int - integer sent by sigqueue(2)
-	 * - ptr - Pointer sent by sigqueue(2)
-	 * - utime - User CPU time consumed (SIGCHLD)
-	 * - stime - System CPU time consumed (SIGCHLD)
-	 * - addr - address that generated a hardware-generated signal
-	 */
-	cSigInfo = rb_define_class_under(cSignalFD, "SigInfo", rb_cObject);
-	rb_define_alloc_func(cSigInfo, ssi_alloc);
-	rb_define_private_method(cSigInfo, "initialize", ssi_init, 0);
-
-	/* TODO:  si_code values */
-
-	rb_define_singleton_method(cSignalFD, "new", s_new, -1);
-#ifdef SFD_NONBLOCK
-	NODOC_CONST(cSignalFD, "NONBLOCK", INT2NUM(SFD_NONBLOCK));
-#endif
-#ifdef SFD_CLOEXEC
-	NODOC_CONST(cSignalFD, "CLOEXEC", INT2NUM(SFD_CLOEXEC));
-#endif
-
-	rb_define_method(cSignalFD, "take", sfd_take, -1);
-	rb_define_method(cSignalFD, "update!", update_bang, -1);
-	ssi_members = rb_ary_new();
-
-	NODOC_CONST(cSigInfo, "MEMBERS", ssi_members);
-
-	/*
-	 * the minimum signal number for real-time signals,
-	 * 34 on NPTL-based systems
-	 */
-	rb_define_const(cSignalFD, "RTMIN", INT2NUM(SIGRTMIN));
-
-	/*
-	 * the maximum signal number for real-time signals,
-	 * 64 on NPTL-based systems
-	 */
-	rb_define_const(cSignalFD, "RTMAX", INT2NUM(SIGRTMAX));
-
-#define SSI_READER(FIELD) do { \
-	  rb_define_method(cSigInfo, #FIELD, ssi_##FIELD, 0); \
-	  rb_ary_push(ssi_members, ID2SYM(rb_intern(#FIELD))); \
-	} while (0)
-
-	SSI_READER(signo);
-	SSI_READER(errno);
-	SSI_READER(code);
-	SSI_READER(pid);
-	SSI_READER(uid);
-	SSI_READER(fd);
-	SSI_READER(tid);
-	SSI_READER(band);
-	SSI_READER(overrun);
-	SSI_READER(trapno);
-	SSI_READER(status);
-	SSI_READER(int);
-	SSI_READER(ptr);
-	SSI_READER(utime);
-	SSI_READER(stime);
-	SSI_READER(addr);
-	rb_obj_freeze(ssi_members);
-
-	rb_require("sleepy_penguin/signalfd/sig_info");
-}
-#endif /* HAVE_SYS_SIGNALFD_H */
diff --git a/test/test_signalfd.rb b/test/test_signalfd.rb
deleted file mode 100644
index c80a085..0000000
--- a/test/test_signalfd.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-require './test/helper'
-require "dl"
-begin
-  require "dl/func"
-rescue LoadError
-end
-$-w = true
-require 'sleepy_penguin'
-
-class TestSignalFD < Testcase
-  include SleepyPenguin
-
-  def setup
-    @sfd = nil
-    trap(:USR1, "IGNORE")
-    trap(:USR2, "IGNORE")
-  end
-
-  def teardown
-    @sfd.close if @sfd && ! @sfd.closed?
-    trap(:USR1, "DEFAULT")
-    trap(:USR2, "DEFAULT")
-  end
-
-  def test_rt_constants
-    assert [33,34].include?(SignalFD::RTMIN)
-    assert_equal 64, SignalFD::RTMAX
-  end
-
-  def test_new_with_flags
-    @sfd = SignalFD.new(%w(USR1), [:CLOEXEC,:NONBLOCK])
-    assert_instance_of SignalFD, @sfd
-  end if defined?(SignalFD::CLOEXEC) && defined?(SignalFD::NONBLOCK)
-
-  def test_new_with_sym_flag
-    @sfd = SignalFD.new(%w(USR1), :CLOEXEC)
-    assert_instance_of SignalFD, @sfd
-  end if defined?(SignalFD::CLOEXEC)
-
-  def test_take
-    @sfd = SignalFD.new(%w(USR1), 0)
-    pid = fork { sleep 0.01; Process.kill(:USR1, Process.ppid) }
-    siginfo = @sfd.take
-    assert_equal Signal.list["USR1"], siginfo.signo
-    assert_equal pid, siginfo.pid
-    assert Process.waitpid2(pid)[1].success?
-  end if RUBY_VERSION =~ %r{\A1\.9}
-
-  def test_take_nonblock
-    @sfd = SignalFD.new(%w(USR1), :NONBLOCK)
-    assert_nil @sfd.take(true)
-    assert_nil IO.select [ @sfd ], nil, nil, 0
-    pid = fork { sleep 0.01; Process.kill(:USR1, Process.ppid) }
-    siginfo = @sfd.take(true)
-    if siginfo
-      assert_equal Signal.list["USR1"], siginfo.signo
-      assert_equal pid, siginfo.pid
-    else
-      warn "WARNING: SignalFD#take(nonblock=true) broken"
-    end
-    assert Process.waitpid2(pid)[1].success?
-  end if RUBY_VERSION =~ %r{\A1\.9}
-
-  def test_update
-    assert_nothing_raised do
-      @sfd = SignalFD.new
-      @sfd.update! Signal.list["USR1"]
-      @sfd.update! [ "USR1", Signal.list["USR2"] ]
-      @sfd.update! [ "USR1", :USR2 ]
-      @sfd.update! [ Signal.list["USR1"], Signal.list["USR2"] ]
-    end
-  end
-
-  def test_with_sigqueue
-    sig = Signal.list["USR2"]
-    @sfd = SignalFD.new(:USR2)
-    libc = DL::Handle.new("libc.so.6")
-    if defined?(DL::Function)
-      sigqueue = libc["sigqueue"]
-      sigqueue = DL::CFunc.new(sigqueue, DL::TYPE_INT, "sigqueue")
-      args = [DL::TYPE_INT, DL::TYPE_INT,DL::TYPE_INT]
-      sigqueue = DL::Function.new(sigqueue, args, DL::TYPE_INT)
-    else
-      sigqueue = libc["sigqueue", "IIII"]
-    end
-    pid = fork { sleep 0.01; sigqueue.call(Process.ppid, sig, 666)  }
-    siginfo = @sfd.take
-    assert_equal sig, siginfo.signo
-    assert_equal pid, siginfo.pid
-    assert Process.waitpid2(pid)[1].success?
-    assert_equal 666, siginfo.ptr
-    assert_equal 666, siginfo.int
-  end if RUBY_VERSION =~ %r{\A1\.9}
-end if defined?(SleepyPenguin::SignalFD)
diff --git a/test/test_signalfd_siginfo.rb b/test/test_signalfd_siginfo.rb
deleted file mode 100644
index 0bd55f5..0000000
--- a/test/test_signalfd_siginfo.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require './test/helper'
-$-w = true
-require 'sleepy_penguin'
-
-class TestSignalFDSigInfo < Testcase
-  include SleepyPenguin
-
-  def test_members
-    members = SignalFD::SigInfo::MEMBERS
-    assert_equal 16, members.size
-    a = SignalFD::SigInfo.new
-    members.each { |k| assert_equal 0, a.__send__(k) }
-  end
-
-  def test_equality
-    a = SignalFD::SigInfo.new
-    b = SignalFD::SigInfo.new
-    assert_equal a, b
-
-    c = Class.new(SignalFD::SigInfo).new
-    assert_equal a, c
-    assert c != c.to_hash
-  end
-
-  def test_to_hash
-    hash = SignalFD::SigInfo.new.to_hash
-    assert_instance_of Hash, hash
-    members = SignalFD::SigInfo::MEMBERS
-    assert_equal members.size, hash.size
-    members.each { |k| assert_equal 0, hash[k] }
-  end
-end if defined?(SleepyPenguin::SignalFD)
-- 
1.8.4.483.g7fe67e6.dirty

[PATCH 2/2] tests: remove version-dependent FD_CLOEXEC checks

From:
Eric Wong
Date:
2013-10-19 @ 17:59
Not all versions/implementations of Ruby set FD_CLOEXEC by default.
And it is conceivable MRI will disable the current FD_CLOEXEC
default out of portability concerns, so we only test that our
code matches.
---
 test/helper.rb       | 9 +++++++++
 test/test_epoll.rb   | 6 +-----
 test/test_eventfd.rb | 6 +-----
 test/test_inotify.rb | 6 +-----
 test/test_timerfd.rb | 6 +-----
 5 files changed, 13 insertions(+), 20 deletions(-)

diff --git a/test/helper.rb b/test/helper.rb
index 13f79b7..8261168 100644
--- a/test/helper.rb
+++ b/test/helper.rb
@@ -6,3 +6,12 @@ Testcase = begin
 rescue NameError
   Minitest::Unit::TestCase # minitest 4
 end
+
+def check_cloexec(io)
+  pipe = IO.pipe
+  rbimp = Fcntl::FD_CLOEXEC & pipe[0].fcntl(Fcntl::F_GETFD)
+  ours = Fcntl::FD_CLOEXEC & io.fcntl(Fcntl::F_GETFD)
+  assert_equal rbimp, ours, "CLOEXEC default does not match Ruby implementation"
+ensure
+  pipe.each { |io| io.close }
+end
diff --git a/test/test_epoll.rb b/test/test_epoll.rb
index 88d0b6c..61b6e8c 100644
--- a/test/test_epoll.rb
+++ b/test/test_epoll.rb
@@ -348,11 +348,7 @@ class TestEpoll < Testcase
   def test_new
     @ep.close
     io = Epoll.new.to_io
-    if RUBY_VERSION.to_f >= 2.0
-      assert_equal 1, io.fcntl(Fcntl::F_GETFD)
-    else
-      assert_equal 0, io.fcntl(Fcntl::F_GETFD)
-    end
+    check_cloexec(io)
   end
 
   def test_delete
diff --git a/test/test_eventfd.rb b/test/test_eventfd.rb
index 731a6cb..a6b3016 100644
--- a/test/test_eventfd.rb
+++ b/test/test_eventfd.rb
@@ -20,11 +20,7 @@ class TestEventFD < Testcase
   def test_new
     efd = EventFD.new 0
     assert_kind_of(IO, efd)
-    if RUBY_VERSION.to_f >= 2.0
-      assert_equal 1, efd.fcntl(Fcntl::F_GETFD)
-    else
-      assert_equal 0, efd.fcntl(Fcntl::F_GETFD)
-    end
+    check_cloexec(efd)
   end
 
   def test_new_nonblock
diff --git a/test/test_inotify.rb b/test/test_inotify.rb
index c91d6e4..5cf5839 100644
--- a/test/test_inotify.rb
+++ b/test/test_inotify.rb
@@ -17,11 +17,7 @@ class TestInotify < Testcase
   def test_new
     @ino = Inotify.new
     assert_kind_of(IO, ino)
-    if RUBY_VERSION.to_f >= 2.0
-      assert_equal 1, ino.fcntl(Fcntl::F_GETFD)
-    else
-      assert_equal 0, ino.fcntl(Fcntl::F_GETFD)
-    end
+    check_cloexec(ino)
   end
 
   def test_constants
diff --git a/test/test_timerfd.rb b/test/test_timerfd.rb
index 23940a1..6189168 100644
--- a/test/test_timerfd.rb
+++ b/test/test_timerfd.rb
@@ -15,11 +15,7 @@ class TestTimerFD < Testcase
   def test_create
     tfd = TimerFD.new
     assert_kind_of(IO, tfd)
-    if RUBY_VERSION.to_f >= 2.0
-      assert_equal 1, tfd.fcntl(Fcntl::F_GETFD)
-    else
-      assert_equal 0, tfd.fcntl(Fcntl::F_GETFD)
-    end
+    check_cloexec(tfd)
   end
 
   def test_create_nonblock
-- 
1.8.4.483.g7fe67e6.dirty