libuv 0.10.0 → 0.10.2

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 (55) hide show
  1. data/.gitignore +17 -17
  2. data/.gitmodules +3 -3
  3. data/.rspec +1 -1
  4. data/.travis.yml +16 -16
  5. data/Gemfile +2 -2
  6. data/LICENSE +23 -23
  7. data/README.md +82 -73
  8. data/Rakefile +31 -31
  9. data/lib/libuv.rb +53 -34
  10. data/lib/libuv/async.rb +47 -33
  11. data/lib/libuv/check.rb +55 -48
  12. data/lib/libuv/error.rb +70 -70
  13. data/lib/libuv/ext/ext.rb +264 -256
  14. data/lib/libuv/ext/platform/darwin_x64.rb +12 -12
  15. data/lib/libuv/ext/platform/linux.rb +7 -7
  16. data/lib/libuv/ext/platform/unix.rb +13 -13
  17. data/lib/libuv/ext/platform/windows.rb +26 -26
  18. data/lib/libuv/ext/tasks.rb +27 -27
  19. data/lib/libuv/ext/tasks/mac.rb +23 -23
  20. data/lib/libuv/ext/tasks/unix.rb +23 -23
  21. data/lib/libuv/ext/tasks/win.rb +11 -11
  22. data/lib/libuv/ext/types.rb +234 -229
  23. data/lib/libuv/file.rb +192 -0
  24. data/lib/libuv/filesystem.rb +233 -0
  25. data/lib/libuv/fs_event.rb +31 -31
  26. data/lib/libuv/handle.rb +85 -81
  27. data/lib/libuv/idle.rb +56 -49
  28. data/lib/libuv/loop.rb +338 -310
  29. data/lib/libuv/{assertions.rb → mixins/assertions.rb} +23 -23
  30. data/lib/libuv/mixins/fs_checks.rb +55 -0
  31. data/lib/libuv/{listener.rb → mixins/listener.rb} +34 -34
  32. data/lib/libuv/{net.rb → mixins/net.rb} +37 -37
  33. data/lib/libuv/{resource.rb → mixins/resource.rb} +27 -27
  34. data/lib/libuv/{stream.rb → mixins/stream.rb} +143 -123
  35. data/lib/libuv/pipe.rb +197 -97
  36. data/lib/libuv/prepare.rb +56 -49
  37. data/lib/libuv/q.rb +1 -1
  38. data/lib/libuv/signal.rb +51 -0
  39. data/lib/libuv/tcp.rb +204 -193
  40. data/lib/libuv/timer.rb +88 -75
  41. data/lib/libuv/tty.rb +37 -34
  42. data/lib/libuv/udp.rb +273 -255
  43. data/lib/libuv/version.rb +3 -3
  44. data/lib/libuv/work.rb +63 -61
  45. data/libuv.gemspec +54 -54
  46. data/spec/async_spec.rb +60 -60
  47. data/spec/cpu_spec.rb +10 -0
  48. data/spec/defer_spec.rb +980 -980
  49. data/spec/filesystem_spec.rb +119 -0
  50. data/spec/idle_spec.rb +56 -56
  51. data/spec/pipe_spec.rb +153 -148
  52. data/spec/tcp_spec.rb +203 -188
  53. metadata +73 -49
  54. checksums.yaml +0 -15
  55. data/lib/libuv/simple_async.rb +0 -28
data/lib/libuv/handle.rb CHANGED
@@ -1,82 +1,86 @@
1
- module Libuv
2
- class Handle < Q::DeferredPromise
3
- include Assertions, Resource, Listener
4
-
5
-
6
- def initialize(pointer, error)
7
- @pointer = pointer
8
-
9
- # Initialise the promise
10
- super(loop, loop.defer)
11
-
12
- # clean up on init error (always raise here)
13
- if error
14
- ::Libuv::Ext.free(pointer)
15
- defer.reject(error)
16
- @closed = true
17
- raise error
18
- end
19
- end
20
-
21
- # Public: Increment internal ref counter for the handle on the loop. Useful for
22
- # extending the loop with custom watchers that need to make loop not stop
23
- #
24
- # Returns self
25
- def ref
26
- return if @closed
27
- ::Libuv::Ext.ref(handle)
28
- end
29
-
30
- # Public: Decrement internal ref counter for the handle on the loop, useful to stop
31
- # loop even when there are outstanding open handles
32
- #
33
- # Returns self
34
- def unref
35
- return if @closed
36
- ::Libuv::Ext.unref(handle)
37
- end
38
-
39
- def close
40
- return if @closed
41
- @closed = true
42
- Libuv::Ext.close(handle, callback(:on_close))
43
- end
44
-
45
- def active?
46
- ::Libuv::Ext.is_active(handle) > 0
47
- end
48
-
49
- def closing?
50
- ::Libuv::Ext.is_closing(handle) > 0
51
- end
52
-
53
-
54
- protected
55
-
56
-
57
- def loop; @loop; end
58
- def handle; @pointer; end
59
- def defer; @defer; end
60
-
61
-
62
- private
63
-
64
-
65
- # Clean up and throw an error
66
- def reject(reason)
67
- @close_error = reason
68
- close
69
- end
70
-
71
- def on_close(pointer)
72
- ::Libuv::Ext.free(pointer)
73
- clear_callbacks
74
-
75
- if @close_error
76
- defer.reject(@close_error)
77
- else
78
- defer.resolve(nil)
79
- end
80
- end
81
- end
1
+ module Libuv
2
+ class Handle < Q::DeferredPromise
3
+ include Assertions, Resource, Listener
4
+
5
+
6
+ attr_accessor :storage # A place for general storage
7
+ attr_reader :closed
8
+
9
+
10
+ def initialize(pointer, error)
11
+ @pointer = pointer
12
+
13
+ # Initialise the promise
14
+ super(loop, loop.defer)
15
+
16
+ # clean up on init error (always raise here)
17
+ if error
18
+ ::Libuv::Ext.free(pointer)
19
+ defer.reject(error)
20
+ @closed = true
21
+ raise error
22
+ end
23
+ end
24
+
25
+ # Public: Increment internal ref counter for the handle on the loop. Useful for
26
+ # extending the loop with custom watchers that need to make loop not stop
27
+ #
28
+ # Returns self
29
+ def ref
30
+ return if @closed
31
+ ::Libuv::Ext.ref(handle)
32
+ end
33
+
34
+ # Public: Decrement internal ref counter for the handle on the loop, useful to stop
35
+ # loop even when there are outstanding open handles
36
+ #
37
+ # Returns self
38
+ def unref
39
+ return if @closed
40
+ ::Libuv::Ext.unref(handle)
41
+ end
42
+
43
+ def close
44
+ return if @closed
45
+ @closed = true
46
+ Libuv::Ext.close(handle, callback(:on_close))
47
+ end
48
+
49
+ def active?
50
+ ::Libuv::Ext.is_active(handle) > 0
51
+ end
52
+
53
+ def closing?
54
+ ::Libuv::Ext.is_closing(handle) > 0
55
+ end
56
+
57
+
58
+ protected
59
+
60
+
61
+ def loop; @loop; end
62
+ def handle; @pointer; end
63
+ def defer; @defer; end
64
+
65
+
66
+ private
67
+
68
+
69
+ # Clean up and throw an error
70
+ def reject(reason)
71
+ @close_error = reason
72
+ close
73
+ end
74
+
75
+ def on_close(pointer)
76
+ ::Libuv::Ext.free(pointer)
77
+ clear_callbacks
78
+
79
+ if @close_error
80
+ defer.reject(@close_error)
81
+ else
82
+ defer.resolve(nil)
83
+ end
84
+ end
85
+ end
82
86
  end
data/lib/libuv/idle.rb CHANGED
@@ -1,49 +1,56 @@
1
- module Libuv
2
- class Idle < Handle
3
-
4
-
5
- def initialize(loop, callback = nil, &blk)
6
- @loop = loop
7
- @callback = callback || blk
8
-
9
- idle_ptr = ::Libuv::Ext.create_handle(:uv_idle)
10
- error = check_result(::Libuv::Ext.idle_init(loop.handle, idle_ptr))
11
-
12
- super(idle_ptr, error)
13
- end
14
-
15
- def start
16
- return if @closed
17
- error = check_result ::Libuv::Ext.idle_start(handle, callback(:on_idle))
18
- reject(error) if error
19
- end
20
-
21
- def stop
22
- return if @closed
23
- error = check_result ::Libuv::Ext.idle_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_idle(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, :idle_cb, e
45
- end
46
- end
47
- end
48
- end
49
- end
1
+ module Libuv
2
+ class Idle < Handle
3
+
4
+
5
+ # @param loop [::Libuv::Loop] loop this idle handler will be associated
6
+ # @param callback [Proc] callback to be called when the loop is idle
7
+ def initialize(loop, callback = nil, &blk)
8
+ @loop = loop
9
+ @callback = callback || blk
10
+
11
+ idle_ptr = ::Libuv::Ext.create_handle(:uv_idle)
12
+ error = check_result(::Libuv::Ext.idle_init(loop.handle, idle_ptr))
13
+
14
+ super(idle_ptr, error)
15
+ end
16
+
17
+ # Enables the idle handler.
18
+ def start
19
+ return if @closed
20
+ error = check_result ::Libuv::Ext.idle_start(handle, callback(:on_idle))
21
+ reject(error) if error
22
+ end
23
+
24
+ # Disables the idle handler.
25
+ def stop
26
+ return if @closed
27
+ error = check_result ::Libuv::Ext.idle_stop(handle)
28
+ reject(error) if error
29
+ end
30
+
31
+ # Used to update the callback that will be triggered on idle
32
+ #
33
+ # @param callback [Proc] the callback to be called on idle trigger
34
+ def progress(callback = nil, &blk)
35
+ @callback = callback || blk
36
+ end
37
+
38
+
39
+ private
40
+
41
+
42
+ def on_idle(handle, status)
43
+ e = check_result(status)
44
+
45
+ if e
46
+ reject(e)
47
+ else
48
+ begin
49
+ @callback.call
50
+ rescue Exception => e
51
+ @loop.log :error, :idle_cb, e
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
data/lib/libuv/loop.rb CHANGED
@@ -1,310 +1,338 @@
1
- require 'thread'
2
-
3
- module Libuv
4
- class Loop
5
- include Resource, Assertions
6
-
7
-
8
- module ClassMethods
9
- # Get default loop
10
- #
11
- # @return [::Libuv::Loop]
12
- def default
13
- create(::Libuv::Ext.default_loop)
14
- end
15
-
16
- # Create new loop
17
- #
18
- # @return [::Libuv::Loop]
19
- def new
20
- create(::Libuv::Ext.loop_new)
21
- end
22
-
23
- # Create custom loop from pointer
24
- #
25
- # @return [::Libuv::Loop]
26
- def create(pointer)
27
- allocate.tap { |i| i.send(:initialize, FFI::AutoPointer.new(pointer, ::Libuv::Ext.method(:loop_delete))) }
28
- end
29
- end
30
- extend ClassMethods
31
-
32
-
33
- # Initialize a loop using an FFI::Pointer
34
- #
35
- # @return [::Libuv::Loop]
36
- def initialize(pointer) # :notnew:
37
- @pointer = pointer
38
- @loop = self
39
-
40
- # Create an async call for scheduling work from other threads
41
- @run_queue = Queue.new
42
- @queue_proc = proc do
43
- # Rubinius fix for promises
44
- # Anything calling schedule will
45
- # be delayed a tick outside of promise callbacks on rubinius (see https://github.com/ffi/ffi/issues/279)
46
- #@reactor_thread = Thread.current # Should work in rubinius 2.0
47
-
48
- # ensure we only execute what was required for this tick
49
- length = @run_queue.length
50
- length.times do
51
- begin
52
- run = @run_queue.pop true # pop non-block
53
- run.call
54
- rescue Exception => e
55
- @loop.log :error, :next_tick_cb, e
56
- end
57
- end
58
- end
59
- @process_queue = SimpleAsync.new(@loop, @queue_proc)
60
-
61
- # Create a next tick timer
62
- @next_tick = @loop.timer do
63
- @next_tick_scheduled = false
64
- @queue_proc.call
65
- end
66
-
67
- # Create an async call for ending the loop
68
- @stop_loop = SimpleAsync.new @loop do
69
- @process_queue.close
70
- @stop_loop.close
71
- @next_tick.close
72
-
73
- ::Libuv::Ext.stop(@pointer)
74
- end
75
- end
76
-
77
- def handle; @pointer; end
78
-
79
- # Run the actual event loop. This method will block for the duration of event loop unless
80
- # it is run inside an existing event loop, where a new thread will be created for it
81
- #
82
- # @param run_type [:UV_RUN_DEFAULT, :UV_RUN_ONCE, :UV_RUN_NOWAIT]
83
- # @yieldparam promise [::Libuv::Loop] Yields a promise that can be used for logging unhandled
84
- # exceptions on the loop.
85
- # @return [::Libuv::Q::Promise]
86
- def run(run_type = :UV_RUN_DEFAULT)
87
- @loop_notify = @loop.defer
88
-
89
- begin
90
- @reactor_thread = Thread.current
91
- yield @loop_notify.promise if block_given?
92
- ::Libuv::Ext.run(@pointer, run_type) # This is blocking
93
- ensure
94
- @reactor_thread = nil
95
- @run_queue.clear
96
- end
97
-
98
- @loop
99
- end
100
-
101
-
102
- # Creates a deferred result object for where the result of an operation may only be returned
103
- # at some point in the future or is being processed on a different thread (thread safe)
104
- #
105
- # @return [::Libuv::Q::Deferred]
106
- def defer
107
- Q.defer(@loop)
108
- end
109
-
110
- # Combines multiple promises into a single promise that is resolved when all of the input
111
- # promises are resolved. (thread safe)
112
- #
113
- # @param [*Promise] Promises a number of promises that will be combined into a single promise
114
- # @return [Promise] Returns a single promise that will be resolved with an array of values,
115
- # each value corresponding to the promise at the same index in the `promises` array. If any of
116
- # the promises is resolved with a rejection, this resulting promise will be resolved with the
117
- # same rejection.
118
- def all(*promises)
119
- Q.all(@loop, *promises)
120
- end
121
-
122
- #
123
- # Combines multiple promises into a single promise that is resolved when any of the input
124
- # promises are resolved.
125
- #
126
- # @param [*Promise] Promises a number of promises that will be combined into a single promise
127
- # @return [Promise] Returns a single promise
128
- def any(*promises)
129
- Q.any(@loop, *promises)
130
- end
131
-
132
- #
133
- # Combines multiple promises into a single promise that is resolved when all of the input
134
- # promises are resolved or rejected.
135
- #
136
- # @param [*Promise] Promises a number of promises that will be combined into a single promise
137
- # @return [Promise] Returns a single promise that will be resolved with an array of values,
138
- # each [result, wasResolved] value pair corresponding to a at the same index in the `promises` array.
139
- def finally(*promises)
140
- Q.finally(@loop, *promises)
141
- end
142
-
143
-
144
- # forces loop time update, useful for getting more granular times
145
- #
146
- # @return nil
147
- def update_time
148
- ::Libuv::Ext.update_time(@pointer)
149
- end
150
-
151
- # Get current time in microseconds
152
- #
153
- # @return [Fixnum]
154
- def now
155
- ::Libuv::Ext.now(@pointer)
156
- end
157
-
158
- # Lookup an error code and return is as an error object
159
- #
160
- # @param err [Integer] The error code to look up.
161
- # @return [::Libuv::Error]
162
- def lookup_error(err)
163
- name = ::Libuv::Ext.err_name(err)
164
- msg = ::Libuv::Ext.strerror(err)
165
-
166
- ::Libuv::Error.const_get(name.to_sym).new(msg)
167
- rescue Exception => e
168
- @loop.log :warn, :error_lookup_failed, e
169
- ::Libuv::Error::UNKNOWN.new("error lookup failed for code #{err} #{name} #{msg}")
170
- end
171
-
172
- # Get a new TCP instance
173
- #
174
- # @return [::Libuv::TCP]
175
- def tcp
176
- TCP.new(@loop)
177
- end
178
-
179
- # Get a new UDP instance
180
- #
181
- # @return [::Libuv::UDP]
182
- def udp
183
- UDP.new(@loop)
184
- end
185
-
186
- # Get a new TTY instance
187
- #
188
- # @param fileno [Integer] Integer file descriptor of a tty device
189
- # @param readable [true, false] Boolean indicating if TTY is readable
190
- # @return [::Libuv::TTY]
191
- def tty(fileno, readable = false)
192
- assert_type(Integer, fileno, "io#fileno must return an integer file descriptor, #{fileno.inspect} given")
193
-
194
- TTY.new(@loop, fileno, readable)
195
- end
196
-
197
- # Get a new Pipe instance
198
- #
199
- # @param ipc [true, false]
200
- # indicate if a handle will be used for ipc, useful for sharing tcp socket between processes
201
- # @return [::Libuv::Pipe]
202
- def pipe(ipc = false)
203
- Pipe.new(@loop, ipc)
204
- end
205
-
206
- # Get a new timer instance
207
- #
208
- # @return [::Libuv::Timer]
209
- def timer(callback = nil, &blk)
210
- Timer.new(@loop, callback || blk)
211
- end
212
-
213
- # Get a new Prepare handle
214
- #
215
- # @return [::Libuv::Prepare]
216
- def prepare
217
- Prepare.new(@loop)
218
- end
219
-
220
- # Get a new Check handle
221
- #
222
- # @return [::Libuv::Check]
223
- def check
224
- Check.new(@loop)
225
- end
226
-
227
- # Get a new Idle handle
228
- #
229
- # @return [::Libuv::Idle]
230
- def idle(callback = nil, &block)
231
- Idle.new(@loop, callback || block)
232
- end
233
-
234
- # Get a new Async handle
235
- #
236
- # @return [::Libuv::Async]
237
- def async(callback = nil, &block)
238
- callback ||= block
239
- handle = Async.new(@loop)
240
- handle.progress callback if callback
241
- handle
242
- end
243
-
244
- # Queue some work for processing in the libuv thread pool
245
- #
246
- # @return [::Libuv::Work]
247
- # @raise [ArgumentError] if block is not given
248
- def work(callback = nil, &block)
249
- Work.new(@loop, callback || block) # Work is a promise object
250
- end
251
-
252
- # Get a new FSEvent instance
253
- #
254
- # @return [::Libuv::FSEvent]
255
- def fs_event(path)
256
- assert_type(String, path)
257
- FSEvent.new(@loop, path)
258
- end
259
-
260
-
261
- # Schedule some work to be processed on the event loop (thread safe)
262
- #
263
- # @return [nil]
264
- def schedule(&block)
265
- assert_block(block)
266
-
267
- if @reactor_thread == Thread.current
268
- block.call
269
- else
270
- @run_queue << block
271
- @process_queue.call
272
- end
273
- end
274
-
275
- # Schedule some work to be processed in the next iteration of the event loop (thread safe)
276
- #
277
- # @return [nil]
278
- def next_tick(&block)
279
- assert_block(block)
280
-
281
- @run_queue << block
282
- if @reactor_thread == Thread.current
283
- # Create a next tick timer
284
- if not @next_tick_scheduled
285
- @next_tick.start(0)
286
- @next_tick_scheduled = true
287
- end
288
- else
289
- @process_queue.call
290
- end
291
- end
292
-
293
- # Notifies the loop there was an event that should be logged
294
- #
295
- # @param level [Symbol] the error level (info, warn, error etc)
296
- # @param id [Object] some kind of identifying information
297
- # @param *args [*args] any additional information
298
- # @return [nil]
299
- def log(level, id, *args)
300
- @loop_notify.notify(level, id, *args)
301
- end
302
-
303
- # Closes handles opened by the loop class and completes the current loop iteration (thread safe)
304
- #
305
- # @return [nil]
306
- def stop
307
- @stop_loop.call
308
- end
309
- end
310
- end
1
+ require 'thread'
2
+
3
+ module Libuv
4
+ class Loop
5
+ include Resource, Assertions
6
+
7
+
8
+ module ClassMethods
9
+ # Get default loop
10
+ #
11
+ # @return [::Libuv::Loop]
12
+ def default
13
+ create(::Libuv::Ext.default_loop)
14
+ end
15
+
16
+ # Create new Libuv loop
17
+ #
18
+ # @return [::Libuv::Loop]
19
+ def new
20
+ create(::Libuv::Ext.loop_new)
21
+ end
22
+
23
+ # Build a Ruby Libuv loop from an existing loop pointer
24
+ #
25
+ # @return [::Libuv::Loop]
26
+ def create(pointer)
27
+ allocate.tap { |i| i.send(:initialize, FFI::AutoPointer.new(pointer, ::Libuv::Ext.method(:loop_delete))) }
28
+ end
29
+ end
30
+ extend ClassMethods
31
+
32
+
33
+ # Initialize a loop using an FFI::Pointer to a libuv loop
34
+ def initialize(pointer) # :notnew:
35
+ @pointer = pointer
36
+ @loop = self
37
+
38
+ # Create an async call for scheduling work from other threads
39
+ @run_queue = Queue.new
40
+ @queue_proc = proc do
41
+ # ensure we only execute what was required for this tick
42
+ length = @run_queue.length
43
+ length.times do
44
+ begin
45
+ run = @run_queue.pop true # pop non-block
46
+ run.call
47
+ rescue Exception => e
48
+ @loop.log :error, :next_tick_cb, e
49
+ end
50
+ end
51
+ end
52
+ @process_queue = Async.new(@loop, @queue_proc)
53
+
54
+ # Create a next tick timer
55
+ @next_tick = @loop.timer do
56
+ @next_tick_scheduled = false
57
+ @queue_proc.call
58
+ end
59
+
60
+ # Create an async call for ending the loop
61
+ @stop_loop = Async.new @loop do
62
+ @process_queue.close
63
+ @stop_loop.close
64
+ @next_tick.close
65
+
66
+ ::Libuv::Ext.stop(@pointer)
67
+ end
68
+ end
69
+
70
+ def handle; @pointer; end
71
+
72
+ # Run the actual event loop. This method will block until the loop is stopped.
73
+ #
74
+ # @param run_type [:UV_RUN_DEFAULT, :UV_RUN_ONCE, :UV_RUN_NOWAIT]
75
+ # @yieldparam promise [::Libuv::Q::Promise] Yields a promise that can be used for logging unhandled
76
+ # exceptions on the loop.
77
+ def run(run_type = :UV_RUN_DEFAULT)
78
+ @loop_notify = @loop.defer
79
+
80
+ begin
81
+ @reactor_thread = Thread.current
82
+ yield @loop_notify.promise if block_given?
83
+ ::Libuv::Ext.run(@pointer, run_type) # This is blocking
84
+ ensure
85
+ @reactor_thread = nil
86
+ @run_queue.clear
87
+ end
88
+
89
+ @loop
90
+ end
91
+
92
+
93
+ # Creates a deferred result object for where the result of an operation may only be returned
94
+ # at some point in the future or is being processed on a different thread (thread safe)
95
+ #
96
+ # @return [::Libuv::Q::Deferred]
97
+ def defer
98
+ Q.defer(@loop)
99
+ end
100
+
101
+ # Combines multiple promises into a single promise that is resolved when all of the input
102
+ # promises are resolved. (thread safe)
103
+ #
104
+ # @param *promises [::Libuv::Q::Promise] a number of promises that will be combined into a single promise
105
+ # @return [::Libuv::Q::Promise] Returns a single promise that will be resolved with an array of values,
106
+ # each value corresponding to the promise at the same index in the `promises` array. If any of
107
+ # the promises is resolved with a rejection, this resulting promise will be resolved with the
108
+ # same rejection.
109
+ def all(*promises)
110
+ Q.all(@loop, *promises)
111
+ end
112
+
113
+ #
114
+ # Combines multiple promises into a single promise that is resolved when any of the input
115
+ # promises are resolved.
116
+ #
117
+ # @param *promises [::Libuv::Q::Promise] a number of promises that will be combined into a single promise
118
+ # @return [::Libuv::Q::Promise] Returns a single promise
119
+ def any(*promises)
120
+ Q.any(@loop, *promises)
121
+ end
122
+
123
+ #
124
+ # Combines multiple promises into a single promise that is resolved when all of the input
125
+ # promises are resolved or rejected.
126
+ #
127
+ # @param *promises [::Libuv::Q::Promise] a number of promises that will be combined into a single promise
128
+ # @return [::Libuv::Q::Promise] Returns a single promise that will be resolved with an array of values,
129
+ # each [result, wasResolved] value pair corresponding to a at the same index in the `promises` array.
130
+ def finally(*promises)
131
+ Q.finally(@loop, *promises)
132
+ end
133
+
134
+
135
+ # forces loop time update, useful for getting more granular times
136
+ #
137
+ # @return nil
138
+ def update_time
139
+ ::Libuv::Ext.update_time(@pointer)
140
+ end
141
+
142
+ # Get current time in microseconds
143
+ #
144
+ # @return [Fixnum]
145
+ def now
146
+ ::Libuv::Ext.now(@pointer)
147
+ end
148
+
149
+ # Lookup an error code and return is as an error object
150
+ #
151
+ # @param err [Integer] The error code to look up.
152
+ # @return [::Libuv::Error]
153
+ def lookup_error(err)
154
+ name = ::Libuv::Ext.err_name(err)
155
+ msg = ::Libuv::Ext.strerror(err)
156
+
157
+ ::Libuv::Error.const_get(name.to_sym).new(msg)
158
+ rescue Exception => e
159
+ @loop.log :warn, :error_lookup_failed, e
160
+ ::Libuv::Error::UNKNOWN.new("error lookup failed for code #{err} #{name} #{msg}")
161
+ end
162
+
163
+ # Get a new TCP instance
164
+ #
165
+ # @return [::Libuv::TCP]
166
+ def tcp
167
+ TCP.new(@loop)
168
+ end
169
+
170
+ # Get a new UDP instance
171
+ #
172
+ # @return [::Libuv::UDP]
173
+ def udp
174
+ UDP.new(@loop)
175
+ end
176
+
177
+ # Get a new TTY instance
178
+ #
179
+ # @param fileno [Integer] Integer file descriptor of a tty device
180
+ # @param readable [true, false] Boolean indicating if TTY is readable
181
+ # @return [::Libuv::TTY]
182
+ def tty(fileno, readable = false)
183
+ assert_type(Integer, fileno, "io#fileno must return an integer file descriptor, #{fileno.inspect} given")
184
+
185
+ TTY.new(@loop, fileno, readable)
186
+ end
187
+
188
+ # Get a new Pipe instance
189
+ #
190
+ # @param ipc [true, false] indicate if a handle will be used for ipc, useful for sharing tcp socket between processes
191
+ # @return [::Libuv::Pipe]
192
+ def pipe(ipc = false)
193
+ Pipe.new(@loop, ipc)
194
+ end
195
+
196
+ # Get a new timer instance
197
+ #
198
+ # @param callback [Proc] the callback to be called on timer trigger
199
+ # @return [::Libuv::Timer]
200
+ def timer(callback = nil, &blk)
201
+ Timer.new(@loop, callback || blk)
202
+ end
203
+
204
+ # Get a new Prepare handle
205
+ #
206
+ # @return [::Libuv::Prepare]
207
+ def prepare
208
+ Prepare.new(@loop)
209
+ end
210
+
211
+ # Get a new Check handle
212
+ #
213
+ # @return [::Libuv::Check]
214
+ def check
215
+ Check.new(@loop)
216
+ end
217
+
218
+ # Get a new Idle handle
219
+ #
220
+ # @param callback [Proc] the callback to be called on idle trigger
221
+ # @return [::Libuv::Idle]
222
+ def idle(callback = nil, &block)
223
+ Idle.new(@loop, callback || block)
224
+ end
225
+
226
+ # Get a new Async handle
227
+ #
228
+ # @return [::Libuv::Async]
229
+ def async(callback = nil, &block)
230
+ callback ||= block
231
+ handle = Async.new(@loop)
232
+ handle.progress callback if callback
233
+ handle
234
+ end
235
+
236
+ # Get a new signal handler
237
+ #
238
+ # @return [::Libuv::Signal]
239
+ def signal(signum = nil, callback = nil, &block)
240
+ callback ||= block
241
+ handle = Signal.new(@loop)
242
+ handle.progress callback if callback
243
+ handle.start(signum) if signum
244
+ handle
245
+ end
246
+
247
+ # Queue some work for processing in the libuv thread pool
248
+ #
249
+ # @param callback [Proc] the callback to be called in the thread pool
250
+ # @return [::Libuv::Work]
251
+ # @raise [ArgumentError] if block is not given
252
+ def work(callback = nil, &block)
253
+ callback ||= block
254
+ assert_block(callback)
255
+ Work.new(@loop, callback) # Work is a promise object
256
+ end
257
+
258
+ # Get a new FSEvent instance
259
+ #
260
+ # @param path [String] the path to the file or folder for watching
261
+ # @return [::Libuv::FSEvent]
262
+ # @raise [ArgumentError] if path is not a string
263
+ def fs_event(path)
264
+ assert_type(String, path)
265
+ FSEvent.new(@loop, path)
266
+ end
267
+
268
+ # Opens a file and returns an object that can be used to manipulate it
269
+ #
270
+ # @param path [String] the path to the file or folder for watching
271
+ # @param flags [Integer] see ruby File::Constants
272
+ # @param mode [Integer]
273
+ # @return [::Libuv::File]
274
+ def file(path, flags = 0, mode = 0)
275
+ assert_type(String, path, "path must be a String")
276
+ assert_type(Integer, flags, "flags must be an Integer")
277
+ assert_type(Integer, mode, "mode must be an Integer")
278
+ File.new(@loop, path, flags, mode)
279
+ end
280
+
281
+ # Returns an object for manipulating the filesystem
282
+ #
283
+ # @return [::Libuv::Filesystem]
284
+ def filesystem
285
+ Filesystem.new(@loop)
286
+ end
287
+
288
+ # Schedule some work to be processed on the event loop as soon as possible (thread safe)
289
+ #
290
+ # @param callback [Proc] the callback to be called on the reactor thread
291
+ # @raise [ArgumentError] if block is not given
292
+ def schedule(callback = nil, &block)
293
+ callback ||= block
294
+ assert_block(callback)
295
+
296
+ if @reactor_thread == Thread.current
297
+ block.call
298
+ else
299
+ @run_queue << callback
300
+ @process_queue.call
301
+ end
302
+ end
303
+
304
+ # Queue some work to be processed in the next iteration of the event loop (thread safe)
305
+ #
306
+ # @param callback [Proc] the callback to be called on the reactor thread
307
+ # @raise [ArgumentError] if block is not given
308
+ def next_tick(callback = nil, &block)
309
+ callback ||= block
310
+ assert_block(callback)
311
+
312
+ @run_queue << callback
313
+ if @reactor_thread == Thread.current
314
+ # Create a next tick timer
315
+ if not @next_tick_scheduled
316
+ @next_tick.start(0)
317
+ @next_tick_scheduled = true
318
+ end
319
+ else
320
+ @process_queue.call
321
+ end
322
+ end
323
+
324
+ # Notifies the loop there was an event that should be logged
325
+ #
326
+ # @param level [Symbol] the error level (info, warn, error etc)
327
+ # @param id [Object] some kind of identifying information
328
+ # @param *args [*args] any additional information
329
+ def log(level, id, *args)
330
+ @loop_notify.notify(level, id, *args)
331
+ end
332
+
333
+ # Closes handles opened by the loop class and completes the current loop iteration (thread safe)
334
+ def stop
335
+ @stop_loop.call
336
+ end
337
+ end
338
+ end