engineyard-serverside 1.5.23.ruby19.8 → 1.5.23.ruby19.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. data/lib/engineyard-serverside.rb +3 -1
  2. data/lib/engineyard-serverside/cli.rb +11 -19
  3. data/lib/engineyard-serverside/deploy.rb +3 -3
  4. data/lib/engineyard-serverside/future.rb +33 -0
  5. data/lib/engineyard-serverside/futures/celluloid.rb +25 -0
  6. data/lib/engineyard-serverside/futures/dataflow.rb +25 -0
  7. data/lib/engineyard-serverside/logged_output.rb +8 -3
  8. data/lib/engineyard-serverside/task.rb +9 -12
  9. data/lib/engineyard-serverside/version.rb +1 -1
  10. data/lib/vendor/celluloid/lib/celluloid.rb +261 -0
  11. data/lib/vendor/celluloid/lib/celluloid/actor.rb +242 -0
  12. data/lib/vendor/celluloid/lib/celluloid/actor_pool.rb +54 -0
  13. data/lib/vendor/celluloid/lib/celluloid/actor_proxy.rb +75 -0
  14. data/lib/vendor/celluloid/lib/celluloid/application.rb +78 -0
  15. data/lib/vendor/celluloid/lib/celluloid/calls.rb +94 -0
  16. data/lib/vendor/celluloid/lib/celluloid/core_ext.rb +14 -0
  17. data/lib/vendor/celluloid/lib/celluloid/events.rb +14 -0
  18. data/lib/vendor/celluloid/lib/celluloid/fiber.rb +33 -0
  19. data/lib/vendor/celluloid/lib/celluloid/fsm.rb +141 -0
  20. data/lib/vendor/celluloid/lib/celluloid/future.rb +60 -0
  21. data/lib/vendor/celluloid/lib/celluloid/links.rb +61 -0
  22. data/lib/vendor/celluloid/lib/celluloid/logger.rb +32 -0
  23. data/lib/vendor/celluloid/lib/celluloid/mailbox.rb +124 -0
  24. data/lib/vendor/celluloid/lib/celluloid/receivers.rb +66 -0
  25. data/lib/vendor/celluloid/lib/celluloid/registry.rb +33 -0
  26. data/lib/vendor/celluloid/lib/celluloid/responses.rb +26 -0
  27. data/lib/vendor/celluloid/lib/celluloid/rspec.rb +2 -0
  28. data/lib/vendor/celluloid/lib/celluloid/signals.rb +50 -0
  29. data/lib/vendor/celluloid/lib/celluloid/supervisor.rb +57 -0
  30. data/lib/vendor/celluloid/lib/celluloid/task.rb +73 -0
  31. data/lib/vendor/celluloid/lib/celluloid/tcp_server.rb +33 -0
  32. data/lib/vendor/celluloid/lib/celluloid/timers.rb +109 -0
  33. data/lib/vendor/celluloid/lib/celluloid/version.rb +4 -0
  34. data/lib/vendor/dataflow/dataflow.rb +124 -0
  35. data/lib/vendor/dataflow/dataflow/actor.rb +22 -0
  36. data/lib/vendor/dataflow/dataflow/equality.rb +44 -0
  37. data/lib/vendor/dataflow/dataflow/future_queue.rb +24 -0
  38. data/lib/vendor/dataflow/dataflow/port.rb +54 -0
  39. data/lib/vendor/open4/lib/open4.rb +432 -0
  40. data/lib/vendor/thor/lib/thor.rb +244 -0
  41. data/lib/vendor/thor/lib/thor/actions.rb +275 -0
  42. data/lib/vendor/thor/lib/thor/actions/create_file.rb +103 -0
  43. data/lib/vendor/thor/lib/thor/actions/directory.rb +91 -0
  44. data/lib/vendor/thor/lib/thor/actions/empty_directory.rb +134 -0
  45. data/lib/vendor/thor/lib/thor/actions/file_manipulation.rb +223 -0
  46. data/lib/vendor/thor/lib/thor/actions/inject_into_file.rb +104 -0
  47. data/lib/vendor/thor/lib/thor/base.rb +540 -0
  48. data/lib/vendor/thor/lib/thor/core_ext/file_binary_read.rb +9 -0
  49. data/lib/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  50. data/lib/vendor/thor/lib/thor/core_ext/ordered_hash.rb +100 -0
  51. data/lib/vendor/thor/lib/thor/error.rb +30 -0
  52. data/lib/vendor/thor/lib/thor/group.rb +271 -0
  53. data/lib/vendor/thor/lib/thor/invocation.rb +180 -0
  54. data/lib/vendor/thor/lib/thor/parser.rb +4 -0
  55. data/lib/vendor/thor/lib/thor/parser/argument.rb +67 -0
  56. data/lib/vendor/thor/lib/thor/parser/arguments.rb +150 -0
  57. data/lib/vendor/thor/lib/thor/parser/option.rb +128 -0
  58. data/lib/vendor/thor/lib/thor/parser/options.rb +169 -0
  59. data/lib/vendor/thor/lib/thor/rake_compat.rb +66 -0
  60. data/lib/vendor/thor/lib/thor/runner.rb +314 -0
  61. data/lib/vendor/thor/lib/thor/shell.rb +83 -0
  62. data/lib/vendor/thor/lib/thor/shell/basic.rb +239 -0
  63. data/lib/vendor/thor/lib/thor/shell/color.rb +108 -0
  64. data/lib/vendor/thor/lib/thor/task.rb +102 -0
  65. data/lib/vendor/thor/lib/thor/util.rb +230 -0
  66. data/lib/vendor/thor/lib/thor/version.rb +3 -0
  67. metadata +70 -10
@@ -0,0 +1,109 @@
1
+ module Celluloid
2
+ # Low precision timers implemented in pure Ruby
3
+ class Timers
4
+ def initialize
5
+ @timers = []
6
+ end
7
+
8
+ # Call the given block after the given interval
9
+ def add(interval, &block)
10
+ Timer.new(self, interval, block)
11
+ end
12
+
13
+ # Wait for the next timer and fire it
14
+ def wait
15
+ return if @timers.empty?
16
+
17
+ interval = wait_interval
18
+ sleep interval if interval >= Timer::QUANTUM
19
+ fire
20
+ end
21
+
22
+ # Interval to wait until when the next timer will fire
23
+ def wait_interval
24
+ @timers.first.time - Time.now unless empty?
25
+ end
26
+
27
+ # Fire all timers that are ready
28
+ def fire
29
+ return if @timers.empty?
30
+
31
+ time = Time.now + Timer::QUANTUM
32
+ while not empty? and time > @timers.first.time
33
+ timer = @timers.shift
34
+ timer.call
35
+ end
36
+ end
37
+
38
+ # Insert a timer into the active timers
39
+ def insert(timer)
40
+ @timers.insert(index(timer), timer)
41
+ end
42
+
43
+ # Remove a given timer from the set we're monitoring
44
+ def cancel(timer)
45
+ @timers.delete timer
46
+ end
47
+
48
+ # Are there any timers pending?
49
+ def empty?
50
+ @timers.empty?
51
+ end
52
+
53
+ # Index where a timer would be located in the sorted timers array
54
+ def index(timer)
55
+ l, r = 0, @timers.size - 1
56
+
57
+ while l <= r
58
+ m = (r + l) / 2
59
+ if timer < @timers.at(m)
60
+ r = m - 1
61
+ else
62
+ l = m + 1
63
+ end
64
+ end
65
+ l
66
+ end
67
+ end
68
+
69
+ # An individual timer set to fire a given proc at a given time
70
+ class Timer
71
+ include Comparable
72
+
73
+ # The timer system is guaranteed (at least by the specs) to be this precise
74
+ # during normal operation. Long blocking calls within actors will delay the
75
+ # firing of timers
76
+ QUANTUM = 0.02
77
+
78
+ attr_reader :interval, :time
79
+
80
+ def initialize(timers, interval, block)
81
+ @timers, @interval = timers, interval
82
+ @block = block
83
+
84
+ reset
85
+ end
86
+
87
+ def <=>(other)
88
+ @time <=> other.time
89
+ end
90
+
91
+ # Cancel this timer
92
+ def cancel
93
+ @timers.cancel self
94
+ end
95
+
96
+ # Reset this timer
97
+ def reset
98
+ @timers.cancel self if defined?(@time)
99
+ @time = Time.now + @interval
100
+ @timers.insert self
101
+ end
102
+
103
+ # Fire the block
104
+ def fire
105
+ @block.call
106
+ end
107
+ alias_method :call, :fire
108
+ end
109
+ end
@@ -0,0 +1,4 @@
1
+ module Celluloid
2
+ VERSION = '0.7.1'
3
+ def self.version; VERSION; end
4
+ end
@@ -0,0 +1,124 @@
1
+ require 'monitor'
2
+
3
+ module Dataflow
4
+ VERSION = "0.3.1"
5
+ class << self
6
+ attr_accessor :forker
7
+ end
8
+ self.forker = Thread.method(:fork)
9
+
10
+ def self.included(cls)
11
+ class << cls
12
+ def declare(*readers)
13
+ readers.each do |name|
14
+ class_eval <<-RUBY
15
+ def #{name}
16
+ return @__dataflow_#{name}__ if defined? @__dataflow_#{name}__
17
+ Variable::LOCK.synchronize { @__dataflow_#{name}__ ||= Variable.new }
18
+ end
19
+ RUBY
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ def local(&block)
26
+ return Variable.new unless block_given?
27
+ vars = Array.new(block.arity) { Variable.new }
28
+ block.call *vars
29
+ end
30
+
31
+ def unify(variable, value)
32
+ variable.__unify__ value
33
+ end
34
+
35
+ def by_need(&block)
36
+ Variable.new &block
37
+ end
38
+
39
+ def barrier(*variables)
40
+ variables.each{|v| v.__wait__ }
41
+ end
42
+
43
+ def flow(output=nil, &block)
44
+ Dataflow.forker.call do
45
+ result = block.call
46
+ unify output, result if output
47
+ end
48
+ end
49
+
50
+ def need_later(&block)
51
+ local do |future|
52
+ flow(future) { block.call }
53
+ future
54
+ end
55
+ end
56
+
57
+ extend self
58
+
59
+ # Note that this class uses instance variables directly rather than nicely
60
+ # initialized instance variables in get/set methods for memory and
61
+ # performance reasons
62
+ class Variable
63
+ # Briefly disable the warning we would get when undefining object_id.
64
+ # We actually rely on the ability to do that, so...
65
+ v = $VERBOSE; $VERBOSE = nil
66
+ instance_methods.each { |m| undef_method(m) unless m =~ /^__|instance_eval/ }
67
+ $VERBOSE = v # back to sanity
68
+ LOCK = Monitor.new
69
+ def initialize(&block) @__trigger__ = block if block_given? end
70
+
71
+ # Lazy-load conditions to be nice on memory usage
72
+ def __binding_condition__() @__binding_condition__ ||= LOCK.new_cond end
73
+
74
+ def __unify__(value)
75
+ LOCK.synchronize do
76
+ __activate_trigger__ if @__trigger__
77
+ if @__bound__
78
+ return @__value__.__unify__(value) if @__value__.__dataflow__? rescue nil
79
+ raise UnificationError, "#{@__value__.inspect} != #{value.inspect}" if self != value
80
+ else
81
+ @__value__ = value
82
+ @__bound__ = true
83
+ __binding_condition__.broadcast # wakeup all method callers
84
+ @__binding_condition__ = nil # GC
85
+ end
86
+ end
87
+ @__value__
88
+ end
89
+
90
+ def __activate_trigger__
91
+ @__value__ = @__trigger__.call
92
+ @__bound__ = true
93
+ @__trigger__ = nil # GC
94
+ end
95
+
96
+ def __wait__
97
+ LOCK.synchronize do
98
+ unless @__bound__
99
+ if @__trigger__
100
+ __activate_trigger__
101
+ else
102
+ __binding_condition__.wait
103
+ end
104
+ end
105
+ end unless @__bound__
106
+ end
107
+
108
+ def method_missing(name, *args, &block)
109
+ return "#<Dataflow::Variable:#{__id__} unbound>" if !@__bound__ && name == :inspect
110
+ __wait__
111
+ @__value__.__send__(name, *args, &block)
112
+ end
113
+
114
+ def __dataflow__?
115
+ true
116
+ end
117
+ end
118
+
119
+ UnificationError = Class.new StandardError
120
+ end
121
+
122
+ require "#{File.dirname(__FILE__)}/dataflow/port"
123
+ require "#{File.dirname(__FILE__)}/dataflow/actor"
124
+ require "#{File.dirname(__FILE__)}/dataflow/future_queue"
@@ -0,0 +1,22 @@
1
+ module Dataflow
2
+ class Actor < Thread
3
+ def initialize(&block)
4
+ @stream = Variable.new
5
+ @port = Port.new(@stream)
6
+ # Run this block in a new thread
7
+ super { instance_eval &block }
8
+ end
9
+
10
+ def send message
11
+ @port.send message
12
+ end
13
+
14
+ private
15
+
16
+ def receive
17
+ result = @stream.head
18
+ @stream = @stream.tail
19
+ result
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,44 @@
1
+ # The purpose of this file is to make equality use method calls in as
2
+ # many Ruby implementations as possible. If method calls are used,
3
+ # then equality operations using dataflow variaables becomes seemless
4
+ # I realize overriding core classes is a pretty nasty hack, but if you
5
+ # have a better idea that also passes the equality_specs then I'm all
6
+ # ears. Please run the rubyspec before committing changes to this file.
7
+
8
+ class Object
9
+ alias original_equality ==
10
+
11
+ def ==(other)
12
+ object_id == other.object_id
13
+ end
14
+ end
15
+
16
+ class Symbol
17
+ alias original_equality ==
18
+
19
+ def ==(other)
20
+ object_id == other.object_id
21
+ end
22
+ end
23
+
24
+ class Regexp
25
+ alias original_equality ==
26
+
27
+ if /lol/.respond_to?(:encoding)
28
+ def ==(other)
29
+ other.is_a?(Regexp) &&
30
+ casefold? == other.casefold? &&
31
+ encoding == other.encoding &&
32
+ options == other.options &&
33
+ source == other.source
34
+ end
35
+ else
36
+ def ==(other)
37
+ other.is_a?(Regexp) &&
38
+ casefold? == other.casefold? &&
39
+ kcode == other.kcode &&
40
+ options == other.options &&
41
+ source == other.source
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,24 @@
1
+ module Dataflow
2
+ class FutureQueue
3
+ include Dataflow
4
+ declare :push_port, :pop_port
5
+
6
+ def initialize
7
+ local do |pushed, popped|
8
+ unify push_port, Dataflow::Port.new(pushed)
9
+ unify pop_port, Dataflow::Port.new(popped)
10
+
11
+ Thread.new {
12
+ loop do
13
+ barrier pushed.head, popped.head
14
+ unify popped.head, pushed.head
15
+ pushed, popped = pushed.tail, popped.tail
16
+ end
17
+ }
18
+ end
19
+ end
20
+
21
+ def push(x) push_port.send x end
22
+ def pop(x) pop_port.send x end
23
+ end
24
+ end
@@ -0,0 +1,54 @@
1
+ require 'thread'
2
+
3
+ module Dataflow
4
+ class Port
5
+ include Dataflow
6
+ LOCK = Mutex.new
7
+
8
+ class Stream
9
+ include Dataflow
10
+ declare :head, :tail
11
+
12
+ # Defining each allows us to use the enumerable mixin
13
+ # None of the list can be garbage collected less the head is
14
+ # garbage collected, so it will grow indefinitely even though
15
+ # the function isn't recursive.
16
+ include Enumerable
17
+ def each
18
+ s = self
19
+ loop do
20
+ yield s.head
21
+ s = s.tail
22
+ end
23
+ end
24
+
25
+ # Backported Enumerable#take for any 1.8.6 compatible Ruby
26
+ unless method_defined?(:take)
27
+ def take(num)
28
+ result = []
29
+ each_with_index do |x, i|
30
+ return result if num == i
31
+ result << x
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ # Create a stream object, bind it to the input variable
38
+ # Instance variables are necessary because @end is state
39
+ def initialize(x)
40
+ @end = Stream.new
41
+ unify x, @end
42
+ end
43
+
44
+ # This needs to be synchronized because it uses @end as state
45
+ def send value
46
+ LOCK.synchronize do
47
+ unify @end.head, value
48
+ unify @end.tail, Stream.new
49
+ @end = @end.tail
50
+ end
51
+ end
52
+ end
53
+ end
54
+
@@ -0,0 +1,432 @@
1
+ # vim: ts=2:sw=2:sts=2:et:fdm=marker
2
+ require 'fcntl'
3
+ require 'timeout'
4
+ require 'thread'
5
+
6
+ module Open4
7
+ VERSION = '1.3.0'
8
+ def self.version() VERSION end
9
+
10
+ class Error < ::StandardError; end
11
+
12
+ def pfork4(fun, &b)
13
+ Open4.do_popen(b, :block) do |ps_read, _|
14
+ ps_read.close
15
+ begin
16
+ fun.call
17
+ rescue SystemExit => e
18
+ # Make it seem to the caller that calling Kernel#exit in +fun+ kills
19
+ # the child process normally. Kernel#exit! bypasses this rescue
20
+ # block.
21
+ exit! e.status
22
+ else
23
+ exit! 0
24
+ end
25
+ end
26
+ end
27
+ module_function :pfork4
28
+
29
+ def popen4(*cmd, &b)
30
+ Open4.do_popen(b, :init) do |ps_read, ps_write|
31
+ ps_read.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
32
+ ps_write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
33
+ exec(*cmd)
34
+ raise 'forty-two' # Is this really needed?
35
+ end
36
+ end
37
+ alias open4 popen4
38
+ module_function :popen4
39
+ module_function :open4
40
+
41
+ def popen4ext(closefds=false, *cmd, &b)
42
+ Open4.do_popen(b, :init, closefds) do |ps_read, ps_write|
43
+ ps_read.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
44
+ ps_write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
45
+ exec(*cmd)
46
+ raise 'forty-two' # Is this really needed?
47
+ end
48
+ end
49
+ module_function :popen4ext
50
+
51
+ def self.do_popen(b = nil, exception_propagation_at = nil, closefds=false, &cmd)
52
+ pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
53
+
54
+ verbose = $VERBOSE
55
+ begin
56
+ $VERBOSE = nil
57
+
58
+ cid = fork {
59
+ if closefds
60
+ exlist = [0, 1, 2] | [pw,pr,pe,ps].map{|p| [p.first.fileno, p.last.fileno] }.flatten
61
+ ObjectSpace.each_object(IO){|io|
62
+ io.close if (not io.closed?) and (not exlist.include? io.fileno)
63
+ }
64
+ end
65
+
66
+ pw.last.close
67
+ STDIN.reopen pw.first
68
+ pw.first.close
69
+
70
+ pr.first.close
71
+ STDOUT.reopen pr.last
72
+ pr.last.close
73
+
74
+ pe.first.close
75
+ STDERR.reopen pe.last
76
+ pe.last.close
77
+
78
+ STDOUT.sync = STDERR.sync = true
79
+
80
+ begin
81
+ cmd.call(ps)
82
+ rescue Exception => e
83
+ Marshal.dump(e, ps.last)
84
+ ps.last.flush
85
+ ensure
86
+ ps.last.close unless ps.last.closed?
87
+ end
88
+
89
+ exit!
90
+ }
91
+ ensure
92
+ $VERBOSE = verbose
93
+ end
94
+
95
+ [ pw.first, pr.last, pe.last, ps.last ].each { |fd| fd.close }
96
+
97
+ Open4.propagate_exception cid, ps.first if exception_propagation_at == :init
98
+
99
+ pw.last.sync = true
100
+
101
+ pi = [ pw.last, pr.first, pe.first ]
102
+
103
+ begin
104
+ return [cid, *pi] unless b
105
+
106
+ begin
107
+ b.call(cid, *pi)
108
+ ensure
109
+ pi.each { |fd| fd.close unless fd.closed? }
110
+ end
111
+
112
+ Open4.propagate_exception cid, ps.first if exception_propagation_at == :block
113
+
114
+ Process.waitpid2(cid).last
115
+ ensure
116
+ ps.first.close unless ps.first.closed?
117
+ end
118
+ end
119
+
120
+ def self.propagate_exception(cid, ps_read)
121
+ e = Marshal.load ps_read
122
+ raise Exception === e ? e : "unknown failure!"
123
+ rescue EOFError
124
+ # Child process did not raise exception.
125
+ rescue
126
+ # Child process raised exception; wait it in order to avoid a zombie.
127
+ Process.waitpid2 cid
128
+ raise
129
+ ensure
130
+ ps_read.close
131
+ end
132
+
133
+ class SpawnError < Error
134
+ attr 'cmd'
135
+ attr 'status'
136
+ attr 'signals'
137
+ def exitstatus
138
+ @status.exitstatus
139
+ end
140
+ def initialize cmd, status
141
+ @cmd, @status = cmd, status
142
+ @signals = {}
143
+ if status.signaled?
144
+ @signals['termsig'] = status.termsig
145
+ @signals['stopsig'] = status.stopsig
146
+ end
147
+ sigs = @signals.map{|k,v| "#{ k }:#{ v.inspect }"}.join(' ')
148
+ super "cmd <#{ cmd }> failed with status <#{ exitstatus.inspect }> signals <#{ sigs }>"
149
+ end
150
+ end
151
+
152
+ class ThreadEnsemble
153
+ attr 'threads'
154
+
155
+ def initialize cid
156
+ @cid, @threads, @argv, @done, @running = cid, [], [], Queue.new, false
157
+ @killed = false
158
+ end
159
+
160
+ def add_thread *a, &b
161
+ @running ? raise : (@argv << [a, b])
162
+ end
163
+
164
+ #
165
+ # take down process more nicely
166
+ #
167
+ def killall
168
+ c = Thread.critical
169
+ return nil if @killed
170
+ Thread.critical = true
171
+ (@threads - [Thread.current]).each{|t| t.kill rescue nil}
172
+ @killed = true
173
+ ensure
174
+ Thread.critical = c
175
+ end
176
+
177
+ def run
178
+ @running = true
179
+
180
+ begin
181
+ @argv.each do |a, b|
182
+ @threads << Thread.new(*a) do |*a|
183
+ begin
184
+ b[*a]
185
+ ensure
186
+ killall rescue nil if $!
187
+ @done.push Thread.current
188
+ end
189
+ end
190
+ end
191
+ rescue
192
+ killall
193
+ raise
194
+ ensure
195
+ all_done
196
+ end
197
+
198
+ @threads.map{|t| t.value}
199
+ end
200
+
201
+ def all_done
202
+ @threads.size.times{ @done.pop }
203
+ end
204
+ end
205
+
206
+ def to timeout = nil
207
+ Timeout.timeout(timeout){ yield }
208
+ end
209
+ module_function :to
210
+
211
+ def new_thread *a, &b
212
+ cur = Thread.current
213
+ Thread.new(*a) do |*a|
214
+ begin
215
+ b[*a]
216
+ rescue Exception => e
217
+ cur.raise e
218
+ end
219
+ end
220
+ end
221
+ module_function :new_thread
222
+
223
+ def getopts opts = {}
224
+ lambda do |*args|
225
+ keys, default, ignored = args
226
+ catch(:opt) do
227
+ [keys].flatten.each do |key|
228
+ [key, key.to_s, key.to_s.intern].each do |key|
229
+ throw :opt, opts[key] if opts.has_key?(key)
230
+ end
231
+ end
232
+ default
233
+ end
234
+ end
235
+ end
236
+ module_function :getopts
237
+
238
+ def relay src, dst = nil, t = nil
239
+ send_dst =
240
+ if dst.respond_to?(:call)
241
+ lambda{|buf| dst.call(buf)}
242
+ elsif dst.respond_to?(:<<)
243
+ lambda{|buf| dst << buf }
244
+ else
245
+ lambda{|buf| buf }
246
+ end
247
+
248
+ unless src.nil?
249
+ if src.respond_to? :gets
250
+ while buf = to(t){ src.gets }
251
+ send_dst[buf]
252
+ end
253
+
254
+ elsif src.respond_to? :each
255
+ q = Queue.new
256
+ th = nil
257
+
258
+ timer_set = lambda do |t|
259
+ th = new_thread{ to(t){ q.pop } }
260
+ end
261
+
262
+ timer_cancel = lambda do |t|
263
+ th.kill if th rescue nil
264
+ end
265
+
266
+ timer_set[t]
267
+ begin
268
+ src.each do |buf|
269
+ timer_cancel[t]
270
+ send_dst[buf]
271
+ timer_set[t]
272
+ end
273
+ ensure
274
+ timer_cancel[t]
275
+ end
276
+
277
+ elsif src.respond_to? :read
278
+ buf = to(t){ src.read }
279
+ send_dst[buf]
280
+
281
+ else
282
+ buf = to(t){ src.to_s }
283
+ send_dst[buf]
284
+ end
285
+ end
286
+ end
287
+ module_function :relay
288
+
289
+ def spawn arg, *argv
290
+ argv.unshift(arg)
291
+ opts = ((argv.size > 1 and Hash === argv.last) ? argv.pop : {})
292
+ argv.flatten!
293
+ cmd = argv.join(' ')
294
+
295
+
296
+ getopt = getopts opts
297
+
298
+ ignore_exit_failure = getopt[ 'ignore_exit_failure', getopt['quiet', false] ]
299
+ ignore_exec_failure = getopt[ 'ignore_exec_failure', !getopt['raise', true] ]
300
+ exitstatus = getopt[ %w( exitstatus exit_status status ) ]
301
+ stdin = getopt[ %w( stdin in i 0 ) << 0 ]
302
+ stdout = getopt[ %w( stdout out o 1 ) << 1 ]
303
+ stderr = getopt[ %w( stderr err e 2 ) << 2 ]
304
+ pid = getopt[ 'pid' ]
305
+ timeout = getopt[ %w( timeout spawn_timeout ) ]
306
+ stdin_timeout = getopt[ %w( stdin_timeout ) ]
307
+ stdout_timeout = getopt[ %w( stdout_timeout io_timeout ) ]
308
+ stderr_timeout = getopt[ %w( stderr_timeout ) ]
309
+ status = getopt[ %w( status ) ]
310
+ cwd = getopt[ %w( cwd dir ) ]
311
+
312
+ exitstatus =
313
+ case exitstatus
314
+ when TrueClass, FalseClass
315
+ ignore_exit_failure = true if exitstatus
316
+ [0]
317
+ else
318
+ [*(exitstatus || 0)].map{|i| Integer i}
319
+ end
320
+
321
+ stdin ||= '' if stdin_timeout
322
+ stdout ||= '' if stdout_timeout
323
+ stderr ||= '' if stderr_timeout
324
+
325
+ started = false
326
+
327
+ status =
328
+ begin
329
+ chdir(cwd) do
330
+ Timeout::timeout(timeout) do
331
+ popen4(*argv) do |c, i, o, e|
332
+ started = true
333
+
334
+ %w( replace pid= << push update ).each do |msg|
335
+ break(pid.send(msg, c)) if pid.respond_to? msg
336
+ end
337
+
338
+ te = ThreadEnsemble.new c
339
+
340
+ te.add_thread(i, stdin) do |i, stdin|
341
+ relay stdin, i, stdin_timeout
342
+ i.close rescue nil
343
+ end
344
+
345
+ te.add_thread(o, stdout) do |o, stdout|
346
+ relay o, stdout, stdout_timeout
347
+ end
348
+
349
+ te.add_thread(e, stderr) do |o, stderr|
350
+ relay e, stderr, stderr_timeout
351
+ end
352
+
353
+ te.run
354
+ end
355
+ end
356
+ end
357
+ rescue
358
+ raise unless(not started and ignore_exec_failure)
359
+ end
360
+
361
+ raise SpawnError.new(cmd, status) unless
362
+ (ignore_exit_failure or (status.nil? and ignore_exec_failure) or exitstatus.include?(status.exitstatus))
363
+
364
+ status
365
+ end
366
+ module_function :spawn
367
+
368
+ def chdir cwd, &block
369
+ return(block.call Dir.pwd) unless cwd
370
+ Dir.chdir cwd, &block
371
+ end
372
+ module_function :chdir
373
+
374
+ def background arg, *argv
375
+ require 'thread'
376
+ q = Queue.new
377
+ opts = { 'pid' => q, :pid => q }
378
+ case argv.last
379
+ when Hash
380
+ argv.last.update opts
381
+ else
382
+ argv.push opts
383
+ end
384
+ thread = Thread.new(arg, argv){|arg, argv| spawn arg, *argv}
385
+ sc = class << thread; self; end
386
+ sc.module_eval {
387
+ define_method(:pid){ @pid ||= q.pop }
388
+ define_method(:spawn_status){ @spawn_status ||= value }
389
+ define_method(:exitstatus){ @exitstatus ||= spawn_status.exitstatus }
390
+ }
391
+ thread
392
+ end
393
+ alias bg background
394
+ module_function :background
395
+ module_function :bg
396
+
397
+ def maim pid, opts = {}
398
+ getopt = getopts opts
399
+ sigs = getopt[ 'signals', %w(SIGTERM SIGQUIT SIGKILL) ]
400
+ suspend = getopt[ 'suspend', 4 ]
401
+ pid = Integer pid
402
+ existed = false
403
+ sigs.each do |sig|
404
+ begin
405
+ Process.kill sig, pid
406
+ existed = true
407
+ rescue Errno::ESRCH
408
+ return(existed ? nil : true)
409
+ end
410
+ return true unless alive? pid
411
+ sleep suspend
412
+ return true unless alive? pid
413
+ end
414
+ return(not alive?(pid))
415
+ end
416
+ module_function :maim
417
+
418
+ def alive pid
419
+ pid = Integer pid
420
+ begin
421
+ Process.kill 0, pid
422
+ true
423
+ rescue Errno::ESRCH
424
+ false
425
+ end
426
+ end
427
+ alias alive? alive
428
+ module_function :alive
429
+ module_function :'alive?'
430
+ end
431
+
432
+ def open4(*cmd, &b) cmd.size == 0 ? Open4 : Open4::popen4(*cmd, &b) end