libuv 2.0.12 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/README.md +67 -34
  4. data/lib/libuv.rb +30 -5
  5. data/lib/libuv/async.rb +16 -10
  6. data/lib/libuv/check.rb +19 -12
  7. data/lib/libuv/coroutines.rb +39 -12
  8. data/lib/libuv/dns.rb +25 -18
  9. data/lib/libuv/error.rb +2 -0
  10. data/lib/libuv/ext/ext.rb +28 -36
  11. data/lib/libuv/ext/platform/darwin_x64.rb +2 -0
  12. data/lib/libuv/ext/platform/unix.rb +2 -0
  13. data/lib/libuv/ext/platform/windows.rb +2 -0
  14. data/lib/libuv/ext/tasks.rb +2 -0
  15. data/lib/libuv/ext/tasks/mac.rb +2 -0
  16. data/lib/libuv/ext/tasks/unix.rb +2 -0
  17. data/lib/libuv/ext/tasks/win.rb +2 -0
  18. data/lib/libuv/ext/types.rb +2 -1
  19. data/lib/libuv/file.rb +67 -50
  20. data/lib/libuv/filesystem.rb +63 -61
  21. data/lib/libuv/fs_event.rb +7 -4
  22. data/lib/libuv/handle.rb +30 -14
  23. data/lib/libuv/idle.rb +17 -10
  24. data/lib/libuv/mixins/accessors.rb +41 -0
  25. data/lib/libuv/mixins/assertions.rb +3 -1
  26. data/lib/libuv/mixins/fs_checks.rb +29 -6
  27. data/lib/libuv/mixins/listener.rb +4 -2
  28. data/lib/libuv/mixins/net.rb +4 -2
  29. data/lib/libuv/mixins/resource.rb +5 -3
  30. data/lib/libuv/mixins/stream.rb +128 -35
  31. data/lib/libuv/pipe.rb +54 -27
  32. data/lib/libuv/prepare.rb +19 -12
  33. data/lib/libuv/q.rb +109 -101
  34. data/lib/libuv/{loop.rb → reactor.rb} +163 -85
  35. data/lib/libuv/signal.rb +13 -5
  36. data/lib/libuv/tcp.rb +109 -63
  37. data/lib/libuv/timer.rb +44 -24
  38. data/lib/libuv/tty.rb +8 -3
  39. data/lib/libuv/udp.rb +49 -22
  40. data/lib/libuv/version.rb +3 -1
  41. data/lib/libuv/work.rb +14 -10
  42. data/libuv.gemspec +11 -9
  43. data/spec/async_spec.rb +13 -13
  44. data/spec/coroutines_spec.rb +20 -50
  45. data/spec/defer_spec.rb +182 -311
  46. data/spec/dns_spec.rb +51 -41
  47. data/spec/dsl_spec.rb +43 -0
  48. data/spec/filesystem_spec.rb +65 -87
  49. data/spec/idle_spec.rb +19 -33
  50. data/spec/pipe_spec.rb +25 -32
  51. data/spec/tcp_spec.rb +116 -53
  52. data/spec/timer_spec.rb +3 -3
  53. data/spec/udp_spec.rb +16 -17
  54. data/spec/zen_spec.rb +2 -3
  55. metadata +37 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3392e84572469e65fb1505dc8ca99782acca89af
4
- data.tar.gz: bf6311317ca08be77c06a503b4e23b5b574c83e9
3
+ metadata.gz: 328790a2659803772fb2ca5891889bda349b47fd
4
+ data.tar.gz: 0b534aaeeb7fd1fbfd11c35090e8f048be6db411
5
5
  SHA512:
6
- metadata.gz: 64e492f11ddcac4a87cb3460459fc64bcbfa5484b8732346c18d03071d693c7a04281298edd32f851e3787fb3cccef87bc4af08ac06a55182f8f8eaef6e03e81
7
- data.tar.gz: 7a4372cce3b02f884a29cb90a03b3a83bfde40d5b9487a1650185d52df53fb93668898e9a89954dc4c360b9b343f8371df121442d5a8523ad131eee2bc2e1714
6
+ metadata.gz: 00f0aed38c13defc6a2e310a67e81f576fad3d90308d00575982794e4bad6d0f95204b20d5f66db130984e22223c616bc02f78b3ffbefcc2d623c8efec9b1516
7
+ data.tar.gz: da08a1d126e8d25dd0bb8fa8d0deb3a25d05a369725362d2aa5a4fbf5254aa2d5db7f6ec7f10c8b70ad2f24f2f6924018c59259276e83fa4d22eae58554a4599
data/.gitignore CHANGED
@@ -17,3 +17,5 @@ Gemfile-custom
17
17
  ext/*
18
18
 
19
19
  *.gem
20
+
21
+ *.rdb
data/README.md CHANGED
@@ -4,60 +4,93 @@
4
4
 
5
5
  [Libuv](https://github.com/libuv/libuv) is a cross platform asynchronous IO implementation that powers NodeJS. It supports sockets, both UDP and TCP, filesystem watch, TTY, Pipes and other asynchronous primitives like timer, check, prepare and idle.
6
6
 
7
- The Libuv gem contains Libuv and a Ruby wrapper that implements [pipelined promises](http://en.wikipedia.org/wiki/Futures_and_promises#Promise_pipelining) for asynchronous flow control and [coroutines](http://en.wikipedia.org/wiki/Coroutine) for untangling evented code
7
+ The Libuv gem contains Libuv and a Ruby wrapper that implements [pipelined promises](http://en.wikipedia.org/wiki/Futures_and_promises#Promise_pipelining) for asynchronous flow control and [coroutines](http://en.wikipedia.org/wiki/Coroutine) / [futures](https://en.wikipedia.org/wiki/Futures_and_promises) for untangling evented code
8
8
 
9
9
  ## Usage
10
10
 
11
- using promises
11
+ Libuv supports multiple reactors that can run on different threads.
12
+
13
+ For convenience the thread local or default reactor can be accessed via the `reactor` method
14
+ You can pass a block to be executed on the reactor and the reactor will run until there is nothing left to do.
12
15
 
13
16
  ```ruby
14
17
  require 'libuv'
15
18
 
16
- loop = Libuv::Loop.default
17
- # or
18
- # loop = Libuv::Loop.new
19
-
20
- loop.run do
21
- timer = loop.timer do
19
+ reactor do |reactor|
20
+ reactor.timer {
22
21
  puts "5 seconds passed"
23
- timer.close
24
- end
25
- timer.catch do |error|
26
- puts "error with timer: #{error}"
27
- end
28
- timer.finally do
29
- puts "timer handle was closed"
30
- end
31
- timer.start(5000)
22
+ }.start(5000)
32
23
  end
24
+
25
+ puts "reactor stopped. No more IO to process"
33
26
  ```
34
27
 
35
- using coroutines (if a somewhat abstract example)
28
+ Promises are used to simplify code flow.
36
29
 
37
30
  ```ruby
38
31
  require 'libuv'
39
- require 'libuv/coroutines'
40
32
 
41
- loop = Libuv::Loop.default
42
- loop.run do
43
- begin
44
- timer = loop.timer do
45
- puts "5 seconds passed"
46
- timer.close
47
- end
48
- timer.start(5000)
33
+ reactor do |reactor|
34
+ reactor.tcp { |data, socket|
35
+ puts "received: #{data}"
36
+ socket.close
37
+ }
38
+ .connect('127.0.0.1', 3000) { |socket|
39
+ socket.start_read
40
+ .write("GET / HTTP/1.1\r\n\r\n")
41
+ }
42
+ .catch { |error|
43
+ puts "error: #{error}"
44
+ }
45
+ .finally {
46
+ puts "socket closed"
47
+ }
48
+ end
49
+ ```
49
50
 
50
- # co-routine waits for timer to close
51
- co timer
51
+ Continuations are used if callbacks are not defined
52
52
 
53
- puts "timer handle was closed"
53
+ ```ruby
54
+ require 'libuv'
55
+
56
+ reactor do |reactor|
57
+ begin
58
+ reactor.tcp { |data, socket|
59
+ puts "received: #{data}"
60
+ socket.close
61
+ }
62
+ .connect('127.0.0.1', 3000)
63
+ .start_read
64
+ .write("GET / HTTP/1.1\r\n\r\n")
54
65
  rescue => error
55
- puts "error with timer: #{error}"
66
+ puts "error: #{error}"
56
67
  end
57
68
  end
58
69
  ```
59
70
 
60
- Check out the [yard documentation](http://rubydoc.info/gems/libuv/Libuv/Loop)
71
+ Any promise can be converted into a continuation
72
+
73
+ ```ruby
74
+ require 'libuv'
75
+
76
+ reactor do |reactor|
77
+ # Perform work on the thread pool with promises
78
+ reactor.work {
79
+ 10 * 2
80
+ }.then { |result|
81
+ puts "result using a promise #{result}"
82
+ }
83
+
84
+ # Use the coroutine helper to obtain the result without a callback
85
+ result = reactor.work {
86
+ 10 * 3
87
+ }.value
88
+ puts "no additional callbacks here #{result}"
89
+ end
90
+ ```
91
+
92
+
93
+ Check out the [yard documentation](http://rubydoc.info/gems/libuv/Libuv/Reactor)
61
94
 
62
95
 
63
96
  ## Installation
@@ -107,6 +140,6 @@ Windows users will additionally require:
107
140
  * Filesystem Events
108
141
  * Filesystem manipulation
109
142
  * File manipulation
110
- * Errors (with a catch-all fallback for anything unhandled on the event loop)
143
+ * Errors (with a catch-all fallback for anything unhandled on the event reactor)
111
144
  * Work queue (thread pool)
112
- * Coroutines (optional - makes use of Fibers)
145
+ * Coroutines / futures (makes use of Fibers)
data/lib/libuv.rb CHANGED
@@ -1,21 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
4
+ require 'thread'
2
5
  require 'ffi'
3
6
 
4
7
  module Libuv
8
+ DefaultThread = Thread.current
9
+
5
10
  require 'libuv/ext/ext' # The libuv ffi ext
6
11
  require 'libuv/error' # List of errors (matching those in uv.h)
7
12
  require 'libuv/q' # The promise library
8
13
 
9
- # -- The classes required for a loop instance --
14
+ # -- The classes required for a reactor instance --
10
15
  require 'libuv/mixins/assertions' # Common code to check arguments
16
+ require 'libuv/mixins/accessors' # Helper methods for accessing reactor functions
11
17
  require 'libuv/mixins/resource' # Common code to check for errors
12
18
  require 'libuv/mixins/listener' # Common callback code
13
19
 
14
20
  require 'libuv/handle' # Base class for most libuv functionality
15
- require 'libuv/prepare' # Called at the end of a loop cycle
16
- require 'libuv/async' # Provide a threadsafe way to signal the event loop
21
+ require 'libuv/prepare' # Called at the end of a reactor cycle
22
+ require 'libuv/async' # Provide a threadsafe way to signal the event reactor
17
23
  require 'libuv/timer' # High resolution timer
18
- require 'libuv/loop' # The libuv reactor or event loop
24
+ require 'libuv/reactor' # The libuv reactor or event reactor
25
+ require 'libuv/coroutines'
19
26
  # --
20
27
 
21
28
  autoload :FsChecks, 'libuv/mixins/fs_checks' # Common code to check file system results
@@ -25,7 +32,7 @@ module Libuv
25
32
  autoload :Filesystem, 'libuv/filesystem' # Async directory manipulation
26
33
  autoload :FSEvent, 'libuv/fs_event' # Notifies of changes to files and folders as they occur
27
34
  autoload :Signal, 'libuv/signal' # Used to handle OS signals
28
- autoload :Check, 'libuv/check' # Called before processing events on the loop
35
+ autoload :Check, 'libuv/check' # Called before processing events on the reactor
29
36
  autoload :File, 'libuv/file' # Async file reading and writing
30
37
  autoload :Idle, 'libuv/idle' # Called when there are no events to process
31
38
  autoload :Work, 'libuv/work' # Provide work to be completed on another thread (thread pool)
@@ -52,4 +59,22 @@ module Libuv
52
59
  return nil
53
60
  end
54
61
  end
62
+
63
+ # Include all the accessors at this level
64
+ extend Accessors
65
+
66
+ # Run the default reactor
67
+ at_exit do
68
+ reactor = Reactor.default
69
+ reactor.run if $!.nil? && reactor.run_count == 0
70
+ end
71
+ end
72
+
73
+
74
+ class Object
75
+ private
76
+
77
+ def reactor(&blk)
78
+ Libuv.reactor &blk
79
+ end
55
80
  end
data/lib/libuv/async.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Libuv
2
4
  class Async < Handle
3
5
 
@@ -5,14 +7,14 @@ module Libuv
5
7
  define_callback function: :on_async
6
8
 
7
9
 
8
- # @param thread [::Libuv::Loop] loop this async callback will be associated
9
- def initialize(thread, callback = nil, &blk)
10
- @loop = thread
10
+ # @param reactor [::Libuv::Reactor] reactor this async callback will be associated
11
+ def initialize(reactor, callback = nil, &blk)
12
+ @reactor = reactor
11
13
  @callback = callback || blk
12
14
 
13
15
  async_ptr = ::Libuv::Ext.allocate_handle_async
14
16
  on_async = callback(:on_async, async_ptr.address)
15
- error = check_result(::Libuv::Ext.async_init(loop.handle, async_ptr, on_async))
17
+ error = check_result(::Libuv::Ext.async_init(reactor.handle, async_ptr, on_async))
16
18
 
17
19
  super(async_ptr, error)
18
20
  end
@@ -22,13 +24,15 @@ module Libuv
22
24
  return if @closed
23
25
  error = check_result ::Libuv::Ext.async_send(handle)
24
26
  reject(error) if error
27
+ self
25
28
  end
26
29
 
27
30
  # Used to update the callback that will be triggered when async is called
28
31
  #
29
- # @param callback [Proc] the callback to be called on loop prepare
32
+ # @param callback [Proc] the callback to be called on reactor prepare
30
33
  def progress(callback = nil, &blk)
31
34
  @callback = callback || blk
35
+ self
32
36
  end
33
37
 
34
38
 
@@ -36,11 +40,13 @@ module Libuv
36
40
 
37
41
 
38
42
  def on_async(handle)
39
- begin
40
- @callback.call
41
- rescue Exception => e
42
- @loop.log :error, :async_cb, e
43
- end
43
+ ::Fiber.new {
44
+ begin
45
+ @callback.call
46
+ rescue Exception => e
47
+ @reactor.log e, 'performing async callback'
48
+ end
49
+ }.resume
44
50
  end
45
51
  end
46
52
  end
data/lib/libuv/check.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Libuv
2
4
  class Check < Handle
3
5
 
@@ -5,14 +7,14 @@ module Libuv
5
7
  define_callback function: :on_check
6
8
 
7
9
 
8
- # @param loop [::Libuv::Loop] loop this check will be associated
9
- # @param callback [Proc] callback to be called on loop check
10
- def initialize(loop, callback = nil, &blk)
11
- @loop = loop
10
+ # @param reactor [::Libuv::Reactor] reactor this check will be associated
11
+ # @param callback [Proc] callback to be called on reactor check
12
+ def initialize(reactor, callback = nil, &blk)
13
+ @reactor = reactor
12
14
  @callback = callback || blk
13
15
 
14
16
  check_ptr = ::Libuv::Ext.allocate_handle_check
15
- error = check_result(::Libuv::Ext.check_init(loop.handle, check_ptr))
17
+ error = check_result(::Libuv::Ext.check_init(reactor.handle, check_ptr))
16
18
 
17
19
  super(check_ptr, error)
18
20
  end
@@ -22,6 +24,7 @@ module Libuv
22
24
  return if @closed
23
25
  error = check_result ::Libuv::Ext.check_start(handle, callback(:on_check))
24
26
  reject(error) if error
27
+ self
25
28
  end
26
29
 
27
30
  # Disables the check handler.
@@ -29,13 +32,15 @@ module Libuv
29
32
  return if @closed
30
33
  error = check_result ::Libuv::Ext.check_stop(handle)
31
34
  reject(error) if error
35
+ self
32
36
  end
33
37
 
34
- # Used to update the callback that will be triggered on loop check
38
+ # Used to update the callback that will be triggered on reactor check
35
39
  #
36
- # @param callback [Proc] the callback to be called on loop check
40
+ # @param callback [Proc] the callback to be called on reactor check
37
41
  def progress(callback = nil, &blk)
38
42
  @callback = callback || blk
43
+ self
39
44
  end
40
45
 
41
46
 
@@ -43,11 +48,13 @@ module Libuv
43
48
 
44
49
 
45
50
  def on_check(handle)
46
- begin
47
- @callback.call
48
- rescue Exception => e
49
- @loop.log :error, :check_cb, e
50
- end
51
+ ::Fiber.new {
52
+ begin
53
+ @callback.call
54
+ rescue Exception => e
55
+ @reactor.log e, 'performing check callback'
56
+ end
57
+ }.resume
51
58
  end
52
59
  end
53
60
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fiber'
2
4
 
3
- class ::Libuv::Loop
4
- @@use_fibers = true
5
+ class CoroutineRejection < RuntimeError
6
+ attr_accessor :value
5
7
  end
6
8
 
7
9
  class Object
@@ -15,32 +17,57 @@ class Object
15
17
  # @return [Object] Returns the result of a single promise or an array of results if provided multiple promises
16
18
  # @raise [Exception] if the promise is rejected
17
19
  def co(*yieldable, &block)
20
+ on_reactor = Libuv::Reactor.current
21
+ raise 'must be running on a reactor thread to use coroutines' unless on_reactor
22
+
18
23
  f = Fiber.current
19
24
  wasError = false
20
25
 
21
- # Convert the input into a promise
26
+ # Convert the input into a promise on the current reactor
22
27
  if yieldable.length == 1
23
28
  promise = yieldable[0]
29
+ # Passed independently as this is often overwritten for performance
30
+ promise.progress(block) if block_given?
24
31
  else
25
- promise = ::Libuv::Loop.current.all(*yieldable)
32
+ promise = on_reactor.all(*yieldable)
26
33
  end
27
34
 
28
35
  # Use the promise to resume the Fiber
29
36
  promise.then(proc { |res|
30
- f.resume res
37
+ if Libuv::Reactor.current == on_reactor
38
+ f.resume res
39
+ else
40
+ on_reactor.schedule { f.resume(res) }
41
+ end
31
42
  }, proc { |err|
32
43
  wasError = true
33
- f.resume err
44
+ if Libuv::Reactor.current == on_reactor
45
+ f.resume err
46
+ else
47
+ on_reactor.schedule { f.resume(err) }
48
+ end
34
49
  })
35
50
 
36
- # Passed independently as this is often overwritten for performance
37
- promise.progress(block) if block_given?
38
-
39
- # Assign the result from the resume
40
- result = Fiber.yield
51
+ # We want to prevent the reactor from stopping while we are waiting on a response
52
+ on_reactor.ref
53
+ result = Fiber.yield # Assign the result from the resume
54
+ on_reactor.unref
41
55
 
42
56
  # Either return the result or raise an error
43
- raise result if wasError
57
+ if wasError
58
+ if result.is_a?(Exception)
59
+ raise result
60
+ else
61
+ e = case result
62
+ when String, Symbol
63
+ CoroutineRejection.new(result.to_s)
64
+ else
65
+ CoroutineRejection.new
66
+ end
67
+ e.value = result
68
+ raise e
69
+ end
70
+ end
44
71
  result
45
72
  end
46
73
  end
data/lib/libuv/dns.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Libuv
2
4
  class Dns < Q::DeferredPromise
3
5
  include Resource, Listener, Net
@@ -28,11 +30,11 @@ module Libuv
28
30
  end
29
31
 
30
32
 
31
- # @param loop [::Libuv::Loop] loop this work request will be associated
33
+ # @param reactor [::Libuv::Reactor] reactor this work request will be associated
32
34
  # @param domain [String] the domain name to resolve
33
35
  # @param port [Integer, String] the port we wish to use
34
- def initialize(loop, domain, port, hint = :IPv4)
35
- super(loop, loop.defer)
36
+ def initialize(reactor, domain, port, hint = :IPv4, wait: true)
37
+ super(reactor, reactor.defer)
36
38
 
37
39
  @domain = domain
38
40
  @port = port
@@ -42,13 +44,15 @@ module Libuv
42
44
  @error = nil # error in callback
43
45
 
44
46
  @instance_id = @pointer.address
45
- error = check_result ::Libuv::Ext.getaddrinfo(@loop, @pointer, callback(:on_complete), domain, port.to_s, HINTS[hint])
47
+ error = check_result ::Libuv::Ext.getaddrinfo(@reactor, @pointer, callback(:on_complete), domain, port.to_s, HINTS[hint])
46
48
 
47
49
  if error
48
50
  ::Libuv::Ext.free(@pointer)
49
51
  @complete = true
50
52
  @defer.reject(error)
51
53
  end
54
+
55
+ co(@defer.promise) if wait
52
56
  end
53
57
 
54
58
  # Indicates if the lookup has completed yet or not.
@@ -67,22 +71,25 @@ module Libuv
67
71
  ::Libuv::Ext.free(req)
68
72
 
69
73
  e = check_result(status)
70
- if e
71
- @defer.reject(e)
72
- else
73
- begin
74
- current = addrinfo
75
- @results = []
76
- while !current.null?
77
- @results << get_ip_and_port(current[:addr])
78
- current = current[:next]
79
- end
80
- @defer.resolve(@results)
81
- rescue Exception => e
74
+
75
+ ::Fiber.new {
76
+ if e
82
77
  @defer.reject(e)
78
+ else
79
+ begin
80
+ current = addrinfo
81
+ @results = []
82
+ while !current.null?
83
+ @results << get_ip_and_port(current[:addr])
84
+ current = current[:next]
85
+ end
86
+ @defer.resolve(@results)
87
+ rescue Exception => e
88
+ @defer.reject(e)
89
+ end
90
+ ::Libuv::Ext.freeaddrinfo(addrinfo)
83
91
  end
84
- ::Libuv::Ext.freeaddrinfo(addrinfo)
85
- end
92
+ }.resume
86
93
 
87
94
  # Clean up references
88
95
  cleanup_callbacks