libuv 2.0.12 → 3.0.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 (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
data/lib/libuv/tty.rb CHANGED
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Libuv
2
4
  class TTY < Handle
3
5
  include Stream
4
6
 
5
7
 
6
- def initialize(loop, fileno, readable)
7
- @loop = loop
8
+ def initialize(reactor, fileno, readable)
9
+ @reactor = reactor
8
10
 
9
11
  tty_ptr = ::Libuv::Ext.allocate_handle_tty
10
- error = check_result(::Libuv::Ext.tty_init(loop.handle, tty_ptr, fileno, readable ? 1 : 0))
12
+ error = check_result(::Libuv::Ext.tty_init(reactor.handle, tty_ptr, fileno, readable ? 1 : 0))
11
13
 
12
14
  super(tty_ptr, error)
13
15
  end
@@ -15,15 +17,18 @@ module Libuv
15
17
  def enable_raw_mode
16
18
  return if @closed
17
19
  check_result ::Libuv::Ext.tty_set_mode(handle, 1)
20
+ self
18
21
  end
19
22
 
20
23
  def disable_raw_mode
21
24
  return if @closed
22
25
  check_result ::Libuv::Ext.tty_set_mode(handle, 0)
26
+ self
23
27
  end
24
28
 
25
29
  def reset_mode
26
30
  ::Libuv::Ext.tty_reset_mode
31
+ self
27
32
  end
28
33
 
29
34
  def winsize
data/lib/libuv/udp.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ipaddr'
2
4
 
3
5
 
@@ -11,18 +13,19 @@ module Libuv
11
13
  define_callback function: :send_complete, params: [:pointer, :int]
12
14
 
13
15
 
14
- SEND_DATA_ERROR = "data must be a String".freeze
15
- TTL_ARGUMENT_ERROR = "ttl must be an Integer".freeze
16
- MULTICAST_ARGUMENT_ERROR = "multicast_address must be a String".freeze
17
- INTERFACE_ARGUMENT_ERROR = "interface_address must be a String".freeze
18
- HANDLE_CLOSED_ERROR = "unable to send as handle closed".freeze
16
+ SEND_DATA_ERROR = "data must be a String"
17
+ TTL_ARGUMENT_ERROR = "ttl must be an Integer"
18
+ MULTICAST_ARGUMENT_ERROR = "multicast_address must be a String"
19
+ INTERFACE_ARGUMENT_ERROR = "interface_address must be a String"
20
+ HANDLE_CLOSED_ERROR = "unable to send as handle closed"
19
21
 
20
22
 
21
- def initialize(loop)
22
- @loop = loop
23
+ def initialize(reactor, progress: nil)
24
+ @reactor = reactor
25
+ @progress = progress
23
26
 
24
27
  udp_ptr = ::Libuv::Ext.allocate_handle_udp
25
- error = check_result(::Libuv::Ext.udp_init(loop.handle, udp_ptr))
28
+ error = check_result(::Libuv::Ext.udp_init(reactor.handle, udp_ptr))
26
29
  @request_refs = {}
27
30
 
28
31
  super(udp_ptr, error)
@@ -36,12 +39,16 @@ module Libuv
36
39
  sockaddr = create_sockaddr(ip, port)
37
40
  error = check_result ::Libuv::Ext.udp_bind(handle, sockaddr, 0)
38
41
  reject(error) if error
42
+
43
+ self
39
44
  end
40
45
 
41
46
  def open(fd, binding = true, callback = nil, &blk)
42
47
  return if @closed
43
48
  error = check_result UV.udp_open(handle, fd)
44
49
  reject(error) if error
50
+
51
+ self
45
52
  end
46
53
 
47
54
  def sockname
@@ -58,6 +65,7 @@ module Libuv
58
65
 
59
66
  error = check_result ::Libuv::Ext.udp_set_membership(handle, multicast_address, interface_address, :uv_join_group)
60
67
  reject(error) if error
68
+ self
61
69
  end
62
70
 
63
71
  def leave(multicast_address, interface_address)
@@ -67,6 +75,7 @@ module Libuv
67
75
 
68
76
  error = check_result ::Libuv::Ext.udp_set_membership(handle, multicast_address, interface_address, :uv_leave_group)
69
77
  reject(error) if error
78
+ self
70
79
  end
71
80
 
72
81
  # Starts reading from the handle
@@ -75,6 +84,7 @@ module Libuv
75
84
  return if @closed
76
85
  error = check_result ::Libuv::Ext.udp_recv_start(handle, callback(:on_allocate), callback(:on_recv))
77
86
  reject(error) if error
87
+ self
78
88
  end
79
89
 
80
90
  # Stops reading from the handle
@@ -83,6 +93,7 @@ module Libuv
83
93
  return if @closed
84
94
  error = check_result ::Libuv::Ext.udp_recv_stop(handle)
85
95
  reject(error) if error
96
+ self
86
97
  end
87
98
 
88
99
  def try_send(ip, port, data)
@@ -108,9 +119,9 @@ module Libuv
108
119
  return result
109
120
  end
110
121
 
111
- def send(ip, port, data)
122
+ def send(ip, port, data, wait: false)
112
123
  # NOTE:: Similar to stream.rb -> write
113
- deferred = @loop.defer
124
+ deferred = @reactor.defer
114
125
  if !@closed
115
126
  begin
116
127
  assert_type(String, ip, IP_ARGUMENT_ERROR)
@@ -149,18 +160,27 @@ module Libuv
149
160
  deferred.reject(RuntimeError.new(HANDLE_CLOSED_ERROR))
150
161
  end
151
162
  deferred.promise
163
+
164
+ if wait
165
+ return deferred.promise if wait == :promise
166
+ co deferred.promise
167
+ end
168
+
169
+ self
152
170
  end
153
171
 
154
- def enable_multicast_loop
172
+ def enable_multicast_reactor
155
173
  return if @closed
156
- error = check_result ::Libuv::Ext.udp_set_multicast_loop(handle, 1)
174
+ error = check_result ::Libuv::Ext.udp_set_multicast_reactor(handle, 1)
157
175
  reject(error) if error
176
+ self
158
177
  end
159
178
 
160
- def disable_multicast_loop
179
+ def disable_multicast_reactor
161
180
  return if @closed
162
- error = check_result ::Libuv::Ext.udp_set_multicast_loop(handle, 0)
181
+ error = check_result ::Libuv::Ext.udp_set_multicast_reactor(handle, 0)
163
182
  reject(error) if error
183
+ self
164
184
  end
165
185
 
166
186
  def multicast_ttl=(ttl)
@@ -168,18 +188,21 @@ module Libuv
168
188
  assert_type(Integer, ttl, TTL_ARGUMENT_ERROR)
169
189
  error = check_result ::Libuv::Ext.udp_set_multicast_ttl(handle, ttl)
170
190
  reject(error) if error
191
+ self
171
192
  end
172
193
 
173
194
  def enable_broadcast
174
195
  return if @closed
175
196
  error = check_result ::Libuv::Ext.udp_set_broadcast(handle, 1)
176
197
  reject(error) if error
198
+ self
177
199
  end
178
200
 
179
201
  def disable_broadcast
180
202
  return if @closed
181
203
  error = check_result ::Libuv::Ext.udp_set_broadcast(handle, 0)
182
204
  reject(error) if error
205
+ self
183
206
  end
184
207
 
185
208
  def ttl=(ttl)
@@ -187,10 +210,12 @@ module Libuv
187
210
  assert_type(Integer, ttl, TTL_ARGUMENT_ERROR)
188
211
  error = check_result ::Libuv::Ext.udp_set_ttl(handle, Integer(ttl))
189
212
  reject(error) if error
213
+ self
190
214
  end
191
215
 
192
216
  def progress(callback = nil, &blk)
193
217
  @progress = callback || blk
218
+ self
194
219
  end
195
220
 
196
221
 
@@ -239,18 +264,20 @@ module Libuv
239
264
  e = check_result(nread)
240
265
 
241
266
  if e
242
- reject(e) # Will call close
267
+ ::Fiber.new { reject(e) }.resume # Will call close
243
268
  elsif nread > 0
244
269
  data = @receive_buff.read_string(nread)
245
270
  unless sockaddr.null?
246
271
  ip, port = get_ip_and_port(sockaddr)
247
272
  end
248
-
249
- begin
250
- @progress.call data, ip, port, self
251
- rescue Exception => e
252
- @loop.log :error, :udp_progress_cb, e
253
- end
273
+
274
+ ::Fiber.new {
275
+ begin
276
+ @progress.call data, ip, port, self
277
+ rescue Exception => e
278
+ @reactor.log e, 'performing UDP data received callback'
279
+ end
280
+ }.resume
254
281
  else
255
282
  ::Libuv::Ext.free(@receive_buff)
256
283
  @receive_buff = nil
@@ -265,7 +292,7 @@ module Libuv
265
292
  ::Libuv::Ext.free(req)
266
293
  buffer1.free
267
294
 
268
- resolve deferred, status
295
+ ::Fiber.new { resolve deferred, status }.resume
269
296
  end
270
297
  end
271
298
  end
data/lib/libuv/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Libuv
2
- VERSION = '2.0.12'
4
+ VERSION = '3.0.0'
3
5
  end
data/lib/libuv/work.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Libuv
2
4
  class Work < Q::DeferredPromise
3
5
  include Resource, Listener
@@ -11,7 +13,7 @@ module Libuv
11
13
  define_callback function: :on_complete, params: [:pointer, :int]
12
14
 
13
15
 
14
- # @param thread [::Libuv::Loop] thread this work request will be associated
16
+ # @param thread [::Libuv::Reactor] thread this work request will be associated
15
17
  # @param work [Proc] callback to be called in the thread pool
16
18
  def initialize(thread, work)
17
19
  super(thread, thread.defer)
@@ -23,7 +25,7 @@ module Libuv
23
25
 
24
26
  @instance_id = @pointer.address
25
27
 
26
- error = check_result ::Libuv::Ext.queue_work(@loop, @pointer, callback(:on_work), callback(:on_complete))
28
+ error = check_result ::Libuv::Ext.queue_work(@reactor, @pointer, callback(:on_work), callback(:on_complete))
27
29
  if error
28
30
  ::Libuv::Ext.free(@pointer)
29
31
  @complete = true
@@ -56,16 +58,18 @@ module Libuv
56
58
  @complete = true
57
59
  ::Libuv::Ext.free(req)
58
60
 
59
- e = check_result(status)
60
- if e
61
- @defer.reject(e)
62
- else
63
- if @error
64
- @defer.reject(@error)
61
+ ::Fiber.new {
62
+ e = check_result(status)
63
+ if e
64
+ @defer.reject(e)
65
65
  else
66
- @defer.resolve(@result)
66
+ if @error
67
+ @defer.reject(@error)
68
+ else
69
+ @defer.resolve(@result)
70
+ end
67
71
  end
68
- end
72
+ }.resume
69
73
 
70
74
  # Clean up references
71
75
  cleanup_callbacks @instance_id
data/libuv.gemspec CHANGED
@@ -1,27 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require File.expand_path("../lib/libuv/version", __FILE__)
2
4
 
3
5
  Gem::Specification.new do |gem|
4
6
  gem.name = "libuv"
5
7
  gem.version = Libuv::VERSION
6
8
  gem.license = 'MIT'
7
- gem.authors = ["Bulat Shakirzyanov", "Stephen von Takach"]
8
- gem.email = ["mallluhuct@gmail.com", "steve@cotag.me"]
9
+ gem.authors = ["Stephen von Takach"]
10
+ gem.email = ["steve@cotag.me"]
9
11
  gem.homepage = "https://github.com/cotag/libuv"
10
12
  gem.summary = "libuv bindings for Ruby"
11
13
  gem.description = "An opinionated wrapper around libuv for Ruby"
12
14
 
13
15
  gem.extensions << "ext/Rakefile"
14
16
 
15
- gem.required_ruby_version = '>= 1.9.2'
17
+ gem.required_ruby_version = '>= 2.0.0'
16
18
  gem.require_paths = ["lib"]
17
19
 
18
- gem.add_runtime_dependency 'ffi', '>= 1.9'
19
- gem.add_runtime_dependency 'thread_safe'
20
- gem.add_runtime_dependency 'ruby-tls', '>= 2.0.0'
20
+ gem.add_runtime_dependency 'ffi', '~> 1.9'
21
+ gem.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
22
+ gem.add_runtime_dependency 'ruby-tls', '~> 2.1'
21
23
 
22
- gem.add_development_dependency 'rspec', '>= 2.14'
23
- gem.add_development_dependency 'rake', '>= 10.1'
24
- gem.add_development_dependency 'yard'
24
+ gem.add_development_dependency 'rspec', '~> 3.5'
25
+ gem.add_development_dependency 'rake', '~> 11.2'
26
+ gem.add_development_dependency 'yard', '~> 0.9'
25
27
 
26
28
  gem.files = `git ls-files`.split("\n")
27
29
  gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
data/spec/async_spec.rb CHANGED
@@ -6,25 +6,25 @@ describe Libuv::Async do
6
6
  @log = []
7
7
  @general_failure = []
8
8
 
9
- @loop = Libuv::Loop.default
10
- @call = @loop.pipe
11
- @timeout = @loop.timer do
12
- @loop.stop
9
+ @reactor = Libuv::Reactor.default
10
+ @call = @reactor.pipe
11
+ @timeout = @reactor.timer do
12
+ @reactor.stop
13
13
  @general_failure << "test timed out"
14
14
  end
15
15
  @timeout.start(5000)
16
16
 
17
- @loop.all(@server, @client, @timeout).catch do |reason|
17
+ @reactor.all(@server, @client, @timeout).catch do |reason|
18
18
  @general_failure << reason.inspect
19
19
  p "Failed with: #{reason.message}\n#{reason.backtrace.join("\n")}\n"
20
20
  end
21
21
  end
22
22
 
23
23
  it "Should call the async function from the thread pool stopping the counter" do
24
- @loop.run { |logger|
25
- logger.progress do |level, errorid, error|
24
+ @reactor.run { |reactor|
25
+ reactor.notifier do |error, context|
26
26
  begin
27
- p "Log called: #{level}: #{errorid}\n#{error.message}\n#{error.backtrace.join("\n")}\n"
27
+ p "Log called: #{context}\n#{error.message}\n#{error.backtrace.join("\n")}\n"
28
28
  rescue Exception
29
29
  p 'error in logger'
30
30
  end
@@ -32,24 +32,24 @@ describe Libuv::Async do
32
32
 
33
33
  @count = 0
34
34
 
35
- timer = @loop.timer do
35
+ timer = @reactor.timer do
36
36
  @count += 1
37
37
  end
38
38
  timer.start(0, 200)
39
39
 
40
- callback = @loop.async do
41
- stopper = @loop.timer do
40
+ callback = @reactor.async do
41
+ stopper = @reactor.timer do
42
42
  timer.close
43
43
  callback.close
44
44
  stopper.close
45
45
  @timeout.close
46
- @loop.stop
46
+ @reactor.stop
47
47
  end
48
48
  stopper.start(1000)
49
49
  callback.close
50
50
  end
51
51
 
52
- @loop.work(proc {
52
+ @reactor.work(proc {
53
53
  callback.call
54
54
  }).catch do |err|
55
55
  @general_failure << err
@@ -10,10 +10,18 @@ if RUBY_PLATFORM != 'java'
10
10
  @log = []
11
11
  @general_failure = []
12
12
 
13
- @loop = Libuv::Loop.default
14
- @timeout = @loop.timer do
13
+ @reactor = Libuv::Reactor.default
14
+ @reactor.notifier do |error, context|
15
+ begin
16
+ @general_failure << "Log called: #{context}\n#{error.message}\n#{error.backtrace.join("\n") if error.backtrace}\n"
17
+ rescue Exception => e
18
+ @general_failure << "error in logger #{e.inspect}"
19
+ end
20
+ end
21
+
22
+ @timeout = @reactor.timer do
15
23
  @timeout.close
16
- @loop.stop
24
+ @reactor.stop
17
25
  @general_failure << "test timed out"
18
26
  end
19
27
  @timeout.start(5000)
@@ -21,24 +29,15 @@ if RUBY_PLATFORM != 'java'
21
29
 
22
30
  describe 'serial execution' do
23
31
  it "should wait for work to complete and return the result" do
24
- @loop.run { |logger|
25
- logger.progress do |level, errorid, error|
26
- begin
27
- @general_failure << "Log called: #{level}: #{errorid}\n#{error.message}\n#{error.backtrace.join("\n") if error.backtrace}\n"
28
- rescue Exception => e
29
- @general_failure << "error in logger #{e.inspect}"
30
- end
31
- end
32
+ @reactor.run { |reactor|
32
33
 
33
-
34
- @log << co(@loop.work(proc {
34
+ @log << co(@reactor.work(proc {
35
35
  sleep 1
36
36
  'work done'
37
37
  }))
38
38
  @log << 'after work'
39
39
 
40
40
  @timeout.close
41
- @loop.stop
42
41
  }
43
42
 
44
43
  expect(@general_failure).to eq([])
@@ -46,17 +45,9 @@ if RUBY_PLATFORM != 'java'
46
45
  end
47
46
 
48
47
  it "should raise an error if the promise is rejected" do
49
- @loop.run { |logger|
50
- logger.progress do |level, errorid, error|
51
- begin
52
- @general_failure << "Log called: #{level}: #{errorid}\n#{error.message}\n#{error.backtrace.join("\n") if error.backtrace}\n"
53
- rescue Exception => e
54
- @general_failure << "error in logger #{e.inspect}"
55
- end
56
- end
57
-
48
+ @reactor.run { |reactor|
58
49
  begin
59
- @log << co(@loop.work(proc {
50
+ @log << co(@reactor.work(proc {
60
51
  raise 'rejected'
61
52
  }))
62
53
  @log << 'after work'
@@ -65,7 +56,6 @@ if RUBY_PLATFORM != 'java'
65
56
  end
66
57
 
67
58
  @timeout.close
68
- @loop.stop
69
59
  }
70
60
 
71
61
  expect(@general_failure).to eq([])
@@ -73,22 +63,13 @@ if RUBY_PLATFORM != 'java'
73
63
  end
74
64
 
75
65
  it "should return the results of multiple promises" do
76
- @loop.run { |logger|
77
- logger.progress do |level, errorid, error|
78
- begin
79
- @general_failure << "Log called: #{level}: #{errorid}\n#{error.message}\n#{error.backtrace.join("\n") if error.backtrace}\n"
80
- rescue Exception => e
81
- @general_failure << "error in logger #{e.inspect}"
82
- end
83
- end
84
-
85
-
86
- job1 = @loop.work(proc {
66
+ @reactor.run { |reactor|
67
+ job1 = @reactor.work(proc {
87
68
  sleep 1
88
69
  'job1'
89
70
  })
90
71
 
91
- job2 = @loop.work(proc {
72
+ job2 = @reactor.work(proc {
92
73
  sleep 1
93
74
  'job2'
94
75
  })
@@ -101,7 +82,6 @@ if RUBY_PLATFORM != 'java'
101
82
  @log << 'after work'
102
83
 
103
84
  @timeout.close
104
- @loop.stop
105
85
  }
106
86
 
107
87
  expect(@general_failure).to eq([])
@@ -110,16 +90,8 @@ if RUBY_PLATFORM != 'java'
110
90
 
111
91
 
112
92
  it "should provide a callback option for progress events" do
113
- @loop.run { |logger|
114
- logger.progress do |level, errorid, error|
115
- begin
116
- @general_failure << "Log called: #{level}: #{errorid}\n#{error.message}\n#{error.backtrace.join("\n") if error.backtrace}\n"
117
- rescue Exception => e
118
- @general_failure << "error in logger #{e.inspect}"
119
- end
120
- end
121
-
122
- timer = @loop.timer
93
+ @reactor.run { |reactor|
94
+ timer = @reactor.timer
123
95
  timer.start(0)
124
96
  co(timer) do
125
97
  @log << 'in timer'
@@ -127,9 +99,7 @@ if RUBY_PLATFORM != 'java'
127
99
  end
128
100
 
129
101
  @log << 'after timer'
130
-
131
102
  @timeout.close
132
- @loop.stop
133
103
  }
134
104
 
135
105
  expect(@log).to eq(['in timer', 'after timer'])