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