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.
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