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.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/.gitmodules +3 -0
- data/.rspec +1 -0
- data/.travis.yml +16 -0
- data/Gemfile +2 -0
- data/LICENSE +24 -0
- data/README.md +73 -0
- data/Rakefile +31 -0
- data/lib/libuv.rb +34 -0
- data/lib/libuv/assertions.rb +23 -0
- data/lib/libuv/async.rb +33 -0
- data/lib/libuv/check.rb +49 -0
- data/lib/libuv/error.rb +70 -0
- data/lib/libuv/ext/ext.rb +257 -0
- data/lib/libuv/ext/platform/darwin_x64.rb +12 -0
- data/lib/libuv/ext/platform/linux.rb +8 -0
- data/lib/libuv/ext/platform/unix.rb +14 -0
- data/lib/libuv/ext/platform/windows.rb +27 -0
- data/lib/libuv/ext/tasks.rb +27 -0
- data/lib/libuv/ext/tasks/mac.rb +23 -0
- data/lib/libuv/ext/tasks/unix.rb +23 -0
- data/lib/libuv/ext/tasks/win.rb +11 -0
- data/lib/libuv/ext/types.rb +230 -0
- data/lib/libuv/fs_event.rb +31 -0
- data/lib/libuv/handle.rb +82 -0
- data/lib/libuv/idle.rb +49 -0
- data/lib/libuv/listener.rb +34 -0
- data/lib/libuv/loop.rb +310 -0
- data/lib/libuv/net.rb +38 -0
- data/lib/libuv/pipe.rb +97 -0
- data/lib/libuv/prepare.rb +49 -0
- data/lib/libuv/q.rb +429 -0
- data/lib/libuv/resource.rb +28 -0
- data/lib/libuv/simple_async.rb +28 -0
- data/lib/libuv/stream.rb +124 -0
- data/lib/libuv/tcp.rb +194 -0
- data/lib/libuv/timer.rb +75 -0
- data/lib/libuv/tty.rb +34 -0
- data/lib/libuv/udp.rb +256 -0
- data/lib/libuv/version.rb +3 -0
- data/lib/libuv/work.rb +62 -0
- data/libuv.gemspec +54 -0
- data/spec/async_spec.rb +60 -0
- data/spec/defer_spec.rb +980 -0
- data/spec/idle_spec.rb +56 -0
- data/spec/pipe_spec.rb +148 -0
- data/spec/tcp_spec.rb +188 -0
- metadata +382 -0
data/lib/libuv/tty.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Libuv
|
2
|
+
class TTY < Handle
|
3
|
+
include Stream
|
4
|
+
|
5
|
+
|
6
|
+
def initialize(loop, fileno, readable)
|
7
|
+
@loop = loop
|
8
|
+
|
9
|
+
tty_ptr = ::Libuv::Ext.create_handle(:uv_tty)
|
10
|
+
error = check_result(::Libuv::Ext.tty_init(loop.handle, tty_ptr, fileno, readable ? 1 : 0))
|
11
|
+
|
12
|
+
super(tty_ptr, error)
|
13
|
+
end
|
14
|
+
|
15
|
+
def enable_raw_mode
|
16
|
+
check_result ::Libuv::Ext.tty_set_mode(handle, 1)
|
17
|
+
end
|
18
|
+
|
19
|
+
def disable_raw_mode
|
20
|
+
check_result ::Libuv::Ext.tty_set_mode(handle, 0)
|
21
|
+
end
|
22
|
+
|
23
|
+
def reset_mode
|
24
|
+
::Libuv::Ext.tty_reset_mode
|
25
|
+
end
|
26
|
+
|
27
|
+
def winsize
|
28
|
+
width = FFI::MemoryPointer.new(:int)
|
29
|
+
height = FFI::MemoryPointer.new(:int)
|
30
|
+
::Libuv::Ext.tty_get_winsize(handle, width, height)
|
31
|
+
[width.get_int(0), height.get_int(0)]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/libuv/udp.rb
ADDED
@@ -0,0 +1,256 @@
|
|
1
|
+
module Libuv
|
2
|
+
class UDP < Handle
|
3
|
+
include Net
|
4
|
+
|
5
|
+
|
6
|
+
SEND_DATA_ERROR = "data must be a String".freeze
|
7
|
+
TTL_ARGUMENT_ERROR = "ttl must be an Integer".freeze
|
8
|
+
MULTICAST_ARGUMENT_ERROR = "multicast_address must be a String".freeze
|
9
|
+
INTERFACE_ARGUMENT_ERROR = "interface_address must be a String".freeze
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(loop)
|
13
|
+
@loop = loop
|
14
|
+
|
15
|
+
udp_ptr = ::Libuv::Ext.create_handle(:uv_udp)
|
16
|
+
error = check_result(::Libuv::Ext.udp_init(loop.handle, udp_ptr))
|
17
|
+
|
18
|
+
super(udp_ptr, error)
|
19
|
+
end
|
20
|
+
|
21
|
+
def bind(ip, port, ipv6_only = false)
|
22
|
+
assert_type(String, ip, IP_ARGUMENT_ERROR)
|
23
|
+
assert_type(Integer, port, PORT_ARGUMENT_ERROR)
|
24
|
+
|
25
|
+
begin
|
26
|
+
@udp_socket = create_socket(IPAddr.new(ip), port)
|
27
|
+
@udp_socket.bind(ipv6_only)
|
28
|
+
rescue Exception => e
|
29
|
+
reject(e)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def sockname
|
34
|
+
sockaddr, len = get_sockaddr_and_len
|
35
|
+
check_result! ::Libuv::Ext.udp_getsockname(handle, sockaddr, len)
|
36
|
+
get_ip_and_port(UV::Sockaddr.new(sockaddr), len.get_int(0))
|
37
|
+
end
|
38
|
+
|
39
|
+
def join(multicast_address, interface_address)
|
40
|
+
assert_type(String, multicast_address, MULTICAST_ARGUMENT_ERROR)
|
41
|
+
assert_type(String, interface_address, INTERFACE_ARGUMENT_ERROR)
|
42
|
+
|
43
|
+
error = check_result ::Libuv::Ext.udp_set_membership(handle, multicast_address, interface_address, :uv_join_group)
|
44
|
+
reject(error) if error
|
45
|
+
end
|
46
|
+
|
47
|
+
def leave(multicast_address, interface_address)
|
48
|
+
assert_type(String, multicast_address, MULTICAST_ARGUMENT_ERROR)
|
49
|
+
assert_type(String, interface_address, INTERFACE_ARGUMENT_ERROR)
|
50
|
+
|
51
|
+
error = check_result ::Libuv::Ext.udp_set_membership(handle, multicast_address, interface_address, :uv_leave_group)
|
52
|
+
reject(error) if error
|
53
|
+
end
|
54
|
+
|
55
|
+
def start_recv
|
56
|
+
error = check_result ::Libuv::Ext.udp_recv_start(handle, callback(:on_allocate), callback(:on_recv))
|
57
|
+
reject(error) if error
|
58
|
+
end
|
59
|
+
|
60
|
+
def stop_recv
|
61
|
+
error = check_result ::Libuv::Ext.udp_recv_stop(handle)
|
62
|
+
reject(error) if error
|
63
|
+
end
|
64
|
+
|
65
|
+
def send(ip, port, data)
|
66
|
+
deferred = @loop.defer
|
67
|
+
begin
|
68
|
+
assert_type(String, ip, IP_ARGUMENT_ERROR)
|
69
|
+
assert_type(Integer, port, PORT_ARGUMENT_ERROR)
|
70
|
+
assert_type(String, data, SEND_DATA_ERROR)
|
71
|
+
|
72
|
+
@udp_socket = create_socket(IPAddr.new(ip), port)
|
73
|
+
|
74
|
+
# local as this variable will be avaliable until the handle is closed
|
75
|
+
@sent_callbacks = @sent_callbacks || []
|
76
|
+
|
77
|
+
#
|
78
|
+
# create the curried callback
|
79
|
+
#
|
80
|
+
callback = FFI::Function.new(:void, [:pointer, :int]) do |req, status|
|
81
|
+
::Libuv::Ext.free(req)
|
82
|
+
# remove the callback from the array
|
83
|
+
# assumes sends are done in order
|
84
|
+
promise = @sent_callbacks.shift[0]
|
85
|
+
resolve promise, status
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Save the callback and return the promise
|
90
|
+
#
|
91
|
+
begin
|
92
|
+
@sent_callbacks << [deferred, callback]
|
93
|
+
@udp_socket.send(data, callback)
|
94
|
+
rescue Exception => e
|
95
|
+
@sent_callbacks.pop
|
96
|
+
deferred.reject(e)
|
97
|
+
|
98
|
+
reject(e) # close the handle
|
99
|
+
end
|
100
|
+
rescue Exception => e
|
101
|
+
deferred.reject(e)
|
102
|
+
end
|
103
|
+
deferred.promise
|
104
|
+
end
|
105
|
+
|
106
|
+
def enable_multicast_loop
|
107
|
+
error = check_result ::Libuv::Ext.udp_set_multicast_loop(handle, 1)
|
108
|
+
reject(error) if error
|
109
|
+
end
|
110
|
+
|
111
|
+
def disable_multicast_loop
|
112
|
+
error = check_result ::Libuv::Ext.udp_set_multicast_loop(handle, 0)
|
113
|
+
reject(error) if error
|
114
|
+
end
|
115
|
+
|
116
|
+
def multicast_ttl=(ttl)
|
117
|
+
assert_type(Integer, ttl, TTL_ARGUMENT_ERROR)
|
118
|
+
error = check_result ::Libuv::Ext.udp_set_multicast_ttl(handle, ttl)
|
119
|
+
reject(error) if error
|
120
|
+
end
|
121
|
+
|
122
|
+
def enable_broadcast
|
123
|
+
error = check_result ::Libuv::Ext.udp_set_broadcast(handle, 1)
|
124
|
+
reject(error) if error
|
125
|
+
end
|
126
|
+
|
127
|
+
def disable_broadcast
|
128
|
+
error = check_result ::Libuv::Ext.udp_set_broadcast(handle, 0)
|
129
|
+
reject(error) if error
|
130
|
+
end
|
131
|
+
|
132
|
+
def ttl=(ttl)
|
133
|
+
assert_type(Integer, ttl, TTL_ARGUMENT_ERROR)
|
134
|
+
error = check_result ::Libuv::Ext.udp_set_ttl(handle, Integer(ttl))
|
135
|
+
reject(error) if error
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
|
142
|
+
def on_allocate(client, suggested_size)
|
143
|
+
::Libuv::Ext.buf_init(::Libuv::Ext.malloc(suggested_size), suggested_size)
|
144
|
+
end
|
145
|
+
|
146
|
+
def on_recv(handle, nread, buf, sockaddr, flags)
|
147
|
+
e = check_result(nread)
|
148
|
+
base = buf[:base]
|
149
|
+
|
150
|
+
if e
|
151
|
+
::Libuv::Ext.free(base)
|
152
|
+
reject(e)
|
153
|
+
else
|
154
|
+
data = base.read_string(nread)
|
155
|
+
::Libuv::Ext.free(base)
|
156
|
+
unless sockaddr.null?
|
157
|
+
ip, port = get_ip_and_port(UV::Sockaddr.new(sockaddr))
|
158
|
+
end
|
159
|
+
defer.notify(data, ip, port) # stream the data
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def create_socket(ip, port)
|
164
|
+
if ip.ipv4?
|
165
|
+
Socket4.new(@loop, handle, ip, port)
|
166
|
+
else
|
167
|
+
Socket6.new(@loop, handle, ip, port)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
module SocketMethods
|
173
|
+
include Resource
|
174
|
+
|
175
|
+
def initialize(loop, udp, ip, port)
|
176
|
+
@loop, @udp, @sockaddr = loop, udp, ip_addr(ip.to_s, port)
|
177
|
+
end
|
178
|
+
|
179
|
+
def bind(ipv6_only = false)
|
180
|
+
check_result! udp_bind(ipv6_only)
|
181
|
+
end
|
182
|
+
|
183
|
+
def send(data, callback)
|
184
|
+
check_result! udp_send(data, callback)
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
private
|
189
|
+
|
190
|
+
|
191
|
+
def send_req
|
192
|
+
::Libuv::Ext.create_request(:uv_udp_send)
|
193
|
+
end
|
194
|
+
|
195
|
+
def buf_init(data)
|
196
|
+
::Libuv::Ext.buf_init(FFI::MemoryPointer.from_string(data), data.respond_to?(:bytesize) ? data.bytesize : data.size)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
class Socket4
|
202
|
+
include SocketMethods
|
203
|
+
|
204
|
+
|
205
|
+
private
|
206
|
+
|
207
|
+
|
208
|
+
def ip_addr(ip, port)
|
209
|
+
::Libuv::Ext.ip4_addr(ip, port)
|
210
|
+
end
|
211
|
+
|
212
|
+
def udp_bind(ipv6_only)
|
213
|
+
::Libuv::Ext.udp_bind(@udp, @sockaddr, 0)
|
214
|
+
end
|
215
|
+
|
216
|
+
def udp_send(data, callback)
|
217
|
+
::Libuv::Ext.udp_send(
|
218
|
+
send_req,
|
219
|
+
@udp,
|
220
|
+
buf_init(data),
|
221
|
+
1,
|
222
|
+
@sockaddr,
|
223
|
+
callback
|
224
|
+
)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
class Socket6 < Socket
|
230
|
+
include SocketMethods
|
231
|
+
|
232
|
+
|
233
|
+
private
|
234
|
+
|
235
|
+
|
236
|
+
def ip_addr(ip, port)
|
237
|
+
::Libuv::Ext.ip6_addr(ip, port)
|
238
|
+
end
|
239
|
+
|
240
|
+
def udp_bind(ipv6_only)
|
241
|
+
::Libuv::Ext.udp_bind6(@udp, @sockaddr, ipv6_only ? 1 : 0)
|
242
|
+
end
|
243
|
+
|
244
|
+
def udp_send(data, callback)
|
245
|
+
::Libuv::Ext.udp_send6(
|
246
|
+
send_req,
|
247
|
+
@udp,
|
248
|
+
buf_init(data),
|
249
|
+
1,
|
250
|
+
@sockaddr,
|
251
|
+
callback
|
252
|
+
)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
data/lib/libuv/work.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module Libuv
|
2
|
+
class Work < Q::DeferredPromise
|
3
|
+
include Resource, Listener
|
4
|
+
|
5
|
+
|
6
|
+
def initialize(loop, work)
|
7
|
+
super(loop, loop.defer)
|
8
|
+
|
9
|
+
@work = work
|
10
|
+
@complete = false
|
11
|
+
@pointer = ::Libuv::Ext.create_request(:uv_work)
|
12
|
+
@error = nil # error in callback
|
13
|
+
|
14
|
+
error = check_result ::Libuv::Ext.queue_work(@loop, @pointer, callback(:on_work), callback(:on_complete))
|
15
|
+
if error
|
16
|
+
::Libuv::Ext.free(@pointer)
|
17
|
+
@complete = true
|
18
|
+
@defer.reject(error)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Attempt to cancel the pending work. Returns true if the work has completed or was canceled.
|
23
|
+
#
|
24
|
+
# @return [true, false]
|
25
|
+
def cancel
|
26
|
+
if not @complete
|
27
|
+
@complete = ::Libuv::Ext.cancel(@pointer) >= 0
|
28
|
+
end
|
29
|
+
@complete
|
30
|
+
end
|
31
|
+
|
32
|
+
# Indicates is the work has completed yet or not.
|
33
|
+
#
|
34
|
+
# @return [true, false]
|
35
|
+
def completed?
|
36
|
+
return @complete
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
|
43
|
+
def on_complete(req, status)
|
44
|
+
@complete = true
|
45
|
+
::Libuv::Ext.free(req)
|
46
|
+
|
47
|
+
if @error
|
48
|
+
@defer.reject(@error)
|
49
|
+
else
|
50
|
+
resolve @defer, status
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def on_work(req)
|
55
|
+
begin
|
56
|
+
@work.call
|
57
|
+
rescue Exception => e
|
58
|
+
@error = e # Catch errors for promise resolution
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/libuv.gemspec
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.expand_path("../lib/libuv/version", __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = "libuv"
|
5
|
+
gem.version = Libuv::VERSION
|
6
|
+
gem.license = 'MIT'
|
7
|
+
gem.authors = ["Bulat Shakirzyanov", "Stephen von Takach"]
|
8
|
+
gem.email = ["mallluhuct@gmail.com", "steve@cotag.me"]
|
9
|
+
gem.homepage = "https://github.com/cotag/libuv"
|
10
|
+
gem.summary = "libuv bindings for Ruby"
|
11
|
+
gem.description = "An opinionated wrapper around libuv for Ruby"
|
12
|
+
|
13
|
+
gem.extensions << "ext/Rakefile"
|
14
|
+
|
15
|
+
gem.required_ruby_version = '>= 1.9.2'
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
|
18
|
+
gem.add_runtime_dependency 'ffi', '>= 1.9'
|
19
|
+
gem.add_runtime_dependency 'thread_safe'
|
20
|
+
gem.add_development_dependency 'rspec', '>= 2.14'
|
21
|
+
gem.add_development_dependency 'rake', '>= 10.1'
|
22
|
+
gem.add_development_dependency 'yard'
|
23
|
+
|
24
|
+
gem.files = `git ls-files`.split("\n")
|
25
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
26
|
+
|
27
|
+
# Add the submodule to the gem
|
28
|
+
relative_path = File.expand_path("../", __FILE__) + '/'
|
29
|
+
`git submodule --quiet foreach pwd`.split($\).each do |submodule_path|
|
30
|
+
|
31
|
+
if (ENV['OS'] == 'Windows_NT') && submodule_path[0] == '/'
|
32
|
+
# Detect if cygwin path is being used by git
|
33
|
+
submodule_path = submodule_path[1..-1]
|
34
|
+
submodule_path.insert(1, ':')
|
35
|
+
end
|
36
|
+
|
37
|
+
# for each submodule, change working directory to that submodule
|
38
|
+
Dir.chdir(submodule_path) do
|
39
|
+
# Make the submodule path relative
|
40
|
+
submodule_path = submodule_path.gsub(/#{relative_path}/i, '')
|
41
|
+
|
42
|
+
# issue git ls-files in submodule's directory
|
43
|
+
submodule_files = `git ls-files`.split($\)
|
44
|
+
|
45
|
+
# prepend the submodule path to create relative file paths
|
46
|
+
submodule_files_paths = submodule_files.map do |filename|
|
47
|
+
File.join(submodule_path, filename)
|
48
|
+
end
|
49
|
+
|
50
|
+
# add relative paths to gem.files
|
51
|
+
gem.files += submodule_files_paths
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/spec/async_spec.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'libuv'
|
2
|
+
|
3
|
+
|
4
|
+
describe Libuv::Async do
|
5
|
+
before :each do
|
6
|
+
@log = []
|
7
|
+
@general_failure = []
|
8
|
+
|
9
|
+
@loop = Libuv::Loop.new
|
10
|
+
@call = @loop.pipe
|
11
|
+
@timeout = @loop.timer do
|
12
|
+
@loop.stop
|
13
|
+
@general_failure << "test timed out"
|
14
|
+
end
|
15
|
+
@timeout.start(5000)
|
16
|
+
|
17
|
+
@loop.all(@server, @client, @timeout).catch do |reason|
|
18
|
+
@general_failure << reason.inspect
|
19
|
+
p "Failed with: #{reason.message}\n#{reason.backtrace.join("\n")}\n"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
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|
|
26
|
+
begin
|
27
|
+
p "Log called: #{level}: #{errorid}\n#{error.message}\n#{error.backtrace.join("\n")}\n"
|
28
|
+
rescue Exception
|
29
|
+
p 'error in logger'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
@count = 0
|
34
|
+
|
35
|
+
timer = @loop.timer do
|
36
|
+
@count += 1
|
37
|
+
end
|
38
|
+
timer.start(0, 200)
|
39
|
+
|
40
|
+
callback = @loop.async do
|
41
|
+
stopper = @loop.timer do
|
42
|
+
timer.close
|
43
|
+
callback.close
|
44
|
+
stopper.close
|
45
|
+
@loop.stop
|
46
|
+
end
|
47
|
+
stopper.start(1000)
|
48
|
+
end
|
49
|
+
|
50
|
+
@loop.work(proc {
|
51
|
+
callback.call
|
52
|
+
}).catch do |err|
|
53
|
+
@general_failure << err
|
54
|
+
end
|
55
|
+
}
|
56
|
+
|
57
|
+
@general_failure.should == []
|
58
|
+
(@count < 7 && @count > 3).should == true
|
59
|
+
end
|
60
|
+
end
|