everything related to duct tape audio suite (dtas)
 help / color / mirror / code / Atom feed
From: Eric Wong <e@80x24.org>
To: dtas-all@nongnu.org
Subject: [PATCH 4/5] watchable: use fiddle for inotify support
Date: Sun,  1 Dec 2019 01:26:52 +0000	[thread overview]
Message-ID: <20191201012653.21967-5-e@80x24.org> (raw)
In-Reply-To: <20191201012653.21967-1-e@80x24.org>

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


  parent reply	other threads:[~2019-12-01  1:27 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 ` Eric Wong [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://80x24.org/dtas/README

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191201012653.21967-5-e@80x24.org \
    --to=e@80x24.org \
    --cc=dtas-all@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).