mt-libuv 4.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.gitmodules +6 -0
- data/.rspec +1 -0
- data/.travis.yml +24 -0
- data/Gemfile +9 -0
- data/LICENSE +24 -0
- data/README.md +195 -0
- data/Rakefile +31 -0
- data/ext/README.md +6 -0
- data/ext/Rakefile +28 -0
- data/lib/mt-libuv/async.rb +51 -0
- data/lib/mt-libuv/check.rb +59 -0
- data/lib/mt-libuv/coroutines.rb +79 -0
- data/lib/mt-libuv/dns.rb +98 -0
- data/lib/mt-libuv/error.rb +88 -0
- data/lib/mt-libuv/ext/ext.rb +322 -0
- data/lib/mt-libuv/ext/platform/darwin_x64.rb +61 -0
- data/lib/mt-libuv/ext/platform/unix.rb +69 -0
- data/lib/mt-libuv/ext/platform/windows.rb +83 -0
- data/lib/mt-libuv/ext/tasks/mac.rb +24 -0
- data/lib/mt-libuv/ext/tasks/unix.rb +42 -0
- data/lib/mt-libuv/ext/tasks/win.rb +29 -0
- data/lib/mt-libuv/ext/tasks.rb +27 -0
- data/lib/mt-libuv/ext/types.rb +253 -0
- data/lib/mt-libuv/fiber_pool.rb +83 -0
- data/lib/mt-libuv/file.rb +309 -0
- data/lib/mt-libuv/filesystem.rb +263 -0
- data/lib/mt-libuv/fs_event.rb +37 -0
- data/lib/mt-libuv/handle.rb +108 -0
- data/lib/mt-libuv/idle.rb +59 -0
- data/lib/mt-libuv/mixins/accessors.rb +41 -0
- data/lib/mt-libuv/mixins/assertions.rb +25 -0
- data/lib/mt-libuv/mixins/fs_checks.rb +96 -0
- data/lib/mt-libuv/mixins/listener.rb +69 -0
- data/lib/mt-libuv/mixins/net.rb +42 -0
- data/lib/mt-libuv/mixins/resource.rb +30 -0
- data/lib/mt-libuv/mixins/stream.rb +276 -0
- data/lib/mt-libuv/pipe.rb +217 -0
- data/lib/mt-libuv/prepare.rb +59 -0
- data/lib/mt-libuv/q.rb +475 -0
- data/lib/mt-libuv/reactor.rb +567 -0
- data/lib/mt-libuv/signal.rb +62 -0
- data/lib/mt-libuv/spawn.rb +113 -0
- data/lib/mt-libuv/tcp.rb +465 -0
- data/lib/mt-libuv/timer.rb +107 -0
- data/lib/mt-libuv/tty.rb +42 -0
- data/lib/mt-libuv/udp.rb +302 -0
- data/lib/mt-libuv/version.rb +5 -0
- data/lib/mt-libuv/work.rb +86 -0
- data/lib/mt-libuv.rb +80 -0
- data/mt-libuv.gemspec +62 -0
- data/spec/async_spec.rb +67 -0
- data/spec/coroutines_spec.rb +121 -0
- data/spec/cpu_spec.rb +10 -0
- data/spec/defer_spec.rb +906 -0
- data/spec/dns_spec.rb +110 -0
- data/spec/dsl_spec.rb +43 -0
- data/spec/filesystem_spec.rb +270 -0
- data/spec/idle_spec.rb +44 -0
- data/spec/pipe_spec.rb +151 -0
- data/spec/spawn_spec.rb +119 -0
- data/spec/tcp_spec.rb +272 -0
- data/spec/test.sh +4 -0
- data/spec/test_fail.sh +3 -0
- data/spec/test_read.sh +3 -0
- data/spec/timer_spec.rb +14 -0
- data/spec/udp_spec.rb +73 -0
- data/spec/zen_spec.rb +34 -0
- metadata +196 -0
data/lib/mt-libuv/q.rb
ADDED
@@ -0,0 +1,475 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MTLibuv
|
4
|
+
module Q
|
5
|
+
|
6
|
+
# @abstract
|
7
|
+
class Promise
|
8
|
+
private_class_method :new
|
9
|
+
|
10
|
+
# This allows subclasses to make use of the catch feature
|
11
|
+
alias_method :ruby_catch, :catch
|
12
|
+
|
13
|
+
# Allows a backtrace to be included in any errors
|
14
|
+
attr_accessor :trace
|
15
|
+
|
16
|
+
#
|
17
|
+
# regardless of when the promise was or will be resolved / rejected, calls
|
18
|
+
# the error callback asynchronously if the promise is rejected.
|
19
|
+
#
|
20
|
+
# @param [Proc, &blk] callbacks error, error_block
|
21
|
+
# @return [Promise] Returns an unresolved promise for chaining
|
22
|
+
def catch(&blk)
|
23
|
+
self.then(nil, blk)
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
def progress(&blk)
|
28
|
+
self.then(nil, nil, blk)
|
29
|
+
end
|
30
|
+
|
31
|
+
# A future that provides the value or raises an error if a rejection occurs
|
32
|
+
def value
|
33
|
+
::MTLibuv.co(self)
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# allows you to observe either the fulfillment or rejection of a promise, but to do so
|
38
|
+
# without modifying the final value. This is useful to release resources or do some
|
39
|
+
# clean-up that needs to be done whether the promise was rejected or resolved.
|
40
|
+
#
|
41
|
+
# @param [Proc, &blk] callbacks finally, finally_block
|
42
|
+
# @return [Promise] Returns an unresolved promise for chaining
|
43
|
+
def finally
|
44
|
+
handleCallback = lambda { |value, isResolved|
|
45
|
+
callbackOutput = nil
|
46
|
+
begin
|
47
|
+
callbackOutput = yield
|
48
|
+
rescue Exception => e
|
49
|
+
@reactor.log e, 'performing promise finally callback', @trace
|
50
|
+
return make_promise(e, false, @reactor)
|
51
|
+
end
|
52
|
+
|
53
|
+
if callbackOutput.is_a?(Promise)
|
54
|
+
return callbackOutput.then(proc {
|
55
|
+
make_promise(value, isResolved, @reactor)
|
56
|
+
}, proc { |err|
|
57
|
+
make_promise(err, false, @reactor)
|
58
|
+
})
|
59
|
+
else
|
60
|
+
return make_promise(value, isResolved, @reactor)
|
61
|
+
end
|
62
|
+
}
|
63
|
+
|
64
|
+
self.then(proc {|val|
|
65
|
+
handleCallback.call(val, true)
|
66
|
+
}, proc{|err|
|
67
|
+
handleCallback.call(err, false)
|
68
|
+
})
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
def make_promise(value, resolved, reactor)
|
74
|
+
result = Q.defer(reactor)
|
75
|
+
if (resolved)
|
76
|
+
result.resolve(value)
|
77
|
+
else
|
78
|
+
result.reject(value)
|
79
|
+
end
|
80
|
+
result.promise
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
#
|
86
|
+
# A new promise instance is created when a deferred instance is created and can be
|
87
|
+
# retrieved by calling deferred.promise
|
88
|
+
#
|
89
|
+
class DeferredPromise < Promise
|
90
|
+
public_class_method :new
|
91
|
+
|
92
|
+
def initialize(reactor, defer)
|
93
|
+
raise ArgumentError unless defer.is_a?(Deferred)
|
94
|
+
super()
|
95
|
+
|
96
|
+
@reactor = reactor
|
97
|
+
@defer = defer
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# regardless of when the promise was or will be resolved / rejected, calls one of
|
102
|
+
# the success or error callbacks asynchronously as soon as the result is available.
|
103
|
+
# The callbacks are called with a single argument, the result or rejection reason.
|
104
|
+
#
|
105
|
+
# @param [Proc, Proc, Proc, &blk] callbacks error, success, progress, success_block
|
106
|
+
# @return [Promise] Returns an unresolved promise for chaining
|
107
|
+
def then(callback = nil, errback = nil, progback = nil)
|
108
|
+
result = Q.defer(@reactor)
|
109
|
+
callback = Proc.new if block_given?
|
110
|
+
|
111
|
+
wrappedCallback = proc { |val|
|
112
|
+
begin
|
113
|
+
result.resolve(callback ? callback.call(val) : val)
|
114
|
+
rescue Exception => e
|
115
|
+
result.reject(e)
|
116
|
+
@reactor.log e, 'performing promise resolution callback', @trace
|
117
|
+
end
|
118
|
+
}
|
119
|
+
|
120
|
+
wrappedErrback = proc { |reason|
|
121
|
+
begin
|
122
|
+
result.resolve(errback ? errback.call(reason) : Q.reject(@reactor, reason))
|
123
|
+
rescue Exception => e
|
124
|
+
result.reject(e)
|
125
|
+
@reactor.log e, 'performing promise rejection callback', @trace
|
126
|
+
end
|
127
|
+
}
|
128
|
+
|
129
|
+
wrappedProgback = proc { |*progress|
|
130
|
+
begin
|
131
|
+
result.notify(progback ? progback.call(*progress) : progress)
|
132
|
+
rescue Exception => e
|
133
|
+
@reactor.log e, 'performing promise progress callback', @trace
|
134
|
+
end
|
135
|
+
}
|
136
|
+
|
137
|
+
#
|
138
|
+
# Schedule as we are touching shared state
|
139
|
+
# Everything else is locally scoped
|
140
|
+
#
|
141
|
+
@reactor.schedule do
|
142
|
+
pending_array = pending
|
143
|
+
|
144
|
+
if pending_array.nil?
|
145
|
+
reference.then(wrappedCallback, wrappedErrback, wrappedProgback)
|
146
|
+
else
|
147
|
+
pending_array << [wrappedCallback, wrappedErrback, wrappedProgback]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
result.promise
|
152
|
+
end
|
153
|
+
|
154
|
+
def resolved?
|
155
|
+
pending.nil?
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
|
162
|
+
def pending
|
163
|
+
@defer.pending
|
164
|
+
end
|
165
|
+
|
166
|
+
def reference
|
167
|
+
@defer.reference
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
|
173
|
+
class ResolvedPromise < Promise
|
174
|
+
public_class_method :new
|
175
|
+
|
176
|
+
def initialize(reactor, response, error = false)
|
177
|
+
raise ArgumentError if error && response.is_a?(Promise)
|
178
|
+
super()
|
179
|
+
|
180
|
+
@reactor = reactor
|
181
|
+
@error = error
|
182
|
+
@response = response
|
183
|
+
end
|
184
|
+
|
185
|
+
def then(callback = nil, errback = nil, progback = nil)
|
186
|
+
result = Q.defer(@reactor)
|
187
|
+
callback = Proc.new if block_given?
|
188
|
+
|
189
|
+
@reactor.next_tick {
|
190
|
+
if @error
|
191
|
+
begin
|
192
|
+
result.resolve(errback ? errback.call(@response) : Q.reject(@reactor, @response))
|
193
|
+
rescue Exception => e
|
194
|
+
result.reject(e)
|
195
|
+
@reactor.log e, 'performing promise rejection callback', @trace
|
196
|
+
end
|
197
|
+
else
|
198
|
+
begin
|
199
|
+
result.resolve(callback ? callback.call(@response) : @response)
|
200
|
+
rescue Exception => e
|
201
|
+
result.reject(e)
|
202
|
+
@reactor.log e, 'performing promise resolution callback', @trace
|
203
|
+
end
|
204
|
+
end
|
205
|
+
}
|
206
|
+
|
207
|
+
result.promise
|
208
|
+
end
|
209
|
+
|
210
|
+
def resolved?
|
211
|
+
true
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
|
216
|
+
#
|
217
|
+
# The purpose of the deferred object is to expose the associated Promise instance as well
|
218
|
+
# as APIs that can be used for signalling the successful or unsuccessful completion of a task.
|
219
|
+
#
|
220
|
+
class Deferred
|
221
|
+
include Q
|
222
|
+
|
223
|
+
def initialize(reactor)
|
224
|
+
super()
|
225
|
+
|
226
|
+
@pending = []
|
227
|
+
@reference = nil
|
228
|
+
@reactor = reactor
|
229
|
+
end
|
230
|
+
|
231
|
+
attr_reader :pending, :reference
|
232
|
+
|
233
|
+
#
|
234
|
+
# resolves the derived promise with the value. If the value is a rejection constructed via
|
235
|
+
# Q.reject, the promise will be rejected instead.
|
236
|
+
#
|
237
|
+
# @param [Object] val constant, message or an object representing the result.
|
238
|
+
def resolve(val = nil)
|
239
|
+
@reactor.schedule do
|
240
|
+
if not @pending.nil?
|
241
|
+
callbacks = @pending
|
242
|
+
@pending = nil
|
243
|
+
@reference = ref(@reactor, val)
|
244
|
+
|
245
|
+
if callbacks.length > 0
|
246
|
+
callbacks.each do |callback|
|
247
|
+
@reference.then(callback[0], callback[1], callback[2])
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
self
|
253
|
+
end
|
254
|
+
|
255
|
+
#
|
256
|
+
# rejects the derived promise with the reason. This is equivalent to resolving it with a rejection
|
257
|
+
# constructed via Q.reject.
|
258
|
+
#
|
259
|
+
# @param [Object] reason constant, message, exception or an object representing the rejection reason.
|
260
|
+
def reject(reason = nil)
|
261
|
+
resolve(Q.reject(@reactor, reason))
|
262
|
+
end
|
263
|
+
|
264
|
+
#
|
265
|
+
# Creates a promise object associated with this deferred
|
266
|
+
#
|
267
|
+
def promise
|
268
|
+
@promise ||= DeferredPromise.new(@reactor, self)
|
269
|
+
@promise # Should only ever be one per deferred
|
270
|
+
end
|
271
|
+
|
272
|
+
#
|
273
|
+
# Provides an asynchronous callback mechanism
|
274
|
+
#
|
275
|
+
# @param [*Object] data you would like to send down the promise chain.
|
276
|
+
def notify(*args)
|
277
|
+
@reactor.schedule do # just in case we are on a different event reactor
|
278
|
+
if @pending && @pending.length > 0
|
279
|
+
callbacks = @pending
|
280
|
+
@reactor.next_tick do
|
281
|
+
callbacks.each do |callback|
|
282
|
+
callback[2].call(*args)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
self
|
288
|
+
end
|
289
|
+
|
290
|
+
def resolved?
|
291
|
+
@pending.nil?
|
292
|
+
end
|
293
|
+
|
294
|
+
def value
|
295
|
+
::MTLibuv.co(self.promise)
|
296
|
+
end
|
297
|
+
|
298
|
+
# Overwrite to prevent inspecting errors hanging the VM
|
299
|
+
def inspect
|
300
|
+
if @pending.nil?
|
301
|
+
"#<#{self.class}:0x#{self.__id__.to_s(16)} @reactor=#{@reactor.inspect} @reference=#{@reference.inspect}>"
|
302
|
+
else
|
303
|
+
"#<#{self.class}:0x#{self.__id__.to_s(16)} @reactor=#{@reactor.inspect} @pending.count=#{@pending.length}>"
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
|
309
|
+
|
310
|
+
|
311
|
+
|
312
|
+
|
313
|
+
#
|
314
|
+
# Creates a Deferred object which represents a task which will finish in the future.
|
315
|
+
#
|
316
|
+
# @return [Deferred] Returns a new instance of Deferred
|
317
|
+
def defer(reactor)
|
318
|
+
return Deferred.new(reactor)
|
319
|
+
end
|
320
|
+
|
321
|
+
|
322
|
+
#
|
323
|
+
# Creates a promise that is resolved as rejected with the specified reason. This api should be
|
324
|
+
# used to forward rejection in a chain of promises. If you are dealing with the last promise in
|
325
|
+
# a promise chain, you don't need to worry about it.
|
326
|
+
#
|
327
|
+
# When comparing deferreds/promises to the familiar behaviour of try/catch/throw, think of
|
328
|
+
# reject as the raise keyword in Ruby. This also means that if you "catch" an error via
|
329
|
+
# a promise error callback and you want to forward the error to the promise derived from the
|
330
|
+
# current promise, you have to "rethrow" the error by returning a rejection constructed via
|
331
|
+
# reject.
|
332
|
+
#
|
333
|
+
# @example handling rejections
|
334
|
+
#
|
335
|
+
# #!/usr/bin/env ruby
|
336
|
+
#
|
337
|
+
# require 'rubygems' # or use Bundler.setup
|
338
|
+
# require 'em-promise'
|
339
|
+
#
|
340
|
+
# promiseB = promiseA.then(lambda {|reason|
|
341
|
+
# # error: handle the error if possible and resolve promiseB with newPromiseOrValue,
|
342
|
+
# # otherwise forward the rejection to promiseB
|
343
|
+
# if canHandle(reason)
|
344
|
+
# # handle the error and recover
|
345
|
+
# return newPromiseOrValue
|
346
|
+
# end
|
347
|
+
# return Q.reject(reactor, reason)
|
348
|
+
# }, lambda {|result|
|
349
|
+
# # success: do something and resolve promiseB with the old or a new result
|
350
|
+
# return result
|
351
|
+
# })
|
352
|
+
#
|
353
|
+
# @param [Object] reason constant, message, exception or an object representing the rejection reason.
|
354
|
+
# @return [Promise] Returns a promise that was already resolved as rejected with the reason
|
355
|
+
def reject(reactor, reason = nil)
|
356
|
+
return ResolvedPromise.new(reactor, reason, true) # A resolved failed promise
|
357
|
+
end
|
358
|
+
|
359
|
+
#
|
360
|
+
# Combines multiple promises into a single promise that is resolved when all of the input
|
361
|
+
# promises are resolved.
|
362
|
+
#
|
363
|
+
# @param [*Promise] Promises a number of promises that will be combined into a single promise
|
364
|
+
# @return [Promise] Returns a single promise that will be resolved with an array of values,
|
365
|
+
# each value corresponding to the promise at the same index in the `promises` array. If any of
|
366
|
+
# the promises is resolved with a rejection, this resulting promise will be resolved with the
|
367
|
+
# same rejection.
|
368
|
+
def all(reactor, *promises)
|
369
|
+
deferred = Q.defer(reactor)
|
370
|
+
promises = promises.flatten
|
371
|
+
counter = promises.length
|
372
|
+
results = []
|
373
|
+
|
374
|
+
if counter > 0
|
375
|
+
promises.each_index do |index|
|
376
|
+
ref(reactor, promises[index]).then(proc {|result|
|
377
|
+
if results[index].nil?
|
378
|
+
results[index] = result
|
379
|
+
counter -= 1
|
380
|
+
deferred.resolve(results) if counter <= 0
|
381
|
+
end
|
382
|
+
result
|
383
|
+
}, proc {|reason|
|
384
|
+
if results[index].nil?
|
385
|
+
deferred.reject(reason)
|
386
|
+
end
|
387
|
+
Q.reject(@reactor, reason) # Don't modify result
|
388
|
+
})
|
389
|
+
end
|
390
|
+
else
|
391
|
+
deferred.resolve(results)
|
392
|
+
end
|
393
|
+
|
394
|
+
return deferred.promise
|
395
|
+
end
|
396
|
+
|
397
|
+
|
398
|
+
#
|
399
|
+
# Combines multiple promises into a single promise that is resolved when any of the input
|
400
|
+
# promises are resolved.
|
401
|
+
#
|
402
|
+
# @param [*Promise] Promises a number of promises that will be combined into a single promise
|
403
|
+
# @return [Promise] Returns a single promise
|
404
|
+
def any(reactor, *promises)
|
405
|
+
deferred = Q.defer(reactor)
|
406
|
+
promises = promises.flatten
|
407
|
+
if promises.length > 0
|
408
|
+
promises.each_index do |index|
|
409
|
+
ref(reactor, promises[index]).then(proc { |result|
|
410
|
+
deferred.resolve(result)
|
411
|
+
}, proc { |reason|
|
412
|
+
deferred.reject(reason)
|
413
|
+
Q.reject(@reactor, reason) # Don't modify result
|
414
|
+
})
|
415
|
+
end
|
416
|
+
else
|
417
|
+
deferred.resolve(true)
|
418
|
+
end
|
419
|
+
deferred.promise
|
420
|
+
end
|
421
|
+
|
422
|
+
|
423
|
+
#
|
424
|
+
# Combines multiple promises into a single promise that is resolved when all of the input
|
425
|
+
# promises are resolved or rejected.
|
426
|
+
#
|
427
|
+
# @param [*Promise] Promises a number of promises that will be combined into a single promise
|
428
|
+
# @return [Promise] Returns a single promise that will be resolved with an array of values,
|
429
|
+
# each [result, wasResolved] value pair corresponding to a at the same index in the `promises` array.
|
430
|
+
def self.finally(reactor, *promises)
|
431
|
+
deferred = Q.defer(reactor)
|
432
|
+
promises = promises.flatten
|
433
|
+
counter = promises.length
|
434
|
+
results = []
|
435
|
+
|
436
|
+
if counter > 0
|
437
|
+
promises.each_index do |index|
|
438
|
+
ref(reactor, promises[index]).then(proc {|result|
|
439
|
+
if results[index].nil?
|
440
|
+
results[index] = [result, true]
|
441
|
+
counter -= 1
|
442
|
+
deferred.resolve(results) if counter <= 0
|
443
|
+
end
|
444
|
+
result
|
445
|
+
}, proc {|reason|
|
446
|
+
if results[index].nil?
|
447
|
+
results[index] = [reason, false]
|
448
|
+
counter -= 1
|
449
|
+
deferred.resolve(results) if counter <= 0
|
450
|
+
end
|
451
|
+
Q.reject(@reactor, reason) # Don't modify result
|
452
|
+
})
|
453
|
+
end
|
454
|
+
else
|
455
|
+
deferred.resolve(results)
|
456
|
+
end
|
457
|
+
|
458
|
+
return deferred.promise
|
459
|
+
end
|
460
|
+
|
461
|
+
|
462
|
+
private
|
463
|
+
|
464
|
+
|
465
|
+
def ref(reactor, value)
|
466
|
+
return value if value.is_a?(Promise)
|
467
|
+
return ResolvedPromise.new(reactor, value) # A resolved success promise
|
468
|
+
end
|
469
|
+
|
470
|
+
|
471
|
+
module_function :all, :reject, :defer, :ref, :any
|
472
|
+
private_class_method :ref
|
473
|
+
end
|
474
|
+
|
475
|
+
end
|