Module: DTAS::Process

Includes:
XS
Included in:
Format, Format, PartStats, Sink, Source::Cmd, Source::File, Source::Mp3gain, SplitFX
Defined in:
lib/dtas/process.rb

Overview

:nodoc:

Constant Summary collapse

PIDS =
{}

Class Method Summary collapse

Instance Method Summary collapse

Methods included from XS

#xs

Class Method Details

.reaperObject



11
12
13
14
15
16
17
18
19
20
# File 'lib/dtas/process.rb', line 11

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

Instance Method Details

#dtas_spawn(env, cmd, opts) ⇒ Object

for long-running processes (sox/play/ecasound filters)



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/dtas/process.rb', line 46

def dtas_spawn(env, cmd, opts)
  opts = { close_others: true, pgroup: true }.merge!(opts)
  env = env_expand(env, opts)

  pid = begin
    Process.spawn(env, cmd, opts)
  rescue Errno::EINTR
    # workaround for older Rubies https://bugs.ruby-lang.org/issues/8770
    retry
  end
  warn [ :spawn, pid, cmd ].inspect if $DEBUG
  @spawn_at = Time.now.to_f
  PIDS[pid] = self
  pid
end

#env_expand(env, opts) ⇒ Object

expand common shell constructs based on environment variables this is order-dependent, but Ruby 1.9+ hashes are already order-dependent



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/dtas/process.rb', line 24

def env_expand(env, opts)
  env = env.dup
  if false == opts.delete(:expand)
    env.each do |key, val|
      Numeric === val and env[key] = val.to_s
    end
  else
    env.each do |key, val|
      case val
      when Numeric # stringify numeric values to simplify users' lives
        env[key] = val.to_s
      when /[\`\$]/ # perform variable/command expansion
        tmp = env.dup
        tmp.delete(key)
        val = qx(tmp, "echo #{val}", expand: false)
        env[key] = val.chomp
      end
    end
  end
end

#qx(env, cmd = {}, opts = {}) ⇒ Object

this is like backtick, but takes an array instead of a string This will also raise on errors

Raises:

  • (RuntimeError)


64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/dtas/process.rb', line 64

def qx(env, cmd = {}, opts = {})
  unless Hash === env
    cmd, opts = env, cmd
    env = {}
  end
  r, w = IO.pipe
  opts = opts.merge(out: w)
  r.binmode
  no_raise = opts.delete(:no_raise)
  if err_str = opts.delete(:err_str)
    re, we = IO.pipe
    re.binmode
    opts[:err] = we
  end
  env = env_expand(env, opts)
  pid = begin
    Process.spawn(env, *cmd, opts)
  rescue Errno::EINTR
    # workaround for older Rubies https://bugs.ruby-lang.org/issues/8770
    retry
  end
  w.close
  if err_str
    we.close
    res = "".b
    want = { r => res, re => err_str }
    begin
      readable = IO.select(want.keys) or next
      readable[0].each do |io|
        begin
          want[io] << io.read_nonblock(2000)
        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 # read until EOF
  end
  r.close
  _, status = Process.waitpid2(pid)
  return res if status.success?
  return status if no_raise
  raise RuntimeError, "`#{xs(Array(cmd))}' failed: #{status.inspect}"
end