dtas 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/.gitignore +9 -0
- data/.rsync_doc +3 -0
- data/COPYING +674 -0
- data/Documentation/.gitignore +3 -0
- data/Documentation/GNUmakefile +46 -0
- data/Documentation/dtas-console.txt +42 -0
- data/Documentation/dtas-ctl.txt +64 -0
- data/Documentation/dtas-cueedit.txt +24 -0
- data/Documentation/dtas-enq.txt +29 -0
- data/Documentation/dtas-msinkctl.txt +45 -0
- data/Documentation/dtas-player.txt +110 -0
- data/Documentation/dtas-player_effects.txt +45 -0
- data/Documentation/dtas-player_protocol.txt +181 -0
- data/Documentation/dtas-sinkedit.txt +41 -0
- data/Documentation/dtas-sourceedit.txt +33 -0
- data/Documentation/dtas-xdelay.txt +57 -0
- data/Documentation/troubleshooting.txt +13 -0
- data/GIT-VERSION-GEN +30 -0
- data/GNUmakefile +9 -0
- data/HACKING +12 -0
- data/INSTALL +53 -0
- data/README +103 -0
- data/Rakefile +97 -0
- data/TODO +4 -0
- data/bin/dtas-console +160 -0
- data/bin/dtas-ctl +10 -0
- data/bin/dtas-cueedit +78 -0
- data/bin/dtas-enq +13 -0
- data/bin/dtas-msinkctl +51 -0
- data/bin/dtas-player +34 -0
- data/bin/dtas-sinkedit +58 -0
- data/bin/dtas-sourceedit +48 -0
- data/bin/dtas-xdelay +85 -0
- data/dtas-linux.gemspec +18 -0
- data/dtas-mpris.gemspec +16 -0
- data/examples/dtas_state.yml +18 -0
- data/lib/dtas.rb +7 -0
- data/lib/dtas/buffer.rb +90 -0
- data/lib/dtas/buffer/read_write.rb +102 -0
- data/lib/dtas/buffer/splice.rb +142 -0
- data/lib/dtas/command.rb +43 -0
- data/lib/dtas/compat_onenine.rb +18 -0
- data/lib/dtas/disclaimer.rb +18 -0
- data/lib/dtas/format.rb +151 -0
- data/lib/dtas/pipe.rb +39 -0
- data/lib/dtas/player.rb +393 -0
- data/lib/dtas/player/client_handler.rb +463 -0
- data/lib/dtas/process.rb +87 -0
- data/lib/dtas/replaygain.rb +41 -0
- data/lib/dtas/rg_state.rb +99 -0
- data/lib/dtas/serialize.rb +9 -0
- data/lib/dtas/sigevent.rb +10 -0
- data/lib/dtas/sigevent/efd.rb +20 -0
- data/lib/dtas/sigevent/pipe.rb +28 -0
- data/lib/dtas/sink.rb +121 -0
- data/lib/dtas/source.rb +147 -0
- data/lib/dtas/source/command.rb +40 -0
- data/lib/dtas/source/common.rb +14 -0
- data/lib/dtas/source/mp3.rb +37 -0
- data/lib/dtas/state_file.rb +33 -0
- data/lib/dtas/unix_accepted.rb +76 -0
- data/lib/dtas/unix_client.rb +51 -0
- data/lib/dtas/unix_server.rb +110 -0
- data/lib/dtas/util.rb +15 -0
- data/lib/dtas/writable_iter.rb +22 -0
- data/perl/dtas-graph +129 -0
- data/pkg.mk +26 -0
- data/setup.rb +1586 -0
- data/test/covshow.rb +30 -0
- data/test/helper.rb +76 -0
- data/test/player_integration.rb +121 -0
- data/test/test_buffer.rb +216 -0
- data/test/test_format.rb +61 -0
- data/test/test_format_change.rb +49 -0
- data/test/test_player.rb +47 -0
- data/test/test_player_client_handler.rb +86 -0
- data/test/test_player_integration.rb +220 -0
- data/test/test_rg_integration.rb +117 -0
- data/test/test_rg_state.rb +32 -0
- data/test/test_sink.rb +32 -0
- data/test/test_sink_tee_integration.rb +34 -0
- data/test/test_source.rb +102 -0
- data/test/test_unixserver.rb +66 -0
- data/test/test_util.rb +15 -0
- metadata +208 -0
data/bin/dtas-xdelay
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- encoding: binary -*-
|
3
|
+
# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
|
4
|
+
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
5
|
+
USAGE = "Usage: #$0 [-x FREQ] [-l] /dev/fd/LO /dev/fd/HI DELAY [DELAY ...]"
|
6
|
+
require 'optparse'
|
7
|
+
dryrun = false
|
8
|
+
xover = '80'
|
9
|
+
delay_lo = []
|
10
|
+
delay_hi = []
|
11
|
+
adj_delay = delay_hi
|
12
|
+
out_channels = out_rate = out_type = nil
|
13
|
+
|
14
|
+
lowpass = 'lowpass %s lowpass %s'
|
15
|
+
highpass = 'highpass %s highpass %s'
|
16
|
+
|
17
|
+
op = OptionParser.new('', 24, ' ') do |opts|
|
18
|
+
opts.banner = USAGE
|
19
|
+
opts.on('-x', '--crossover-frequency FREQ') do |freq|
|
20
|
+
xover = freq
|
21
|
+
end
|
22
|
+
opts.on('-l', '--lowpass-delay') { adj_delay = delay_lo }
|
23
|
+
opts.on('-c', '--channels INTEGER') { |val| out_channels = val }
|
24
|
+
opts.on('-r', '--rate RATE') { |val| out_rate = val }
|
25
|
+
opts.on('-t', '--type FILE-TYPE') { |val| out_type = val }
|
26
|
+
opts.on('-n', '--dry-run') { dryrun = true }
|
27
|
+
opts.on('--lowpass FORMAT_STRING') { |s| lowpass = s }
|
28
|
+
opts.on('--highpass FORMAT_STRING') { |s| highpass = s }
|
29
|
+
opts.parse!(ARGV)
|
30
|
+
end
|
31
|
+
|
32
|
+
dev_fd_lo = ARGV.shift
|
33
|
+
dev_fd_hi = ARGV.shift
|
34
|
+
if ARGV.delete('-')
|
35
|
+
# we re-add the '-' below
|
36
|
+
out_channels && out_rate && out_type or
|
37
|
+
abort "-c, -r, and -t must all be specified for standard output"
|
38
|
+
cmd = "sox"
|
39
|
+
elsif out_channels || out_rate || out_type
|
40
|
+
abort "standard output (`-') must be specified with -c, -r, or -t"
|
41
|
+
else
|
42
|
+
cmd = "play"
|
43
|
+
end
|
44
|
+
soxfmt = ENV["SOXFMT"] or abort "#$0 SOXFMT undefined"
|
45
|
+
|
46
|
+
# configure the sox "delay" effect
|
47
|
+
delay = ARGV.dup
|
48
|
+
delay[0] or abort USAGE
|
49
|
+
channels = ENV['CHANNELS'] or abort "#$0 CHANNELS env must be set"
|
50
|
+
channels = channels.to_i
|
51
|
+
adj_delay.replace(delay.dup)
|
52
|
+
until adj_delay.size == channels
|
53
|
+
adj_delay << delay.last
|
54
|
+
end
|
55
|
+
adj_delay.unshift("delay")
|
56
|
+
|
57
|
+
# prepare two inputs:
|
58
|
+
delay_lo = delay_lo.join(' ')
|
59
|
+
delay_hi = delay_hi.join(' ')
|
60
|
+
|
61
|
+
lowpass_args = []
|
62
|
+
lowpass.gsub('%s') { |s| lowpass_args << xover; s }
|
63
|
+
highpass_args = []
|
64
|
+
highpass.gsub('%s') { |s| highpass_args << xover; s }
|
65
|
+
|
66
|
+
lo = "|exec sox #{soxfmt} #{dev_fd_lo} -p " \
|
67
|
+
"#{sprintf(lowpass, *lowpass_args)} #{delay_lo}".strip
|
68
|
+
hi = "|exec sox #{soxfmt} #{dev_fd_hi} -p " \
|
69
|
+
"#{sprintf(highpass, *highpass_args)} #{delay_hi}".strip
|
70
|
+
|
71
|
+
args = [ "-m", "-v1", lo, "-v1", hi ]
|
72
|
+
case cmd
|
73
|
+
when "sox"
|
74
|
+
args.unshift "sox"
|
75
|
+
args.concat(%W(-t#{out_type} -c#{out_channels} -r#{out_rate} -))
|
76
|
+
when "play"
|
77
|
+
args.unshift "play"
|
78
|
+
else
|
79
|
+
abort "BUG: bad cmd=#{cmd.inspect}"
|
80
|
+
end
|
81
|
+
if dryrun
|
82
|
+
p args
|
83
|
+
else
|
84
|
+
exec *args, close_others: false
|
85
|
+
end
|
data/dtas-linux.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# this just declares dependencies to make gem installation a little easier
|
3
|
+
# for Linux users
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = %q{dtas-linux}
|
6
|
+
s.version = '1.0.0'
|
7
|
+
s.authors = ["dtas hackers"]
|
8
|
+
s.summary = "meta-package for dtas users on the Linux kernel"
|
9
|
+
s.description = "gives small performance improvements for dtas users\n" \
|
10
|
+
"via tee(), splice() and eventfd() on Linux"
|
11
|
+
s.email = %q{e@80x24.org}
|
12
|
+
s.files = []
|
13
|
+
s.homepage = 'http://dtas.80x24.org/'
|
14
|
+
s.add_dependency(%q<dtas>)
|
15
|
+
s.add_dependency(%q<io_splice>, '~> 4')
|
16
|
+
s.add_dependency(%q<sleepy_penguin>, '~> 3')
|
17
|
+
s.licenses = "GPLv3+"
|
18
|
+
end
|
data/dtas-mpris.gemspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = %q{dtas-mpris}
|
4
|
+
s.version = '0.0.0'
|
5
|
+
s.authors = ["dtas hackers"]
|
6
|
+
s.summary = "meta-package for the dtas-mpris proxy"
|
7
|
+
s.description =
|
8
|
+
"this allows controlling dtas-player via MPRIS or MPRIS 2.0\n" \
|
9
|
+
"This is currently a dummy package as dtas-mpris is not implemented"
|
10
|
+
s.email = %q{e@80x24.org}
|
11
|
+
s.files = []
|
12
|
+
s.homepage = 'http://dtas.80x24.org/'
|
13
|
+
s.add_dependency(%q<dtas>)
|
14
|
+
s.add_dependency(%q<ruby-dbus>)
|
15
|
+
s.licenses = "GPLv2+"
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
socket: .dtas/player.sock
|
3
|
+
sinks:
|
4
|
+
- name: ODAC
|
5
|
+
command: |-
|
6
|
+
sox -t $SOX_FILETYPE -r $RATE -c $CHANNELS - \
|
7
|
+
-t s$SINK_BITS -r $SINK_RATE -c $SINK_CHANNELS - | \
|
8
|
+
aplay -D hw:DAC_1 -v -q -M --buffer-size=500000 --period-size=500 \
|
9
|
+
--disable-softvol --start-delay=100 \
|
10
|
+
--disable-format --disable-resample --disable-channels \
|
11
|
+
-t raw -c $SINK_CHANNELS -f S${SINK_BITS}_3LE -r $SINK_RATE
|
12
|
+
env:
|
13
|
+
SINK_BITS: 24
|
14
|
+
SINK_RATE: 44100
|
15
|
+
SINK_CHANNELS: 2
|
16
|
+
- name: play
|
17
|
+
prio: -2
|
18
|
+
command: env AUDIODEV=hw:DAC play -t $SOX_FILETYPE -r $RATE -c $CHANNELS -
|
data/lib/dtas.rb
ADDED
data/lib/dtas/buffer.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
|
3
|
+
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
4
|
+
require_relative '../dtas'
|
5
|
+
|
6
|
+
class DTAS::Buffer # :nodoc:
|
7
|
+
begin
|
8
|
+
raise LoadError, "no splice with _DTAS_POSIX" if ENV["_DTAS_POSIX"]
|
9
|
+
require 'io/splice' # splice is only in Linux for now...
|
10
|
+
require_relative 'buffer/splice'
|
11
|
+
include DTAS::Buffer::Splice
|
12
|
+
rescue LoadError
|
13
|
+
require_relative 'buffer/read_write'
|
14
|
+
include DTAS::Buffer::ReadWrite
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :to_io # call nread on this
|
18
|
+
attr_reader :wr # processes (sources) should redirect to this
|
19
|
+
attr_accessor :bytes_xfer
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@bytes_xfer = 0
|
23
|
+
@buffer_size = nil
|
24
|
+
@to_io, @wr = DTAS::Pipe.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.load(hash)
|
28
|
+
buf = new
|
29
|
+
if hash
|
30
|
+
bs = hash["buffer_size"] and buf.buffer_size = bs
|
31
|
+
end
|
32
|
+
buf
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_hsh
|
36
|
+
@buffer_size ? { "buffer_size" => @buffer_size } : {}
|
37
|
+
end
|
38
|
+
|
39
|
+
def __dst_error(dst, e)
|
40
|
+
warn "dropping #{dst.inspect} due to error: #{e.message} (#{e.class})"
|
41
|
+
dst.close unless dst.closed?
|
42
|
+
end
|
43
|
+
|
44
|
+
# This will modify targets
|
45
|
+
# returns one of:
|
46
|
+
# - :wait_readable
|
47
|
+
# - subset of targets array for :wait_writable
|
48
|
+
# - some type of StandardError
|
49
|
+
# - nil
|
50
|
+
def broadcast(targets)
|
51
|
+
bytes = inflight
|
52
|
+
return :wait_readable if 0 == bytes # spurious wakeup
|
53
|
+
|
54
|
+
case targets.size
|
55
|
+
when 0
|
56
|
+
:ignore # this will pause decoders
|
57
|
+
when 1
|
58
|
+
broadcast_one(targets, bytes)
|
59
|
+
else # infinity
|
60
|
+
broadcast_inf(targets, bytes)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def readable_iter
|
65
|
+
# this calls DTAS::Buffer#broadcast from DTAS::Player
|
66
|
+
yield(self, nil)
|
67
|
+
end
|
68
|
+
|
69
|
+
def inflight
|
70
|
+
@to_io.nread
|
71
|
+
end
|
72
|
+
|
73
|
+
# don't really close the pipes under normal circumstances, just clear data
|
74
|
+
def close
|
75
|
+
bytes = inflight
|
76
|
+
discard(bytes) if bytes > 0
|
77
|
+
end
|
78
|
+
|
79
|
+
def buf_reset
|
80
|
+
close!
|
81
|
+
@bytes_xfer = 0
|
82
|
+
@to_io, @wr = DTAS::Pipe.new
|
83
|
+
@wr.pipe_size = @buffer_size if @buffer_size
|
84
|
+
end
|
85
|
+
|
86
|
+
def close!
|
87
|
+
@to_io.close
|
88
|
+
@wr.close
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
|
3
|
+
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
4
|
+
require 'io/wait'
|
5
|
+
require 'io/nonblock'
|
6
|
+
require_relative '../../dtas'
|
7
|
+
require_relative '../pipe'
|
8
|
+
|
9
|
+
module DTAS::Buffer::ReadWrite # :nodoc:
|
10
|
+
MAX_AT_ONCE = 512 # min PIPE_BUF value in POSIX
|
11
|
+
attr_accessor :buffer_size
|
12
|
+
|
13
|
+
def _rbuf
|
14
|
+
Thread.current[:dtas_pbuf] ||= ""
|
15
|
+
end
|
16
|
+
|
17
|
+
# be sure to only call this with nil when all writers to @wr are done
|
18
|
+
def discard(bytes)
|
19
|
+
buf = _rbuf
|
20
|
+
begin
|
21
|
+
@to_io.read(bytes, buf) or break # EOF
|
22
|
+
bytes -= buf.bytesize
|
23
|
+
end until bytes == 0
|
24
|
+
end
|
25
|
+
|
26
|
+
# always block when we have a single target
|
27
|
+
def broadcast_one(targets, bytes)
|
28
|
+
buf = _rbuf
|
29
|
+
@to_io.read(bytes, buf)
|
30
|
+
n = targets[0].write(buf) # IO#write has write-in-full behavior
|
31
|
+
@bytes_xfer += n
|
32
|
+
:wait_readable
|
33
|
+
rescue Errno::EPIPE, IOError => e
|
34
|
+
__dst_error(targets[0], e)
|
35
|
+
targets.clear
|
36
|
+
nil # do not return error here, we already spewed an error message
|
37
|
+
end
|
38
|
+
|
39
|
+
def broadcast_inf(targets, bytes)
|
40
|
+
nr_nb = targets.count { |sink| sink.nonblock? }
|
41
|
+
if nr_nb == 0 || nr_nb == targets.size
|
42
|
+
# if all targets are full, don't start until they're all writable
|
43
|
+
r = IO.select(nil, targets, nil, 0) or return targets
|
44
|
+
blocked = targets - r[1]
|
45
|
+
|
46
|
+
# tell DTAS::UNIXServer#run_once to wait on the blocked targets
|
47
|
+
return blocked if blocked[0]
|
48
|
+
|
49
|
+
# all writable, yay!
|
50
|
+
else
|
51
|
+
blocked = []
|
52
|
+
end
|
53
|
+
|
54
|
+
again = {}
|
55
|
+
|
56
|
+
# don't pin too much on one target
|
57
|
+
bytes = bytes > MAX_AT_ONCE ? MAX_AT_ONCE : bytes
|
58
|
+
buf = _rbuf
|
59
|
+
@to_io.read(bytes, buf)
|
60
|
+
@bytes_xfer += buf.bytesize
|
61
|
+
|
62
|
+
targets.delete_if do |dst|
|
63
|
+
begin
|
64
|
+
if dst.nonblock?
|
65
|
+
w = dst.write_nonblock(buf)
|
66
|
+
again[dst] = buf.byteslice(w, n) if w < n
|
67
|
+
else
|
68
|
+
dst.write(buf)
|
69
|
+
end
|
70
|
+
false
|
71
|
+
rescue Errno::EAGAIN
|
72
|
+
blocked << dst
|
73
|
+
false
|
74
|
+
rescue IOError, Errno::EPIPE => e
|
75
|
+
again.delete(dst)
|
76
|
+
__dst_error(dst, e)
|
77
|
+
true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# try to write as much as possible
|
82
|
+
again.delete_if do |dst, sbuf|
|
83
|
+
begin
|
84
|
+
w = dst.write_nonblock(sbuf)
|
85
|
+
n = sbuf.bytesize
|
86
|
+
if w < n
|
87
|
+
again[dst] = sbuf.byteslice(w, n)
|
88
|
+
false
|
89
|
+
else
|
90
|
+
true
|
91
|
+
end
|
92
|
+
rescue Errno::EAGAIN
|
93
|
+
blocked << dst
|
94
|
+
true
|
95
|
+
rescue IOError, Errno::EPIPE => e
|
96
|
+
__dst_error(dst, e)
|
97
|
+
true
|
98
|
+
end
|
99
|
+
end until again.empty?
|
100
|
+
targets[0] ? :wait_readable : nil
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net>
|
3
|
+
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
4
|
+
require 'io/wait'
|
5
|
+
require 'io/nonblock'
|
6
|
+
require 'io/splice'
|
7
|
+
require_relative '../../dtas'
|
8
|
+
require_relative '../pipe'
|
9
|
+
|
10
|
+
module DTAS::Buffer::Splice # :nodoc:
|
11
|
+
MAX_AT_ONCE = 4096 # page size in Linux
|
12
|
+
MAX_SIZE = File.read("/proc/sys/fs/pipe-max-size").to_i
|
13
|
+
DEVNULL = File.open("/dev/null", "r+")
|
14
|
+
F_MOVE = IO::Splice::F_MOVE
|
15
|
+
WAITALL = IO::Splice::WAITALL
|
16
|
+
|
17
|
+
def buffer_size
|
18
|
+
@to_io.pipe_size
|
19
|
+
end
|
20
|
+
|
21
|
+
# nil is OK, won't reset existing pipe, either...
|
22
|
+
def buffer_size=(bytes)
|
23
|
+
@to_io.pipe_size = bytes if bytes
|
24
|
+
@buffer_size = bytes
|
25
|
+
end
|
26
|
+
|
27
|
+
# be sure to only call this with nil when all writers to @wr are done
|
28
|
+
def discard(bytes)
|
29
|
+
IO.splice(@to_io, nil, DEVNULL, nil, bytes)
|
30
|
+
end
|
31
|
+
|
32
|
+
def broadcast_one(targets, bytes)
|
33
|
+
# single output is always non-blocking
|
34
|
+
s = IO.trysplice(@to_io, nil, targets[0], nil, bytes, F_MOVE)
|
35
|
+
if Symbol === s
|
36
|
+
targets # our one and only target blocked on write
|
37
|
+
else
|
38
|
+
@bytes_xfer += s
|
39
|
+
:wait_readable # we want to read more from @to_io soon
|
40
|
+
end
|
41
|
+
rescue Errno::EPIPE, IOError => e
|
42
|
+
__dst_error(targets[0], e)
|
43
|
+
targets.clear
|
44
|
+
nil # do not return error here, we already spewed an error message
|
45
|
+
end
|
46
|
+
|
47
|
+
# returns the largest value we teed
|
48
|
+
def __broadcast_tee(blocked, targets, chunk_size)
|
49
|
+
most_teed = 0
|
50
|
+
targets.delete_if do |dst|
|
51
|
+
begin
|
52
|
+
t = dst.nonblock? ?
|
53
|
+
IO.trytee(@to_io, dst, chunk_size) :
|
54
|
+
IO.tee(@to_io, dst, chunk_size, WAITALL)
|
55
|
+
if Integer === t
|
56
|
+
most_teed = t if t > most_teed
|
57
|
+
else
|
58
|
+
blocked << dst
|
59
|
+
end
|
60
|
+
false
|
61
|
+
rescue IOError, Errno::EPIPE => e
|
62
|
+
__dst_error(dst, e)
|
63
|
+
true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
most_teed
|
67
|
+
end
|
68
|
+
|
69
|
+
def broadcast_inf(targets, bytes)
|
70
|
+
if targets.none? { |sink| sink.nonblock? }
|
71
|
+
# if all targets are blocking, don't start until they're all writable
|
72
|
+
r = IO.select(nil, targets, nil, 0) or return targets
|
73
|
+
blocked = targets - r[1]
|
74
|
+
|
75
|
+
# tell DTAS::UNIXServer#run_once to wait on the blocked targets
|
76
|
+
return blocked if blocked[0]
|
77
|
+
|
78
|
+
# all writable, yay!
|
79
|
+
else
|
80
|
+
blocked = []
|
81
|
+
end
|
82
|
+
|
83
|
+
# don't pin too much on one target
|
84
|
+
bytes = bytes > MAX_AT_ONCE ? MAX_AT_ONCE : bytes
|
85
|
+
|
86
|
+
last = targets.pop # we splice to the last one, tee to the rest
|
87
|
+
most_teed = __broadcast_tee(blocked, targets, bytes)
|
88
|
+
|
89
|
+
# don't splice more than the largest amount we successfully teed
|
90
|
+
bytes = most_teed if most_teed > 0
|
91
|
+
|
92
|
+
begin
|
93
|
+
targets << last
|
94
|
+
if last.nonblock?
|
95
|
+
s = IO.trysplice(@to_io, nil, last, nil, bytes, F_MOVE)
|
96
|
+
if Symbol === s
|
97
|
+
blocked << last
|
98
|
+
|
99
|
+
# we accomplished nothing!
|
100
|
+
# If _all_ writers are blocked, do not discard data,
|
101
|
+
# stay blocked on :wait_writable
|
102
|
+
return blocked if most_teed == 0
|
103
|
+
|
104
|
+
# the tees targets win, drop data intended for last
|
105
|
+
if most_teed > 0
|
106
|
+
discard(most_teed)
|
107
|
+
@bytes_xfer += most_teed
|
108
|
+
# do not watch for writability of last, last is non-blocking
|
109
|
+
return :wait_readable
|
110
|
+
end
|
111
|
+
end
|
112
|
+
else
|
113
|
+
# the blocking case is simple
|
114
|
+
s = IO.splice(@to_io, nil, last, nil, bytes, WAITALL|F_MOVE)
|
115
|
+
end
|
116
|
+
@bytes_xfer += s
|
117
|
+
|
118
|
+
# if we can't splice everything
|
119
|
+
# discard it so the early targets do not get repeated data
|
120
|
+
if s < bytes && most_teed > 0
|
121
|
+
discard(bytes - s)
|
122
|
+
end
|
123
|
+
:wait_readable
|
124
|
+
rescue IOError, Errno::EPIPE => e # last failed, drop it
|
125
|
+
__dst_error(last, e)
|
126
|
+
targets.pop # we're no longer a valid target
|
127
|
+
|
128
|
+
if most_teed == 0
|
129
|
+
# nothing accomplished, watch any targets
|
130
|
+
return blocked if blocked[0]
|
131
|
+
else
|
132
|
+
# some progress, discard the data we could not splice
|
133
|
+
@bytes_xfer += most_teed
|
134
|
+
discard(most_teed)
|
135
|
+
end
|
136
|
+
|
137
|
+
# stop decoding if we're completely errored out
|
138
|
+
# returning nil will trigger close
|
139
|
+
return targets[0] ? :wait_readable : nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|