From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS22989 209.51.188.0/24 X-Spam-Status: No, score=-3.9 required=3.0 tests=AWL,BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, SPF_HELO_NONE,SPF_PASS shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dcvr.yhbt.net (Postfix) with ESMTPS id 551D31F463 for ; Fri, 20 Dec 2019 01:39:47 +0000 (UTC) Received: from localhost ([::1]:49628 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ii7Gc-0002pJ-HM for e@80x24.org; Thu, 19 Dec 2019 20:39:46 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:38013) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1ii7GZ-0002m9-85 for dtas-all@nongnu.org; Thu, 19 Dec 2019 20:39:44 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ii7GX-0002Fi-Is for dtas-all@nongnu.org; Thu, 19 Dec 2019 20:39:43 -0500 Received: from dcvr.yhbt.net ([64.71.152.64]:49716) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ii7GX-0002AW-2A for dtas-all@nongnu.org; Thu, 19 Dec 2019 20:39:41 -0500 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 15D741F47A for ; Fri, 20 Dec 2019 01:39:19 +0000 (UTC) From: Eric Wong To: dtas-all@nongnu.org Subject: [PATCH v2 4/5] watchable: use fiddle for inotify support Date: Fri, 20 Dec 2019 01:39:16 +0000 Message-Id: <20191220013917.17212-5-e@80x24.org> In-Reply-To: <20191220013917.17212-1-e@80x24.org> References: <20191220013917.17212-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 64.71.152.64 X-BeenThere: dtas-all@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: duct tape audio suite List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dtas-all-bounces+e=80x24.org@nongnu.org Sender: "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 | 115 ++++++++++++++++--------------- lib/dtas/watchable/fiddle_ino.rb | 78 +++++++++++++++++++++ lib/dtas/watchable/inotify.rb | 13 ++++ 3 files changed, 149 insertions(+), 57 deletions(-) create mode 100644 lib/dtas/watchable/fiddle_ino.rb create mode 100644 lib/dtas/watchable/inotify.rb diff --git a/lib/dtas/watchable.rb b/lib/dtas/watchable.rb index d0f37af..2502b7c 100644 --- a/lib/dtas/watchable.rb +++ b/lib/dtas/watchable.rb @@ -1,71 +1,72 @@ # Copyright (C) 2013-2019 all contributors # License: GPL-3.0+ # frozen_string_literal: true +require_relative '../dtas' +require_relative 'nonblock' begin -require 'sleepy_penguin' + module DTAS::Watchable # :nodoc: + module InotifyCommon # :nodoc: + FLAGS =3D 8 | 128 # IN_CLOSE_WRITE | IN_MOVED_TO =20 -# 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) - end - - FLAGS =3D CLOSE_WRITE | MOVED_TO - - def readable_iter - or_call =3D false - while event =3D take(true) # drain the buffer - w =3D @watches[event.wd] or next - if (event.mask & FLAGS) !=3D 0 && w[event.name] - or_call =3D true + def readable_iter + or_call =3D false + while event =3D take(true) # drain the buffer + w =3D @watches[event.wd] or next + if (event.mask & FLAGS) !=3D 0 && w[event.name] + or_call =3D true + end + end + if or_call + @on_readable.call + :delete + else + :wait_readable end end - if or_call - @on_readable.call - :delete - else - :wait_readable - end - end =20 - # we must watch the directory, since - def watch_files(paths, blk) - @watches =3D {} # wd -> { basename -> true } - @on_readable =3D blk - @dir2wd =3D {} - Array(paths).each do |path| - watchdir, watchbase =3D File.split(File.expand_path(path)) - begin - wd =3D @dir2wd[watchdir] ||=3D add_watch(watchdir, FLAGS) - m =3D @watches[wd] ||=3D {} - m[watchbase] =3D true - rescue SystemCallError =3D> e - warn "#{watchdir.dump}: #{e.message} (#{e.class})" + # we must watch the directory, since + def watch_files(paths, blk) + @watches =3D {} # wd -> { basename -> true } + @on_readable =3D blk + @dir2wd =3D {} + Array(paths).each do |path| + watchdir, watchbase =3D File.split(File.expand_path(path)) + begin + wd =3D @dir2wd[watchdir] ||=3D add_watch(watchdir, FLAGS) + m =3D @watches[wd] ||=3D {} + m[watchbase] =3D true + rescue SystemCallError =3D> e + warn "#{watchdir.dump}: #{e.message} (#{e.class})" + end end end - end - end + end # module InotifyCommon =20 - def watch_begin(blk) - @ino =3D InotifyReadableIter.new - @ino.watch_files(@watch_extra << @infile, blk) - @ino - end + begin + require_relative 'watchable/inotify' + rescue LoadError + # TODO: support kevent + require_relative 'watchable/fiddle_ino' + end =20 - def watch_extra(paths) - @ino.watch_extra(paths) - end + def watch_begin(blk) + @ino =3D DTAS::Watchable::InotifyReadableIter.new + @ino.watch_files(@watch_extra << @infile, blk) + @ino + end =20 - # Closing the inotify descriptor (instead of using inotify_rm_watch) - # is cleaner because it avoids EINVAL on race conditions in case - # a directory is deleted: https://lkml.org/lkml/2007/7/9/3 - def watch_end(srv) - srv.wait_ctl(@ino, :delete) - @ino =3D @ino.close - end -end + def watch_extra(paths) + @ino.watch_extra(paths) + end =20 -rescue LoadError -end + # Closing the inotify descriptor (instead of using inotify_rm_watch) + # is cleaner because it avoids EINVAL on race conditions in case + # a directory is deleted: https://lkml.org/lkml/2007/7/9/3 + def watch_end(srv) + srv.wait_ctl(@ino, :delete) + @ino =3D @ino.close + end + end # module DTAS::Watchable +rescue LoadError, StandardError =3D> e + warn "#{e.message} (#{e.class})" +end # begin diff --git a/lib/dtas/watchable/fiddle_ino.rb b/lib/dtas/watchable/fiddle= _ino.rb new file mode 100644 index 0000000..3b9d668 --- /dev/null +++ b/lib/dtas/watchable/fiddle_ino.rb @@ -0,0 +1,78 @@ +# Copyright (C) 2013-2019 all contributors +# License: GPL-3.0+ +# frozen_string_literal: true +require 'fiddle' + +# used to restart DTAS::Source::SplitFX processing in dtas-player +# if the YAML file is edited +class DTAS::Watchable::InotifyReadableIter # :nodoc: + include DTAS::Watchable::InotifyCommon + + Inotify_init =3D Fiddle::Function.new(DTAS.libc['inotify_init1'], + [ Fiddle::TYPE_INT ], + Fiddle::TYPE_INT) + + Inotify_add_watch =3D Fiddle::Function.new(DTAS.libc['inotify_add_watc= h'], + [ Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT ], + Fiddle::TYPE_INT) + + # IO.select compatibility + attr_reader :to_io #:nodoc: + + def initialize # :nodoc: + fd =3D Inotify_init.call(02000000 | 04000) # CLOEXEC | NONBLOCK + raise "inotify_init failed: #{Fiddle.last_error}" if fd < 0 + @to_io =3D DTAS::Nonblock.for_fd(fd) + @buf =3D ''.b + @q =3D [] + end + + # 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 =3D Struct.new(:wd, :mask, :cookie, :len, :name) # :nodoc= : + + def take(nonblock) # :nodoc: + event =3D @q.pop and return event + case rv =3D @to_io.read_nonblock(16384, @buf, exception: false) + when :wait_readable, nil + return + else + until rv.empty? + hdr =3D rv.slice!(0,16) + name =3D nil + wd, mask, cookie, len =3D res =3D hdr.unpack('iIII') + wd && mask && cookie && len or + raise "bogus inotify_event #{res.inspect} hdr=3D#{hdr.inspect}= " + if len > 0 + name =3D rv.slice!(0, len) + name.size =3D=3D len or raise "short name #{name.inspect} !=3D= #{len}" + name.sub!(/\0+\z/, '') or + raise "missing: `\\0', inotify_event.name=3D#{name.inspect}" + name =3D DTAS.dedupe_str(name) + end + ie =3D InotifyEvent.new(wd, mask, cookie, len, name) + if event + @q << ie + else + event =3D ie + end + end # /until rv.empty? + return event + end while true + end + + def add_watch(watchdir, flags) + wd =3D 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 =3D @to_io.close if @to_io + end +end diff --git a/lib/dtas/watchable/inotify.rb b/lib/dtas/watchable/inotify.r= b new file mode 100644 index 0000000..eface0e --- /dev/null +++ b/lib/dtas/watchable/inotify.rb @@ -0,0 +1,13 @@ +# Copyright (C) 2013-2019 all contributors +# License: GPL-3.0+ +# frozen_string_literal: true +require 'sleepy_penguin' + +# used to restart DTAS::Source::SplitFX processing in dtas-player +# if the YAML file is edited +class DTAS::Watchable::InotifyReadableIter < SleepyPenguin::Inotify # :n= odoc: + include DTAS::Watchable::InotifyCommon + def self.new + super(:CLOEXEC) + end +end