dtas 0.15.0 → 0.18.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -1
  3. data/Documentation/.gitignore +1 -1
  4. data/Documentation/GNUmakefile +2 -2
  5. data/Documentation/dtas-archive.pod +2 -2
  6. data/Documentation/dtas-console.pod +2 -2
  7. data/Documentation/dtas-ctl.pod +2 -2
  8. data/Documentation/dtas-cueedit.pod +2 -2
  9. data/Documentation/dtas-enq.pod +2 -2
  10. data/Documentation/dtas-env.pod +3 -3
  11. data/Documentation/dtas-msinkctl.pod +2 -2
  12. data/Documentation/dtas-player.pod +2 -2
  13. data/Documentation/dtas-player_effects.pod +2 -2
  14. data/Documentation/dtas-player_protocol.pod +4 -4
  15. data/Documentation/dtas-player_sink_examples.pod +2 -2
  16. data/Documentation/dtas-sinkedit.pod +4 -4
  17. data/Documentation/dtas-sourceedit.pod +4 -4
  18. data/Documentation/dtas-splitfx.pod +7 -1
  19. data/Documentation/dtas-tl.pod +4 -4
  20. data/Documentation/dtas-xdelay.pod +2 -2
  21. data/Documentation/update-footer.rb +2 -2
  22. data/GIT-VERSION-GEN +2 -2
  23. data/GNUmakefile +1 -1
  24. data/HACKING +2 -2
  25. data/INSTALL +13 -24
  26. data/README +14 -8
  27. data/Rakefile +2 -2
  28. data/TODO +3 -1
  29. data/bin/dtas-archive +1 -1
  30. data/bin/dtas-console +19 -23
  31. data/bin/dtas-ctl +1 -1
  32. data/bin/dtas-cueedit +1 -1
  33. data/bin/dtas-enq +1 -1
  34. data/bin/dtas-mlib +1 -1
  35. data/bin/dtas-msinkctl +1 -1
  36. data/bin/dtas-partstats +1 -1
  37. data/bin/dtas-player +1 -1
  38. data/bin/dtas-readahead +4 -4
  39. data/bin/dtas-sinkedit +1 -1
  40. data/bin/dtas-sourceedit +1 -1
  41. data/bin/dtas-splitfx +8 -1
  42. data/bin/dtas-tl +1 -1
  43. data/bin/dtas-xdelay +1 -1
  44. data/dtas-linux.gemspec +5 -6
  45. data/dtas-mpris.gemspec +2 -2
  46. data/dtas.gemspec +2 -2
  47. data/examples/zsh-completion/README +3 -0
  48. data/examples/zsh-completion/_dtas-archive +16 -0
  49. data/examples/zsh-completion/_dtas-ctl +114 -0
  50. data/examples/zsh-completion/_dtas-cueedit +7 -0
  51. data/examples/zsh-completion/_dtas-enq +7 -0
  52. data/examples/zsh-completion/_dtas-mlib +29 -0
  53. data/examples/zsh-completion/_dtas-msinkctl +8 -0
  54. data/examples/zsh-completion/_dtas-partstats +7 -0
  55. data/examples/zsh-completion/_dtas-sinkedit +11 -0
  56. data/examples/zsh-completion/_dtas-sourceedit +11 -0
  57. data/examples/zsh-completion/_dtas-splitfx +17 -0
  58. data/examples/zsh-completion/_dtas-tl +52 -0
  59. data/examples/zsh-completion/_dtas-xdelay +17 -0
  60. data/lib/dtas.rb +12 -2
  61. data/lib/dtas/buffer.rb +10 -5
  62. data/lib/dtas/buffer/fiddle_splice.rb +216 -0
  63. data/lib/dtas/buffer/read_write.rb +3 -3
  64. data/lib/dtas/buffer/splice.rb +16 -10
  65. data/lib/dtas/command.rb +1 -1
  66. data/lib/dtas/compat_onenine.rb +1 -1
  67. data/lib/dtas/cue_index.rb +1 -1
  68. data/lib/dtas/disclaimer.rb +1 -1
  69. data/lib/dtas/edit_client.rb +1 -1
  70. data/lib/dtas/encoding.rb +57 -0
  71. data/lib/dtas/fadefx.rb +1 -1
  72. data/lib/dtas/format.rb +1 -1
  73. data/lib/dtas/mcache.rb +1 -1
  74. data/lib/dtas/mlib.rb +38 -31
  75. data/lib/dtas/mlib/migrations/0001_initial.rb +1 -1
  76. data/lib/dtas/nonblock.rb +1 -1
  77. data/lib/dtas/parse_freq.rb +1 -1
  78. data/lib/dtas/parse_time.rb +1 -1
  79. data/lib/dtas/partstats.rb +1 -1
  80. data/lib/dtas/pipe.rb +16 -9
  81. data/lib/dtas/pipeline.rb +75 -0
  82. data/lib/dtas/player.rb +3 -3
  83. data/lib/dtas/player/client_handler.rb +23 -15
  84. data/lib/dtas/process.rb +1 -1
  85. data/lib/dtas/replaygain.rb +1 -1
  86. data/lib/dtas/rg_state.rb +1 -1
  87. data/lib/dtas/serialize.rb +1 -1
  88. data/lib/dtas/sigevent.rb +6 -3
  89. data/lib/dtas/sigevent/efd.rb +3 -1
  90. data/lib/dtas/sigevent/fiddle_efd.rb +38 -0
  91. data/lib/dtas/sigevent/pipe.rb +2 -2
  92. data/lib/dtas/sink.rb +1 -1
  93. data/lib/dtas/source.rb +1 -1
  94. data/lib/dtas/source/av.rb +1 -1
  95. data/lib/dtas/source/av_ff_common.rb +1 -1
  96. data/lib/dtas/source/cmd.rb +1 -1
  97. data/lib/dtas/source/common.rb +1 -1
  98. data/lib/dtas/source/ff.rb +1 -1
  99. data/lib/dtas/source/file.rb +1 -1
  100. data/lib/dtas/source/mp3gain.rb +1 -1
  101. data/lib/dtas/source/sox.rb +4 -2
  102. data/lib/dtas/source/splitfx.rb +1 -1
  103. data/lib/dtas/spawn_fix.rb +1 -1
  104. data/lib/dtas/splitfx.rb +6 -3
  105. data/lib/dtas/state_file.rb +1 -1
  106. data/lib/dtas/tfx.rb +1 -1
  107. data/lib/dtas/track.rb +1 -1
  108. data/lib/dtas/tracklist.rb +1 -1
  109. data/lib/dtas/unix_accepted.rb +1 -1
  110. data/lib/dtas/unix_client.rb +1 -1
  111. data/lib/dtas/unix_server.rb +1 -1
  112. data/lib/dtas/util.rb +1 -1
  113. data/lib/dtas/watchable.rb +60 -55
  114. data/lib/dtas/watchable/fiddle_ino.rb +78 -0
  115. data/lib/dtas/watchable/inotify.rb +13 -0
  116. data/lib/dtas/writable_iter.rb +1 -1
  117. data/lib/dtas/xs.rb +1 -1
  118. data/perl/dtas-graph +7 -2
  119. data/setup.rb +1 -2
  120. data/test/covshow.rb +1 -1
  121. data/test/helper.rb +1 -1
  122. data/test/player_integration.rb +1 -1
  123. data/test/test_buffer.rb +14 -10
  124. data/test/test_encoding.rb +20 -0
  125. data/test/test_env.rb +1 -1
  126. data/test/test_fadefx.rb +1 -1
  127. data/test/test_format.rb +1 -1
  128. data/test/test_format_change.rb +1 -1
  129. data/test/test_mcache.rb +1 -1
  130. data/test/test_mlib.rb +3 -3
  131. data/test/test_parse_freq.rb +1 -1
  132. data/test/test_pipeline.rb +47 -0
  133. data/test/test_player.rb +1 -1
  134. data/test/test_player_client_handler.rb +1 -1
  135. data/test/test_player_integration.rb +1 -1
  136. data/test/test_process.rb +1 -1
  137. data/test/test_rg_integration.rb +1 -1
  138. data/test/test_rg_state.rb +1 -1
  139. data/test/test_sigevent.rb +20 -0
  140. data/test/test_sink.rb +1 -1
  141. data/test/test_sink_pipe_size.rb +13 -16
  142. data/test/test_sink_tee_integration.rb +1 -1
  143. data/test/test_source_av.rb +1 -1
  144. data/test/test_source_sox.rb +1 -1
  145. data/test/test_splitfx.rb +1 -1
  146. data/test/test_tfx.rb +1 -1
  147. data/test/test_tracklist.rb +1 -1
  148. data/test/test_unixserver.rb +1 -1
  149. data/test/test_util.rb +1 -1
  150. metadata +26 -5
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2020 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  require 'io/nonblock'
@@ -6,8 +6,8 @@
6
6
  require_relative '../pipe'
7
7
  require_relative '../nonblock'
8
8
 
9
- # compatibility code for systems lacking "splice" support via the
10
- # "io-splice" RubyGem. Used only by -player
9
+ # compatibility code for non-Linux systems lacking "splice" support.
10
+ # Used only by -player
11
11
  module DTAS::Buffer::ReadWrite # :nodoc:
12
12
  MAX_AT_ONCE = 512 # min PIPE_BUF value in POSIX
13
13
  attr_accessor :buffer_size
@@ -1,16 +1,19 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2020 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  require 'io/nonblock'
5
- require 'io/splice'
5
+ require 'sleepy_penguin'
6
6
  require_relative '../../dtas'
7
7
  require_relative '../pipe'
8
+ SleepyPenguin.respond_to?(:splice) or
9
+ raise LoadError, 'sleepy_penguin 3.5+ required for splice', []
8
10
 
9
- # Used by -player on Linux systems with the "io-splice" RubyGem installed
11
+ # Used by -player on Linux systems with the "sleepy_penguin" RubyGem installed
10
12
  module DTAS::Buffer::Splice # :nodoc:
11
13
  MAX_AT_ONCE = 4096 # page size in Linux
12
14
  MAX_AT_ONCE_1 = 65536
13
- F_MOVE = IO::Splice::F_MOVE
15
+ F_MOVE = SleepyPenguin::F_MOVE
16
+ F_NONBLOCK = SleepyPenguin::F_NONBLOCK
14
17
 
15
18
  def buffer_size
16
19
  @to_io.pipe_size
@@ -24,13 +27,14 @@ def buffer_size=(bytes)
24
27
 
25
28
  # be sure to only call this with nil when all writers to @wr are done
26
29
  def discard(bytes)
27
- IO.splice(@to_io, nil, DTAS.null, nil, bytes)
30
+ SleepyPenguin.splice(@to_io, DTAS.null, bytes)
28
31
  end
29
32
 
30
33
  def broadcast_one(targets, limit = nil)
31
34
  # single output is always non-blocking
32
35
  limit ||= MAX_AT_ONCE_1
33
- s = IO.trysplice(@to_io, nil, targets[0], nil, limit, F_MOVE)
36
+ s = SleepyPenguin.splice(@to_io, targets[0], limit, F_MOVE|F_NONBLOCK,
37
+ exception: false)
34
38
  if Symbol === s
35
39
  targets # our one and only target blocked on write
36
40
  else
@@ -46,7 +50,7 @@ def broadcast_one(targets, limit = nil)
46
50
  def __tee_in_full(src, dst, bytes)
47
51
  rv = 0
48
52
  while bytes > 0
49
- s = IO.tee(src, dst, bytes)
53
+ s = SleepyPenguin.tee(src, dst, bytes)
50
54
  bytes -= s
51
55
  rv += s
52
56
  end
@@ -56,7 +60,7 @@ def __tee_in_full(src, dst, bytes)
56
60
  def __splice_in_full(src, dst, bytes, flags)
57
61
  rv = 0
58
62
  while bytes > 0
59
- s = IO.splice(src, nil, dst, nil, bytes, flags)
63
+ s = SleepyPenguin.splice(src, dst, bytes, flags)
60
64
  rv += s
61
65
  bytes -= s
62
66
  end
@@ -69,7 +73,8 @@ def __broadcast_tee(blocked, targets, chunk_size)
69
73
  targets.delete_if do |dst|
70
74
  begin
71
75
  t = (dst.nonblock? || most_teed == 0) ?
72
- IO.trytee(@to_io, dst, chunk_size) :
76
+ SleepyPenguin.tee(@to_io, dst, chunk_size, F_NONBLOCK,
77
+ exception: false) :
73
78
  __tee_in_full(@to_io, dst, chunk_size)
74
79
  if Integer === t
75
80
  if t > most_teed
@@ -117,7 +122,8 @@ def broadcast_inf(targets, limit = nil)
117
122
  begin
118
123
  targets << last
119
124
  if last.nonblock? || most_teed == 0
120
- s = IO.trysplice(@to_io, nil, last, nil, bytes, F_MOVE)
125
+ s = SleepyPenguin.splice(@to_io, last, bytes, F_MOVE|F_NONBLOCK,
126
+ exception: false)
121
127
  if Symbol === s
122
128
  blocked << last
123
129
 
data/lib/dtas/command.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2020 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  require_relative 'serialize'
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2020 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
 
4
4
  # Make Ruby 1.9.3 look like Ruby 2.0.0 to us
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2020 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  require_relative '../dtas'
@@ -1,5 +1,5 @@
1
1
  # :enddoc:
2
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
2
+ # Copyright (C) 2013-2020 all contributors <dtas-all@nongnu.org>
3
3
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4
4
  # frozen_string_literal: true
5
5
  DTAS_PROGNAME = File.basename($0)
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2020 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  require 'tempfile'
@@ -0,0 +1,57 @@
1
+ # Copyright (C) 2018-2020 all contributors <dtas-all@nongnu.org>
2
+ # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
+ # frozen_string_literal: true
4
+
5
+ # This module gets included in DTAS
6
+ module DTAS::Encoding # :nodoc:
7
+ def self.extended(mod)
8
+ mod.instance_eval { @charlock_holmes = nil}
9
+ end
10
+
11
+ private
12
+
13
+ def try_enc_harder(str, enc, old) # :nodoc:
14
+ begin
15
+ require 'charlock_holmes'
16
+ @charlock_holmes = CharlockHolmes::EncodingDetector.new
17
+ rescue LoadError
18
+ @charlock_holmes = false
19
+ end if @charlock_holmes.nil?
20
+
21
+ case @charlock_holmes
22
+ when false
23
+ enc_fallback(str, enc, old)
24
+ else
25
+ res = @charlock_holmes.detect(str)
26
+ if det = res[:ruby_encoding]
27
+ str.force_encoding(det)
28
+ warn "charlock_holmes detected #{str.inspect} as #{det}..."
29
+ str.valid_encoding? or enc_fallback(str, det, old)
30
+ else
31
+ enc_fallback(str, enc, old)
32
+ end
33
+ end
34
+ str
35
+ end
36
+
37
+ def enc_fallback(str, enc, old) # :nodoc:
38
+ str.force_encoding(old)
39
+ warn "could not detect encoding for #{str.inspect} (not #{enc})"
40
+ end
41
+
42
+ public
43
+
44
+ def try_enc(str, enc, harder = true) # :nodoc:
45
+ old = str.encoding
46
+ return str if old == enc
47
+ str.force_encoding(enc)
48
+ unless str.valid_encoding?
49
+ if harder
50
+ try_enc_harder(str, enc, old)
51
+ else
52
+ enc_fallback(str, enc, old)
53
+ end
54
+ end
55
+ str
56
+ end
57
+ end
data/lib/dtas/fadefx.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2020 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  require_relative '../dtas'
data/lib/dtas/format.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2020 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  require_relative '../dtas'
data/lib/dtas/mcache.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2016-2020 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  # encoding: binary
data/lib/dtas/mlib.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # Copyright (C) 2015-2016 all contributors <dtas-all@nongnu.org>
2
+ # Copyright (C) 2015-2020 all contributors <dtas-all@nongnu.org>
3
3
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4
4
  # frozen_string_literal: true
5
5
  #
@@ -10,6 +10,7 @@
10
10
  require_relative 'source/ff'
11
11
  require_relative 'source/splitfx'
12
12
  require 'socket'
13
+ require 'tempfile'
13
14
 
14
15
  # For the DTAS Music Library, based on what MPD uses.
15
16
  class DTAS::Mlib # :nodoc:
@@ -37,16 +38,13 @@ class DTAS::Mlib # :nodoc:
37
38
 
38
39
  def initialize(db)
39
40
  if String === db
41
+ require 'sequel'
42
+ opts = { single_threaded: true }
40
43
  db = "sqlite://#{db}" unless db.include?('://')
41
- require 'sequel/no_core_ext'
42
- db = Sequel.connect(db, single_threaded: true)
43
- end
44
- if db.class.to_s.downcase.include?('sqlite')
45
- db.transaction_mode = :immediate
46
- db.synchronous = :off
47
- db.case_sensitive_like = false
48
- else
49
- warn 'non-SQLite databases may not work in the future'
44
+ opts[:transaction_mode] = :immediate
45
+ opts[:synchronous] = :off
46
+ opts[:case_sensitive_like] = false # only for 'search'
47
+ db = Sequel.connect(db, opts)
50
48
  end
51
49
  @db = db
52
50
  @pwd = nil
@@ -65,15 +63,23 @@ def initialize(db)
65
63
  ]
66
64
  end
67
65
 
66
+ def synchronize
67
+ @lock.flock(File::LOCK_EX)
68
+ @db.transaction { yield }
69
+ ensure
70
+ @lock.flock(File::LOCK_UN)
71
+ end
72
+
68
73
  def init_suffixes
69
- `sox --help 2>/dev/null` =~ /\nAUDIO FILE FORMATS:\s*([^\n]+)/s
74
+ `sox --help 2>/dev/null` =~ /\nAUDIO FILE FORMATS:\s*([^\n]+)/
70
75
  re = $1.split(/\s+/).map! { |x| Regexp.quote(x) }.join('|')
71
76
  @suffixes = Regexp.new("\\.(?:#{re}|yml)\\z", Regexp::IGNORECASE)
72
77
  end
73
78
 
74
- def worker(todo)
79
+ def worker(todo, lock)
80
+ @lock = lock
75
81
  @work.close
76
- @db.tables # reconnect before chdir
82
+ synchronize { @db.tables } # reconnect before chdir
77
83
  @pwd = Dir.pwd.b
78
84
  begin
79
85
  buf = todo.recv(16384) # 4x bigger than PATH_MAX ought to be enough
@@ -87,7 +93,7 @@ def worker(todo)
87
93
  end
88
94
 
89
95
  def ignore(job)
90
- @db.transaction do
96
+ synchronize do
91
97
  node_ensure(job.parent_id, job.path, DM_IGN, job.ctime)
92
98
  end
93
99
  end
@@ -106,24 +112,16 @@ def worker_work(job)
106
112
  end
107
113
  return ignore(job) unless found
108
114
  tlen = found.duration
109
- return ignore(job) if tlen < 0
115
+ return ignore(job) if tlen.nil? || tlen < 0
110
116
  tlen = tlen.round
111
117
  tmp = {}
112
- found.comments.each do |tag, value|
113
- tag_id = @tag_map[tag] or next
114
- value.strip!
115
-
116
- # FIXME: this fallback needs testing
117
- [ Encoding::UTF_8, Encoding::ISO_8859_1 ].each do |enc|
118
- value.force_encoding(enc)
119
- if value.valid_encoding?
120
- value.encode!(Encoding::UTF_8) if enc != Encoding::UTF_8
121
- tmp[tag_id] = value
122
- break
123
- end
118
+ if comments = found.comments
119
+ comments.each do |tag, value|
120
+ tag_id = @tag_map[tag] or next
121
+ tmp[tag_id] = value if value.valid_encoding?
124
122
  end
125
123
  end
126
- @db.transaction do
124
+ synchronize do
127
125
  node_id = node_ensure(job.parent_id, path, tlen, job.ctime)[:id]
128
126
  vals = @db[:vals]
129
127
  comments = @db[:comments]
@@ -154,11 +152,20 @@ def update(path, opts = nil)
154
152
  @work and raise 'update already running'
155
153
  todo, @work = UNIXSocket.pair(:SOCK_SEQPACKET)
156
154
  @db.disconnect
157
- jobs.times { |i| fork { worker(todo) } }
155
+
156
+ # like a Mutex between processes
157
+ @lock = Tempfile.new('dtas.mlib.lock')
158
+ jobs.times do |i|
159
+ lock = File.open(@lock.path, 'w')
160
+ fork { worker(todo, lock) }
161
+ lock.close
162
+ end
163
+ @lock.unlink
158
164
  todo.close
159
165
  scan_dir(path, st)
160
166
  @work.close
161
167
  Process.waitall
168
+ @lock.close
162
169
  ensure
163
170
  @work = nil
164
171
  end
@@ -246,7 +253,7 @@ def root_node
246
253
  end
247
254
 
248
255
  def dir_vivify(parts, ctime)
249
- @db.transaction do
256
+ synchronize do
250
257
  dir = root_node
251
258
  last = parts.pop
252
259
  parts.each do |name|
@@ -300,7 +307,7 @@ def scan_dir(path, st, parent_id = nil)
300
307
  dir = dir_vivify(@pwd.split(%r{/+}n), st.ctime.to_i)
301
308
  dir_id = dir[:id]
302
309
 
303
- @db.transaction do
310
+ synchronize do
304
311
  @db[:nodes].where(parent_id: dir_id).each do |node|
305
312
  File.exist?(node[:name]) or remove_entry(node)
306
313
  end
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2015-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2015-2020 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
 
4
4
  Sequel.migration do
data/lib/dtas/nonblock.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2015-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2015-2020 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
 
4
4
  class DTAS::Nonblock < IO # :nodoc:
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2015-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2015-2020 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
4
  require_relative '../dtas'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
2
+ # Copyright (C) 2013-2020 all contributors <dtas-all@nongnu.org>
3
3
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4
4
  # frozen_string_literal: true
5
5
  require_relative '../dtas'
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: binary -*-
2
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
2
+ # Copyright (C) 2013-2020 all contributors <dtas-all@nongnu.org>
3
3
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4
4
  # frozen_string_literal: true
5
5
  require_relative '../dtas'
data/lib/dtas/pipe.rb CHANGED
@@ -1,10 +1,6 @@
1
- # Copyright (C) 2013-2016 all contributors <dtas-all@nongnu.org>
1
+ # Copyright (C) 2013-2020 all contributors <dtas-all@nongnu.org>
2
2
  # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
3
  # frozen_string_literal: true
4
- begin
5
- require 'io/splice'
6
- rescue LoadError
7
- end
8
4
  require_relative '../dtas'
9
5
  require_relative 'writable_iter'
10
6
  require_relative 'nonblock'
@@ -14,18 +10,29 @@ class DTAS::Pipe < DTAS::Nonblock # :nodoc:
14
10
  include DTAS::WritableIter
15
11
  attr_accessor :sink
16
12
 
13
+ if RUBY_PLATFORM =~ /linux/i && File.readable?('/proc/sys/fs/pipe-max-size')
14
+ F_SETPIPE_SZ = 1031
15
+ F_GETPIPE_SZ = 1032
16
+ end
17
+
17
18
  def self.new
18
19
  _, w = rv = pipe
19
20
  w.writable_iter_init
20
21
  rv
21
22
  end
22
23
 
23
- # create no-op methods for non-Linux
24
- unless method_defined?(:pipe_size=)
25
- def pipe_size=(_)
26
- end
24
+ def pipe_size=(nr)
25
+ fcntl(F_SETPIPE_SZ, nr) if defined?(F_SETPIPE_SZ)
26
+ rescue Errno::EINVAL # old kernel
27
+ rescue Errno::EPERM
28
+ # resizes fail if Linux is close to the pipe limit for the user
29
+ # or if the user does not have permissions to resize
27
30
  end
28
31
 
32
+ def pipe_size
33
+ fcntl(F_GETPIPE_SZ)
34
+ end if defined?(F_GETPIPE_SZ)
35
+
29
36
  # avoid syscall, we never change IO#nonblock= directly
30
37
  def nonblock?
31
38
  false
@@ -0,0 +1,75 @@
1
+ # Copyright (C) 2017-2020 all contributors <dtas-all@nongnu.org>
2
+ # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
+ # frozen_string_literal: true
4
+ require_relative '../dtas'
5
+ require_relative 'spawn_fix'
6
+
7
+ module DTAS::Pipeline # :nodoc:
8
+ include DTAS::SpawnFix
9
+
10
+ # Process.spawn wrapper which supports running Proc-like objects in
11
+ # a separate process, not just external commands.
12
+ # Returns the pid of the spawned process
13
+ def pspawn(env, cmd, rdr = {})
14
+ case cmd
15
+ when Array
16
+ spawn(env, *cmd, rdr)
17
+ else # support running Proc-like objects, too:
18
+ fork do
19
+ ENV.update(env) if env
20
+
21
+ # setup redirects
22
+ [ $stdin, $stdout, $stderr ].each_with_index do |io, fd|
23
+ dst = rdr[fd] and io.reopen(dst)
24
+ end
25
+
26
+ # close all other pipes, since we can't rely on FD_CLOEXEC
27
+ # (as we do not exec, here)
28
+ rdr.each do |k, v|
29
+ k.close if v == :close
30
+ end
31
+ cmd.call
32
+ end
33
+ end
34
+ end
35
+
36
+ # +pipeline+ is an Array of (Arrays or Procs)
37
+ def run_pipeline(env, pipeline)
38
+ pids = {} # pid => pipeline index
39
+ work = pipeline.dup
40
+ last = work.pop
41
+ nr = work.size
42
+ rdr = {} # redirect mapping for Process.spawn
43
+
44
+ # we need to make sure pipes are closed in any forked processes
45
+ # (they are redirected to stdin or stdout, first)
46
+ pipes = nr.times.map { IO.pipe.each { |io| rdr[io] = :close } }
47
+
48
+ # start the first and last commands first, they only have one pipe, each
49
+ last_pid = pspawn(env, last, rdr.merge(0 => pipes[-1][0]))
50
+ pids[last_pid] = nr
51
+ first = work.shift
52
+ first_pid = pspawn(env, first, rdr.merge(1 => pipes[0][1]))
53
+ pids[first_pid] = 0
54
+
55
+ # start the middle commands, they both have two pipes:
56
+ work.each_with_index do |cmd, i|
57
+ pid = pspawn(env, cmd, rdr.merge(0 => pipes[i][0], 1 => pipes[i+1][1]))
58
+ pids[pid] = i + 1
59
+ end
60
+
61
+ # all pipes handed off to children, close so they see EOF
62
+ pipes.flatten!.each(&:close).clear
63
+
64
+ # wait for children to finish
65
+ fails = []
66
+ until pids.empty?
67
+ pid, status = Process.waitpid2(-1)
68
+ nr = pids.delete(pid)
69
+ status.success? or
70
+ fails << "reaped #{nr} #{pipeline[nr].inspect} #{status.inspect}"
71
+ end
72
+ # behave like "set -o pipefail" in bash
73
+ raise fails.join("\n") if fails[0]
74
+ end
75
+ end