From 3e09ac0c10c95bb24a08af62393b4f761e2743d0 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 24 Aug 2013 09:54:45 +0000 Subject: initial commit --- lib/dtas/process.rb | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 lib/dtas/process.rb (limited to 'lib/dtas/process.rb') diff --git a/lib/dtas/process.rb b/lib/dtas/process.rb new file mode 100644 index 0000000..35ca6a6 --- /dev/null +++ b/lib/dtas/process.rb @@ -0,0 +1,88 @@ +# -*- encoding: binary -*- +# :stopdoc: +# Copyright (C) 2013, Eric Wong +# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt) +require 'shellwords' +require 'io/wait' +module DTAS::Process + PIDS = {} + + def self.reaper + begin + pid, status = Process.waitpid2(-1, Process::WNOHANG) + pid or return + obj = PIDS.delete(pid) + yield status, obj + rescue Errno::ECHILD + return + end while true + end + + # a convienient way to display commands so it's easy to + # read, copy and paste to a shell + def xs(cmd) + cmd.map { |w| Shellwords.escape(w) }.join(' ') + end + + # for long-running processes (sox/play/ecasound filters) + def dtas_spawn(env, cmd, opts) + opts = { close_others: true, pgroup: true }.merge!(opts) + + # stringify env, integer values are easier to type unquoted as strings + env.each { |k,v| env[k] = v.to_s } + + pid = begin + Process.spawn(env, cmd, opts) + rescue Errno::EINTR # Ruby bug? + retry + end + warn [ :spawn, pid, cmd ].inspect if $DEBUG + @spawn_at = Time.now.to_f + PIDS[pid] = self + pid + end + + # this is like backtick, but takes an array instead of a string + # This will also raise on errors + def qx(cmd, opts = {}) + r, w = IO.pipe + opts = opts.merge(out: w) + r.binmode + if err = opts[:err] + re, we = IO.pipe + re.binmode + opts[:err] = we + end + pid = begin + Process.spawn(*cmd, opts) + rescue Errno::EINTR # Ruby bug? + retry + end + w.close + if err + we.close + res = "" + want = { r => res, re => err } + begin + readable = IO.select(want.keys) or next + readable[0].each do |io| + bytes = io.nread + begin + want[io] << io.read_nonblock(bytes > 0 ? bytes : 11) + rescue Errno::EAGAIN + # spurious wakeup, bytes may be zero + rescue EOFError + want.delete(io) + end + end + end until want.empty? + re.close + else + res = r.read + end + r.close + _, status = Process.waitpid2(pid) + return res if status.success? + raise RuntimeError, "`#{xs(cmd)}' failed: #{status.inspect}" + end +end -- cgit v1.2.3-24-ge0c7