everything related to duct tape audio suite (dtas)
 help / color / mirror / code / Atom feed
* [PATCH 0/5] use fiddle for Linux-specific APIs
@ 2019-12-01  1:26 Eric Wong
  2019-12-01  1:26 ` [PATCH 1/5] pipe: avoid loading sleepy_penguin Eric Wong
                   ` (5 more replies)
  0 siblings, 6 replies; 8+ messages in thread
From: Eric Wong @ 2019-12-01  1:26 UTC (permalink / raw)
  To: dtas-all

fiddle is distributed with Ruby since 1.9.2 (and the oldest we
support is 1.9.3).  So instead of expecting potential users to
install Ruby development headers and a C compiler, just use
fiddle to save potential users bandwidth and storage space.

We have to define some hard-coded constants, but all of these
constants are architecture-independent and we can trust the
Linux kernel to never break userspace.

Eric Wong (5):
  pipe: avoid loading sleepy_penguin
  use fiddle-based eventfd implementation
  buffer: replace sleepy_penguin with fiddle
  watchable: use fiddle for inotify support
  doc: remove "sleepy_penguin" references

 Documentation/dtas-sinkedit.pod               |  2 +-
 Documentation/dtas-sourceedit.pod             |  2 +-
 INSTALL                                       | 19 -----
 dtas-linux.gemspec                            | 19 -----
 lib/dtas.rb                                   |  8 ++
 lib/dtas/buffer.rb                            |  7 +-
 .../buffer/{splice.rb => fiddle_splice.rb}    | 75 ++++++++++++----
 lib/dtas/buffer/read_write.rb                 |  4 +-
 lib/dtas/pipe.rb                              | 20 +++--
 lib/dtas/sigevent.rb                          |  3 +-
 lib/dtas/sigevent/efd.rb                      | 20 -----
 lib/dtas/sigevent/fiddle_efd.rb               | 38 +++++++++
 lib/dtas/sigevent/pipe.rb                     |  2 +-
 lib/dtas/watchable.rb                         | 85 +++++++++++++++++--
 test/test_buffer.rb                           | 10 +--
 test/test_sigevent.rb                         | 20 +++++
 test/test_sink_pipe_size.rb                   | 27 +++---
 17 files changed, 240 insertions(+), 121 deletions(-)
 delete mode 100644 dtas-linux.gemspec
 rename lib/dtas/buffer/{splice.rb => fiddle_splice.rb} (68%)
 delete mode 100644 lib/dtas/sigevent/efd.rb
 create mode 100644 lib/dtas/sigevent/fiddle_efd.rb
 create mode 100644 test/test_sigevent.rb



^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/5] pipe: avoid loading sleepy_penguin
  2019-12-01  1:26 [PATCH 0/5] use fiddle for Linux-specific APIs Eric Wong
@ 2019-12-01  1:26 ` Eric Wong
  2019-12-01  1:26 ` [PATCH 2/5] use fiddle-based eventfd implementation Eric Wong
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Eric Wong @ 2019-12-01  1:26 UTC (permalink / raw)
  To: dtas-all

The values of F_{GET,SET}PIPE_SZ are architecture-independent
and stable in Linux (unlike Ruby :P), so we won't need to bother
loading an extra .so here for two constants.
---
 lib/dtas/pipe.rb            | 20 ++++++++++++--------
 test/test_sink_pipe_size.rb | 27 ++++++++++++---------------
 2 files changed, 24 insertions(+), 23 deletions(-)

diff --git a/lib/dtas/pipe.rb b/lib/dtas/pipe.rb
index 58d926c..4c3203d 100644
--- a/lib/dtas/pipe.rb
+++ b/lib/dtas/pipe.rb
@@ -1,10 +1,6 @@
 # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
 # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
 # frozen_string_literal: true
-begin
-  require 'sleepy_penguin'
-rescue LoadError
-end
 require_relative '../dtas'
 require_relative 'writable_iter'
 require_relative 'nonblock'
@@ -14,6 +10,11 @@ class DTAS::Pipe < DTAS::Nonblock # :nodoc:
   include DTAS::WritableIter
   attr_accessor :sink
 
+  if RUBY_PLATFORM =~ /linux/i && File.readable?('/proc/sys/fs/pipe-max-size')
+    F_SETPIPE_SZ = 1031
+    F_GETPIPE_SZ = 1032
+  end
+
   def self.new
     _, w = rv = pipe
     w.writable_iter_init
@@ -21,13 +22,16 @@ def self.new
   end
 
   def pipe_size=(nr)
-    defined?(SleepyPenguin::F_SETPIPE_SZ) and
-      fcntl(SleepyPenguin::F_SETPIPE_SZ, nr)
+    fcntl(F_SETPIPE_SZ, nr) if defined?(F_SETPIPE_SZ)
+  rescue Errno::EINVAL # old kernel
+  rescue Errno::EPERM
+    # resizes fail if Linux is close to the pipe limit for the user
+    # or if the user does not have permissions to resize
   end
 
   def pipe_size
-    fcntl(SleepyPenguin::F_GETPIPE_SZ)
-  end if defined?(SleepyPenguin::F_GETPIPE_SZ)
+    fcntl(F_GETPIPE_SZ)
+  end if defined?(F_GETPIPE_SZ)
 
   # avoid syscall, we never change IO#nonblock= directly
   def nonblock?
diff --git a/test/test_sink_pipe_size.rb b/test/test_sink_pipe_size.rb
index 1b6db72..bbe2884 100644
--- a/test/test_sink_pipe_size.rb
+++ b/test/test_sink_pipe_size.rb
@@ -1,20 +1,17 @@
 # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
 # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
 # frozen_string_literal: true
-begin
-  require 'sleepy_penguin'
-  require './test/player_integration'
-  class TestSinkPipeSizeIntegration < Testcase
-    include PlayerIntegration
+require './test/player_integration'
+class TestSinkPipeSizeIntegration < Testcase
+  include PlayerIntegration
 
-    def test_sink_pipe_size_integration
-      s = client_socket
-      default_sink_pid(s)
-      s.req_ok("sink ed default pipe_size=0x1000")
-      s.req_ok("sink ed default pipe_size=0x10000")
-      s.req_ok("sink ed default pipe_size=")
-      s.req_ok("sink ed default pipe_size=4096")
-    end if SleepyPenguin.const_defined?(:F_SETPIPE_SZ)
+  def test_sink_pipe_size_integration
+    s = client_socket
+    default_sink_pid(s)
+    s.req_ok("sink ed default pipe_size=0x1000")
+    s.req_ok("sink ed default pipe_size=0x10000")
+    s.req_ok("sink ed default pipe_size=")
+    s.req_ok("sink ed default pipe_size=4096")
   end
-rescue LoadError
-end
+end if RUBY_PLATFORM =~ /linux/i &&
+      File.readable?('/proc/sys/fs/pipe-max-size')


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/5] use fiddle-based eventfd implementation
  2019-12-01  1:26 [PATCH 0/5] use fiddle for Linux-specific APIs Eric Wong
  2019-12-01  1:26 ` [PATCH 1/5] pipe: avoid loading sleepy_penguin Eric Wong
@ 2019-12-01  1:26 ` Eric Wong
  2019-12-01  1:26 ` [PATCH 3/5] buffer: replace sleepy_penguin with fiddle Eric Wong
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Eric Wong @ 2019-12-01  1:26 UTC (permalink / raw)
  To: dtas-all

sleepy_penguin requires a compiler and development headers to
install, so it could be a PITA to install for users on
distro-provided Ruby.  Use 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            |  3 +--
 lib/dtas/sigevent/efd.rb        | 20 -----------------
 lib/dtas/sigevent/fiddle_efd.rb | 38 +++++++++++++++++++++++++++++++++
 lib/dtas/sigevent/pipe.rb       |  2 +-
 test/test_sigevent.rb           | 20 +++++++++++++++++
 6 files changed, 68 insertions(+), 23 deletions(-)
 delete mode 100644 lib/dtas/sigevent/efd.rb
 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 @@ def self.null # :nodoc:
     @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..81b8160 100644
--- a/lib/dtas/sigevent.rb
+++ b/lib/dtas/sigevent.rb
@@ -3,8 +3,7 @@
 # frozen_string_literal: true
 begin
   raise LoadError, "no eventfd with _DTAS_POSIX" if ENV["_DTAS_POSIX"]
-  require 'sleepy_penguin'
-  require_relative 'sigevent/efd'
+  require_relative 'sigevent/fiddle_efd'
 rescue LoadError
   require_relative 'sigevent/pipe'
 end
diff --git a/lib/dtas/sigevent/efd.rb b/lib/dtas/sigevent/efd.rb
deleted file mode 100644
index 4be2c84..0000000
--- a/lib/dtas/sigevent/efd.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
-# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
-
-# used in various places for safe wakeups from IO.select via signals
-# This requires a modern Linux system and the "sleepy_penguin" RubyGem
-class DTAS::Sigevent < SleepyPenguin::EventFD # :nodoc:
-  def self.new
-    super(0, :CLOEXEC)
-  end
-
-  def signal
-    incr(1)
-  end
-
-  def readable_iter
-    value(true)
-    yield self, nil # calls DTAS::Process.reaper
-    :wait_readable
-  end
-end
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 <dtas-all@nongnu.org>
+# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
+# 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 <dtas-all@nongnu.org>
+# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
+# 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


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/5] buffer: replace sleepy_penguin with fiddle
  2019-12-01  1:26 [PATCH 0/5] use fiddle for Linux-specific APIs Eric Wong
  2019-12-01  1:26 ` [PATCH 1/5] pipe: avoid loading sleepy_penguin Eric Wong
  2019-12-01  1:26 ` [PATCH 2/5] use fiddle-based eventfd implementation Eric Wong
@ 2019-12-01  1:26 ` Eric Wong
  2019-12-01  1:26 ` [PATCH 4/5] watchable: use fiddle for inotify support Eric Wong
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Eric Wong @ 2019-12-01  1:26 UTC (permalink / raw)
  To: dtas-all

Fiddle exists on all Ruby 1.9.2+ installations and seems
alright.  Since splice is a Linux-only API, we don't need to
worry about the values of constants changing (and they're
architecture-independent).
---
 lib/dtas/buffer.rb                            |  7 +-
 .../buffer/{splice.rb => fiddle_splice.rb}    | 75 +++++++++++++++----
 lib/dtas/buffer/read_write.rb                 |  4 +-
 test/test_buffer.rb                           | 10 +--
 4 files changed, 69 insertions(+), 27 deletions(-)
 rename lib/dtas/buffer/{splice.rb => fiddle_splice.rb} (68%)

diff --git a/lib/dtas/buffer.rb b/lib/dtas/buffer.rb
index 39070d7..2bc44ba 100644
--- a/lib/dtas/buffer.rb
+++ b/lib/dtas/buffer.rb
@@ -8,11 +8,8 @@
 class DTAS::Buffer # :nodoc:
   begin
     raise LoadError, "no splice with _DTAS_POSIX" if ENV["_DTAS_POSIX"]
-    require 'sleepy_penguin' # splice is only in Linux for now...
-    SleepyPenguin.respond_to?(:splice) or
-      raise LoadError, 'sleepy_penguin 3.5+ required for splice', []
-    require_relative 'buffer/splice'
-    include DTAS::Buffer::Splice
+    require_relative 'buffer/fiddle_splice' # splice is only in Linux for now...
+    include DTAS::Buffer::FiddleSplice
   rescue LoadError
     require_relative 'buffer/read_write'
     include DTAS::Buffer::ReadWrite
diff --git a/lib/dtas/buffer/splice.rb b/lib/dtas/buffer/fiddle_splice.rb
similarity index 68%
rename from lib/dtas/buffer/splice.rb
rename to lib/dtas/buffer/fiddle_splice.rb
index 2e86d0a..543b3e0 100644
--- a/lib/dtas/buffer/splice.rb
+++ b/lib/dtas/buffer/fiddle_splice.rb
@@ -2,16 +2,64 @@
 # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
 # frozen_string_literal: true
 require 'io/nonblock'
-require 'sleepy_penguin'
+require 'fiddle' # require_relative caller should expect LoadError
 require_relative '../../dtas'
 require_relative '../pipe'
 
-# Used by -player on Linux systems with the "sleepy_penguin" RubyGem installed
-module DTAS::Buffer::Splice # :nodoc:
+# Used by -player on Linux systems with the "splice" syscall
+module DTAS::Buffer::FiddleSplice # :nodoc:
   MAX_AT_ONCE = 4096 # page size in Linux
   MAX_AT_ONCE_1 = 65536
-  F_MOVE = SleepyPenguin::F_MOVE
-  F_NONBLOCK = SleepyPenguin::F_NONBLOCK
+  F_MOVE = 1
+  F_NONBLOCK = 2
+
+  Splice = Fiddle::Function.new(DTAS.libc['splice'], [
+      Fiddle::TYPE_INT, # int fd_in,
+      Fiddle::TYPE_VOIDP, # loff_t *off_in
+      Fiddle::TYPE_INT, # int fd_out
+      Fiddle::TYPE_VOIDP, # loff_t *off_out
+      Fiddle::TYPE_SIZE_T, # size_t len
+      Fiddle::TYPE_INT, # unsigned int flags
+    ],
+    Fiddle::TYPE_SSIZE_T) # ssize_t
+
+  Tee = Fiddle::Function.new(DTAS.libc['tee'], [
+      Fiddle::TYPE_INT, # int fd_in,
+      Fiddle::TYPE_INT, # int fd_out
+      Fiddle::TYPE_SIZE_T, # size_t len
+      Fiddle::TYPE_INT, # unsigned int flags
+    ],
+    Fiddle::TYPE_SSIZE_T) # ssize_t
+
+  def _syserr(s, func)
+    raise "BUG: we should not encounter EOF on #{func}" if s == 0
+    case errno = Fiddle.last_error
+    when Errno::EAGAIN::Errno
+      return :EAGAIN
+    when Errno::EPIPE::Errno
+      raise Errno::EPIPE.exception
+    when Errno::EINTR::Errno
+      return nil
+    else
+      raise SystemCallError, "#{func} error: #{errno}"
+    end
+  end
+
+  def splice(src, dst, len, flags)
+    begin
+      s = Splice.call(src.fileno, nil, dst.fileno, nil, len, flags)
+      return s if s > 0
+      sym = _syserr(s, 'splice') and return sym
+    end while true
+  end
+
+  def tee(src, dst, len, flags = 0)
+    begin
+      s = Tee.call(src.fileno, dst.fileno, len, flags)
+      return s if s > 0
+      sym = _syserr(s, 'tee') and return sym
+    end while true
+  end
 
   def buffer_size
     @to_io.pipe_size
@@ -25,14 +73,13 @@ def buffer_size=(bytes)
 
   # be sure to only call this with nil when all writers to @wr are done
   def discard(bytes)
-    SleepyPenguin.splice(@to_io, DTAS.null, bytes)
+    splice(@to_io, DTAS.null, bytes, 0)
   end
 
   def broadcast_one(targets, limit = nil)
     # single output is always non-blocking
     limit ||= MAX_AT_ONCE_1
-    s = SleepyPenguin.splice(@to_io, targets[0], limit, F_MOVE|F_NONBLOCK,
-                             exception: false)
+    s = splice(@to_io, targets[0], limit, F_MOVE|F_NONBLOCK)
     if Symbol === s
       targets # our one and only target blocked on write
     else
@@ -48,7 +95,7 @@ def broadcast_one(targets, limit = nil)
   def __tee_in_full(src, dst, bytes)
     rv = 0
     while bytes > 0
-      s = SleepyPenguin.tee(src, dst, bytes)
+      s = tee(src, dst, bytes)
       bytes -= s
       rv += s
     end
@@ -58,7 +105,7 @@ def __tee_in_full(src, dst, bytes)
   def __splice_in_full(src, dst, bytes, flags)
     rv = 0
     while bytes > 0
-      s = SleepyPenguin.splice(src, dst, bytes, flags)
+      s = splice(src, dst, bytes, flags)
       rv += s
       bytes -= s
     end
@@ -71,9 +118,8 @@ def __broadcast_tee(blocked, targets, chunk_size)
     targets.delete_if do |dst|
       begin
         t = (dst.nonblock? || most_teed == 0) ?
-            SleepyPenguin.tee(@to_io, dst, chunk_size, F_NONBLOCK,
-                              exception: false) :
-            __tee_in_full(@to_io, dst, chunk_size)
+              tee(@to_io, dst, chunk_size, F_NONBLOCK) :
+              __tee_in_full(@to_io, dst, chunk_size)
         if Integer === t
           if t > most_teed
             chunk_size = t if most_teed == 0
@@ -120,8 +166,7 @@ def broadcast_inf(targets, limit = nil)
     begin
       targets << last
       if last.nonblock? || most_teed == 0
-        s = SleepyPenguin.splice(@to_io, last, bytes, F_MOVE|F_NONBLOCK,
-                                 exception: false)
+        s = splice(@to_io, last, bytes, F_MOVE|F_NONBLOCK)
         if Symbol === s
           blocked << last
 
diff --git a/lib/dtas/buffer/read_write.rb b/lib/dtas/buffer/read_write.rb
index 04856c7..e2001b6 100644
--- a/lib/dtas/buffer/read_write.rb
+++ b/lib/dtas/buffer/read_write.rb
@@ -6,8 +6,8 @@
 require_relative '../pipe'
 require_relative '../nonblock'
 
-# compatibility code for systems lacking "splice" support via the
-# "sleepy_penguin" 3.5+ RubyGem.  Used only by -player
+# compatibility code for non-Linux systems lacking "splice" support.
+# Used only by -player
 module DTAS::Buffer::ReadWrite # :nodoc:
   MAX_AT_ONCE = 512 # min PIPE_BUF value in POSIX
   attr_accessor :buffer_size
diff --git a/test/test_buffer.rb b/test/test_buffer.rb
index 8f5d8b5..1773ca3 100644
--- a/test/test_buffer.rb
+++ b/test/test_buffer.rb
@@ -49,14 +49,14 @@ def test_set_buffer_size
     buf = new_buffer
     buf.buffer_size = @@max_size
     assert_equal @@max_size, buf.buffer_size
-  end if defined?(SleepyPenguin::F_GETPIPE_SZ)
+  end if defined?(DTAS::Pipe::F_GETPIPE_SZ)
 
   def test_buffer_size
     buf = new_buffer
     assert_operator buf.buffer_size, :>, 128
     buf.buffer_size = @@max_size
     assert_equal @@max_size, buf.buffer_size
-  end if defined?(SleepyPenguin::F_GETPIPE_SZ)
+  end if defined?(DTAS::Pipe::F_GETPIPE_SZ)
 
   def test_broadcast_1
     buf = new_buffer
@@ -108,7 +108,7 @@ def test_broadcast
     assert_equal "HELLO", a[0].read(5)
     assert_equal "HELLO", b[0].read(5)
 
-    return unless defined?(SleepyPenguin::F_GETPIPE_SZ)
+    return unless defined?(DTAS::Pipe::F_GETPIPE_SZ)
 
     b[1].nonblock = true
     b[1].write('*' * pipe_size(b[1]))
@@ -167,7 +167,7 @@ def test_broadcast_all_full
     buf.wr.write "HELLO"
     assert_equal tmp, buf.broadcast(tmp)
     assert_equal [a[1], b[1]], tmp
-  end if defined?(SleepyPenguin::F_GETPIPE_SZ)
+  end if defined?(DTAS::Pipe::F_GETPIPE_SZ)
 
   def test_serialize
     buf = new_buffer
@@ -206,6 +206,6 @@ def test_load_size
   end
 
   def pipe_size(io)
-    io.fcntl(SleepyPenguin::F_GETPIPE_SZ)
+    io.fcntl(DTAS::Pipe::F_GETPIPE_SZ)
   end
 end


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 4/5] watchable: use fiddle for inotify support
  2019-12-01  1:26 [PATCH 0/5] use fiddle for Linux-specific APIs Eric Wong
                   ` (2 preceding siblings ...)
  2019-12-01  1:26 ` [PATCH 3/5] buffer: replace sleepy_penguin with fiddle Eric Wong
@ 2019-12-01  1:26 ` Eric Wong
  2019-12-01  1:26 ` [PATCH 5/5] doc: remove "sleepy_penguin" references Eric Wong
  2019-12-13 18:16 ` [PATCH 0/5] use fiddle for Linux-specific APIs Eric Wong
  5 siblings, 0 replies; 8+ messages in thread
From: Eric Wong @ 2019-12-01  1:26 UTC (permalink / raw)
  To: dtas-all

We have String#unpack at our disposal for working with "struct
inotify_event", so use it instead of depending on an extension
which requires a compiler and development headers to install.
---
 lib/dtas/watchable.rb | 85 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 77 insertions(+), 8 deletions(-)

diff --git a/lib/dtas/watchable.rb b/lib/dtas/watchable.rb
index d0f37af..4047a42 100644
--- a/lib/dtas/watchable.rb
+++ b/lib/dtas/watchable.rb
@@ -1,22 +1,80 @@
 # Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
 # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
 # frozen_string_literal: true
+require_relative '../dtas'
+require_relative 'nonblock'
+
 begin
-require 'sleepy_penguin'
+require 'fiddle'
 
 # used to restart DTAS::Source::SplitFX processing in dtas-player
 # if the YAML file is edited
 module DTAS::Watchable # :nodoc:
-  class InotifyReadableIter < SleepyPenguin::Inotify # :nodoc:
-    def self.new
-      super(:CLOEXEC)
+  class InotifyReadableIter # :nodoc:
+
+    Inotify_init = Fiddle::Function.new(DTAS.libc['inotify_init1'],
+      [ Fiddle::TYPE_INT ],
+      Fiddle::TYPE_INT)
+
+    Inotify_add_watch = Fiddle::Function.new(DTAS.libc['inotify_add_watch'],
+      [ Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT ],
+      Fiddle::TYPE_INT)
+
+    # IO.select compatibility
+    attr_reader :to_io  #:nodoc:
+
+    def initialize # :nodoc:
+      fd = Inotify_init.call(02000000 | 04000) # CLOEXEC | NONBLOCK
+      raise "inotify_init failed: #{Fiddle.last_error}" if fd < 0
+      @to_io = DTAS::Nonblock.for_fd(fd)
+      @buf = ''.b
+      @q = []
     end
 
-    FLAGS = CLOSE_WRITE | MOVED_TO
+    # struct inotify_event {
+    #     int      wd;       /* Watch descriptor */
+    #     uint32_t mask;     /* Mask describing event */
+    #     uint32_t cookie;   /* Unique cookie associating related
+    #                           events (for rename(2)) */
+    #     uint32_t len;      /* Size of name field */
+    #     char     name[];   /* Optional null-terminated name */
+    InotifyEvent = Struct.new(:wd, :mask, :cookie, :len, :name) # :nodoc:
+
+    def take # :nodoc:
+      event = @q.pop and return event
+      case rv = @to_io.read_nonblock(16384, @buf, exception: false)
+      when :wait_readable, nil
+        return
+      else
+        until rv.empty?
+          hdr = rv.slice!(0,16)
+          name = nil
+          wd, mask, cookie, len = res = hdr.unpack('iIII')
+          wd && mask && cookie && len or
+            raise "bogus inotify_event #{res.inspect} hdr=#{hdr.inspect}"
+          if len > 0
+            name = rv.slice!(0, len)
+            name.size == len or raise "short name #{name.inspect} != #{len}"
+            name.sub!(/\0+\z/, '') or
+              raise "missing: `\\0', inotify_event.name=#{name.inspect}"
+            name = DTAS.dedupe_str(name)
+          end
+          ie = InotifyEvent.new(wd, mask, cookie, len, name)
+          if event
+            @q << ie
+          else
+            event = ie
+          end
+        end # /until rv.empty?
+        return event
+      end while true
+    end
+
+    FLAGS = 8 | 128 # CLOSE_WRITE | MOVED_TO
 
     def readable_iter
       or_call = false
-      while event = take(true) # drain the buffer
+      while event = take # drain the buffer
         w = @watches[event.wd] or next
         if (event.mask & FLAGS) != 0 && w[event.name]
           or_call = true
@@ -30,6 +88,16 @@ def readable_iter
       end
     end
 
+    def add_watch(watchdir)
+      wd = Inotify_add_watch.call(@to_io.fileno, watchdir, FLAGS)
+      raise "inotify_add_watch failed: #{Fiddle.last_error}" if wd < 0
+      wd
+    end
+
+    def close
+      @to_io = @to_io.close if @to_io
+    end
+
     # we must watch the directory, since
     def watch_files(paths, blk)
       @watches = {} # wd -> { basename -> true }
@@ -38,7 +106,7 @@ def watch_files(paths, blk)
       Array(paths).each do |path|
         watchdir, watchbase = File.split(File.expand_path(path))
         begin
-          wd = @dir2wd[watchdir] ||= add_watch(watchdir, FLAGS)
+          wd = @dir2wd[watchdir] ||= add_watch(watchdir)
           m = @watches[wd] ||= {}
           m[watchbase] = true
         rescue SystemCallError => e
@@ -67,5 +135,6 @@ def watch_end(srv)
   end
 end
 
-rescue LoadError
+rescue LoadError, Fiddle::DLError => e
+  warn "#{e.message} (#{e.class})"
 end


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 5/5] doc: remove "sleepy_penguin" references
  2019-12-01  1:26 [PATCH 0/5] use fiddle for Linux-specific APIs Eric Wong
                   ` (3 preceding siblings ...)
  2019-12-01  1:26 ` [PATCH 4/5] watchable: use fiddle for inotify support Eric Wong
@ 2019-12-01  1:26 ` Eric Wong
  2019-12-13 18:16 ` [PATCH 0/5] use fiddle for Linux-specific APIs Eric Wong
  5 siblings, 0 replies; 8+ messages in thread
From: Eric Wong @ 2019-12-01  1:26 UTC (permalink / raw)
  To: dtas-all

Less to install, less to document, a win for everybody!
---
 Documentation/dtas-sinkedit.pod   |  2 +-
 Documentation/dtas-sourceedit.pod |  2 +-
 INSTALL                           | 19 -------------------
 dtas-linux.gemspec                | 19 -------------------
 4 files changed, 2 insertions(+), 40 deletions(-)
 delete mode 100644 dtas-linux.gemspec

diff --git a/Documentation/dtas-sinkedit.pod b/Documentation/dtas-sinkedit.pod
index ee2d5b7..6787e25 100644
--- a/Documentation/dtas-sinkedit.pod
+++ b/Documentation/dtas-sinkedit.pod
@@ -13,7 +13,7 @@ dtas-sinkedit SINKNAME
 dtas-sinkedit spawns an editor to allow editing of a sink as a YAML file.
 See L<dtas-player_protocol(7)> for details on SINKARGS.
 
-On Linux machines with the sleepy_penguin RubyGem installed, L<inotify(7)>
+On Linux machines with the "fiddle" default RubyGem installed, L<inotify(7)>
 is used to monitor the file for changes while the text editor is running.
 Each time a user finishes saving a file, changes are committed immediately.
 This behavior may be disabled by using the -N or --no-watch command-line
diff --git a/Documentation/dtas-sourceedit.pod b/Documentation/dtas-sourceedit.pod
index ee88e8f..be158d3 100644
--- a/Documentation/dtas-sourceedit.pod
+++ b/Documentation/dtas-sourceedit.pod
@@ -16,7 +16,7 @@ a pipe or file, it is parsed as YAML and fed to the L<dtas-player(1)> instance
 non-interactively.  This is useful for loading various profiles from the
 filesystem.
 
-On Linux machines with the sleepy_penguin RubyGem installed, L<inotify(7)>
+On Linux machines with the "fiddle" default RubyGem installed, L<inotify(7)>
 is used to monitor the file for changes while the text editor is running.
 Each time a user finishes saving a file, changes are committed immediately.
 This behavior may be disabled by using the -N or --no-watch command-line
diff --git a/INSTALL b/INSTALL
index d0b8a24..16d3d34 100644
--- a/INSTALL
+++ b/INSTALL
@@ -16,21 +16,6 @@ Debian 7+ users can install dependencies easily:
 
     sudo apt-get install sox libsox-fmt-all mp3gain flac ruby-dev
 
-# installing dtas RubyGem on GNU/Linux (Linux kernel 2.6.32+)
-
-Be sure to have Ruby development headers and a working C compiler.
-This will pull in the sleepy_penguin RubyGems for minor
-speedups.  If you cannot be bothered to have a development
-environment, just use "gem install dtas".
-
-    sudo gem install dtas-linux
-
-This should pull in the "sleepy_penguin" RubyGems
-
-For future upgrades of dtas (upgrades to dtas-linux will be infrequent)
-
-    sudo gem update dtas
-
 # installing the dtas RubyGem on non-GNU/Linux or old GNU/Linux systems
 
     sudo gem install dtas
@@ -45,10 +30,6 @@ Grab the latest tarball from our HTTPS site:
     $ cd dtas-0.17.0
     $ sudo ruby setup.rb
 
-GNU/Linux users may optionally install the "sleepy_penguin" package:
-
-    * sleepy_penguin - https://bogomips.org/sleepy_penguin/
-
 # CONTACT
 
 Please do not hesitate to send plain-text mail to <dtas-all@nongnu.org>
diff --git a/dtas-linux.gemspec b/dtas-linux.gemspec
deleted file mode 100644
index cc9a8c8..0000000
--- a/dtas-linux.gemspec
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright (C) 2013-2019 all contributors <dtas-all@nongnu.org>
-# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
-#
-# this just declares dependencies to make gem installation a little easier
-# for Linux users
-Gem::Specification.new do |s|
-  s.name = %q{dtas-linux}
-  s.version = '1.1.0'
-  s.authors = ["dtas hackers"]
-  s.summary = "meta-package for dtas users on the Linux kernel"
-  s.description = "gives small performance improvements for dtas users\n" \
-                  "via tee(), splice() and eventfd() on Linux"
-  s.email = %q{e@80x24.org}
-  s.files = []
-  s.homepage = 'https://80x24.org/dtas.git/about/'
-  s.add_dependency(%q<dtas>, '~> 0.16')
-  s.add_dependency(%q<sleepy_penguin>, '~> 3.5')
-  s.licenses = 'GPL-3.0+'
-end


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 0/5] use fiddle for Linux-specific APIs
  2019-12-01  1:26 [PATCH 0/5] use fiddle for Linux-specific APIs Eric Wong
                   ` (4 preceding siblings ...)
  2019-12-01  1:26 ` [PATCH 5/5] doc: remove "sleepy_penguin" references Eric Wong
@ 2019-12-13 18:16 ` Eric Wong
  2019-12-15 20:39   ` fiddle vs sleepy_penguin vs Perl syscall() bench scripts Eric Wong
  5 siblings, 1 reply; 8+ messages in thread
From: Eric Wong @ 2019-12-13 18:16 UTC (permalink / raw)
  To: dtas-all

I'll need to rework this series to maintain sleepy_penguin
support and only use fiddle as a fallback.  libffi and fiddle
use more CPU, which is noticeable on an ancient laptop...


^ permalink raw reply	[flat|nested] 8+ messages in thread

* fiddle vs sleepy_penguin vs Perl syscall() bench scripts
  2019-12-13 18:16 ` [PATCH 0/5] use fiddle for Linux-specific APIs Eric Wong
@ 2019-12-15 20:39   ` Eric Wong
  0 siblings, 0 replies; 8+ messages in thread
From: Eric Wong @ 2019-12-15 20:39 UTC (permalink / raw)
  To: dtas-all

[-- Attachment #1: Type: text/plain, Size: 254 bytes --]

Eric Wong <e@80x24.org> wrote:
> I'll need to rework this series to maintain sleepy_penguin
> support and only use fiddle as a fallback.  libffi and fiddle
> use more CPU, which is noticeable on an ancient laptop...

scripts used for testing attached :>

[-- Attachment #2: splice_bench.pl --]
[-- Type: text/x-perl, Size: 258 bytes --]

#!/usr/bin/perl -w
use strict;
use Benchmark qw(:all :hireswallclock);
BEGIN { require 'sys/syscall.ph' };
my $s;
my $t = timeit(1, sub {
	do {
		$s = syscall(SYS_splice, 0, undef, 1, undef, 4096, 0);
	} while $s;
});
print STDERR "OK: ", timestr($t), "\n";

[-- Attachment #3: splice_bench.rb --]
[-- Type: application/x-ruby, Size: 884 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2019-12-15 20:39 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-01  1:26 [PATCH 0/5] use fiddle for Linux-specific APIs Eric Wong
2019-12-01  1:26 ` [PATCH 1/5] pipe: avoid loading sleepy_penguin Eric Wong
2019-12-01  1:26 ` [PATCH 2/5] use fiddle-based eventfd implementation Eric Wong
2019-12-01  1:26 ` [PATCH 3/5] buffer: replace sleepy_penguin with fiddle Eric Wong
2019-12-01  1:26 ` [PATCH 4/5] watchable: use fiddle for inotify support Eric Wong
2019-12-01  1:26 ` [PATCH 5/5] doc: remove "sleepy_penguin" references Eric Wong
2019-12-13 18:16 ` [PATCH 0/5] use fiddle for Linux-specific APIs Eric Wong
2019-12-15 20:39   ` fiddle vs sleepy_penguin vs Perl syscall() bench scripts Eric Wong

Code repositories for project(s) associated with this public inbox

	https://80x24.org/dtas.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).