libuv 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +17 -0
  3. data/.gitmodules +3 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +16 -0
  6. data/Gemfile +2 -0
  7. data/LICENSE +24 -0
  8. data/README.md +73 -0
  9. data/Rakefile +31 -0
  10. data/lib/libuv.rb +34 -0
  11. data/lib/libuv/assertions.rb +23 -0
  12. data/lib/libuv/async.rb +33 -0
  13. data/lib/libuv/check.rb +49 -0
  14. data/lib/libuv/error.rb +70 -0
  15. data/lib/libuv/ext/ext.rb +257 -0
  16. data/lib/libuv/ext/platform/darwin_x64.rb +12 -0
  17. data/lib/libuv/ext/platform/linux.rb +8 -0
  18. data/lib/libuv/ext/platform/unix.rb +14 -0
  19. data/lib/libuv/ext/platform/windows.rb +27 -0
  20. data/lib/libuv/ext/tasks.rb +27 -0
  21. data/lib/libuv/ext/tasks/mac.rb +23 -0
  22. data/lib/libuv/ext/tasks/unix.rb +23 -0
  23. data/lib/libuv/ext/tasks/win.rb +11 -0
  24. data/lib/libuv/ext/types.rb +230 -0
  25. data/lib/libuv/fs_event.rb +31 -0
  26. data/lib/libuv/handle.rb +82 -0
  27. data/lib/libuv/idle.rb +49 -0
  28. data/lib/libuv/listener.rb +34 -0
  29. data/lib/libuv/loop.rb +310 -0
  30. data/lib/libuv/net.rb +38 -0
  31. data/lib/libuv/pipe.rb +97 -0
  32. data/lib/libuv/prepare.rb +49 -0
  33. data/lib/libuv/q.rb +429 -0
  34. data/lib/libuv/resource.rb +28 -0
  35. data/lib/libuv/simple_async.rb +28 -0
  36. data/lib/libuv/stream.rb +124 -0
  37. data/lib/libuv/tcp.rb +194 -0
  38. data/lib/libuv/timer.rb +75 -0
  39. data/lib/libuv/tty.rb +34 -0
  40. data/lib/libuv/udp.rb +256 -0
  41. data/lib/libuv/version.rb +3 -0
  42. data/lib/libuv/work.rb +62 -0
  43. data/libuv.gemspec +54 -0
  44. data/spec/async_spec.rb +60 -0
  45. data/spec/defer_spec.rb +980 -0
  46. data/spec/idle_spec.rb +56 -0
  47. data/spec/pipe_spec.rb +148 -0
  48. data/spec/tcp_spec.rb +188 -0
  49. 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