mt-libuv 4.1.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 +7 -0
- data/.gitignore +22 -0
- data/.gitmodules +6 -0
- data/.rspec +1 -0
- data/.travis.yml +24 -0
- data/Gemfile +9 -0
- data/LICENSE +24 -0
- data/README.md +195 -0
- data/Rakefile +31 -0
- data/ext/README.md +6 -0
- data/ext/Rakefile +28 -0
- data/lib/mt-libuv/async.rb +51 -0
- data/lib/mt-libuv/check.rb +59 -0
- data/lib/mt-libuv/coroutines.rb +79 -0
- data/lib/mt-libuv/dns.rb +98 -0
- data/lib/mt-libuv/error.rb +88 -0
- data/lib/mt-libuv/ext/ext.rb +322 -0
- data/lib/mt-libuv/ext/platform/darwin_x64.rb +61 -0
- data/lib/mt-libuv/ext/platform/unix.rb +69 -0
- data/lib/mt-libuv/ext/platform/windows.rb +83 -0
- data/lib/mt-libuv/ext/tasks/mac.rb +24 -0
- data/lib/mt-libuv/ext/tasks/unix.rb +42 -0
- data/lib/mt-libuv/ext/tasks/win.rb +29 -0
- data/lib/mt-libuv/ext/tasks.rb +27 -0
- data/lib/mt-libuv/ext/types.rb +253 -0
- data/lib/mt-libuv/fiber_pool.rb +83 -0
- data/lib/mt-libuv/file.rb +309 -0
- data/lib/mt-libuv/filesystem.rb +263 -0
- data/lib/mt-libuv/fs_event.rb +37 -0
- data/lib/mt-libuv/handle.rb +108 -0
- data/lib/mt-libuv/idle.rb +59 -0
- data/lib/mt-libuv/mixins/accessors.rb +41 -0
- data/lib/mt-libuv/mixins/assertions.rb +25 -0
- data/lib/mt-libuv/mixins/fs_checks.rb +96 -0
- data/lib/mt-libuv/mixins/listener.rb +69 -0
- data/lib/mt-libuv/mixins/net.rb +42 -0
- data/lib/mt-libuv/mixins/resource.rb +30 -0
- data/lib/mt-libuv/mixins/stream.rb +276 -0
- data/lib/mt-libuv/pipe.rb +217 -0
- data/lib/mt-libuv/prepare.rb +59 -0
- data/lib/mt-libuv/q.rb +475 -0
- data/lib/mt-libuv/reactor.rb +567 -0
- data/lib/mt-libuv/signal.rb +62 -0
- data/lib/mt-libuv/spawn.rb +113 -0
- data/lib/mt-libuv/tcp.rb +465 -0
- data/lib/mt-libuv/timer.rb +107 -0
- data/lib/mt-libuv/tty.rb +42 -0
- data/lib/mt-libuv/udp.rb +302 -0
- data/lib/mt-libuv/version.rb +5 -0
- data/lib/mt-libuv/work.rb +86 -0
- data/lib/mt-libuv.rb +80 -0
- data/mt-libuv.gemspec +62 -0
- data/spec/async_spec.rb +67 -0
- data/spec/coroutines_spec.rb +121 -0
- data/spec/cpu_spec.rb +10 -0
- data/spec/defer_spec.rb +906 -0
- data/spec/dns_spec.rb +110 -0
- data/spec/dsl_spec.rb +43 -0
- data/spec/filesystem_spec.rb +270 -0
- data/spec/idle_spec.rb +44 -0
- data/spec/pipe_spec.rb +151 -0
- data/spec/spawn_spec.rb +119 -0
- data/spec/tcp_spec.rb +272 -0
- data/spec/test.sh +4 -0
- data/spec/test_fail.sh +3 -0
- data/spec/test_read.sh +3 -0
- data/spec/timer_spec.rb +14 -0
- data/spec/udp_spec.rb +73 -0
- data/spec/zen_spec.rb +34 -0
- metadata +196 -0
data/lib/mt-libuv/tty.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MTLibuv
|
4
|
+
class TTY < Handle
|
5
|
+
include Stream
|
6
|
+
|
7
|
+
|
8
|
+
def initialize(reactor, fileno, readable)
|
9
|
+
@reactor = reactor
|
10
|
+
|
11
|
+
tty_ptr = ::MTLibuv::Ext.allocate_handle_tty
|
12
|
+
error = check_result(::MTLibuv::Ext.tty_init(reactor.handle, tty_ptr, fileno, readable ? 1 : 0))
|
13
|
+
|
14
|
+
super(tty_ptr, error)
|
15
|
+
end
|
16
|
+
|
17
|
+
def enable_raw_mode
|
18
|
+
return if @closed
|
19
|
+
check_result ::MTLibuv::Ext.tty_set_mode(handle, 1)
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def disable_raw_mode
|
24
|
+
return if @closed
|
25
|
+
check_result ::MTLibuv::Ext.tty_set_mode(handle, 0)
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def reset_mode
|
30
|
+
::MTLibuv::Ext.tty_reset_mode
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def winsize
|
35
|
+
return [] if @closed
|
36
|
+
width = FFI::MemoryPointer.new(:int)
|
37
|
+
height = FFI::MemoryPointer.new(:int)
|
38
|
+
::MTLibuv::Ext.tty_get_winsize(handle, width, height)
|
39
|
+
[width.get_int(0), height.get_int(0)]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/mt-libuv/udp.rb
ADDED
@@ -0,0 +1,302 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ipaddr'
|
4
|
+
|
5
|
+
|
6
|
+
module MTLibuv
|
7
|
+
class UDP < Handle
|
8
|
+
include Net
|
9
|
+
|
10
|
+
|
11
|
+
define_callback function: :on_allocate, params: [:pointer, :size_t, Ext::UvBuf.by_ref]
|
12
|
+
define_callback function: :on_recv, params: [:pointer, :ssize_t, Ext::UvBuf.by_ref, Ext::Sockaddr.by_ref, :uint]
|
13
|
+
define_callback function: :send_complete, params: [:pointer, :int]
|
14
|
+
|
15
|
+
|
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"
|
21
|
+
|
22
|
+
|
23
|
+
def initialize(reactor, progress: nil, flags: nil)
|
24
|
+
@reactor = reactor
|
25
|
+
@progress = progress
|
26
|
+
|
27
|
+
udp_ptr = ::MTLibuv::Ext.allocate_handle_udp
|
28
|
+
error = if flags
|
29
|
+
check_result(::MTLibuv::Ext.udp_init_ex(reactor.handle, udp_ptr, flags))
|
30
|
+
else
|
31
|
+
check_result(::MTLibuv::Ext.udp_init(reactor.handle, udp_ptr))
|
32
|
+
end
|
33
|
+
@request_refs = {}
|
34
|
+
|
35
|
+
super(udp_ptr, error)
|
36
|
+
end
|
37
|
+
|
38
|
+
def bind(ip, port)
|
39
|
+
return if @closed
|
40
|
+
assert_type(String, ip, IP_ARGUMENT_ERROR)
|
41
|
+
assert_type(Integer, port, PORT_ARGUMENT_ERROR)
|
42
|
+
|
43
|
+
sockaddr = create_sockaddr(ip, port)
|
44
|
+
error = check_result ::MTLibuv::Ext.udp_bind(handle, sockaddr, 0)
|
45
|
+
reject(error) if error
|
46
|
+
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def open(fd, binding = true)
|
51
|
+
return if @closed
|
52
|
+
error = check_result ::MTLibuv::Ext.udp_open(handle, fd)
|
53
|
+
reject(error) if error
|
54
|
+
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def sockname
|
59
|
+
return [] if @closed
|
60
|
+
sockaddr, len = get_sockaddr_and_len
|
61
|
+
check_result! ::MTLibuv::Ext.udp_getsockname(handle, sockaddr, len)
|
62
|
+
get_ip_and_port(::MTLibuv::Ext::Sockaddr.new(sockaddr), len.get_int(0))
|
63
|
+
end
|
64
|
+
|
65
|
+
def join(multicast_address, interface_address)
|
66
|
+
return if @closed
|
67
|
+
assert_type(String, multicast_address, MULTICAST_ARGUMENT_ERROR)
|
68
|
+
assert_type(String, interface_address, INTERFACE_ARGUMENT_ERROR)
|
69
|
+
|
70
|
+
error = check_result ::MTLibuv::Ext.udp_set_membership(handle, multicast_address, interface_address, :uv_join_group)
|
71
|
+
reject(error) if error
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
def leave(multicast_address, interface_address)
|
76
|
+
return if @closed
|
77
|
+
assert_type(String, multicast_address, MULTICAST_ARGUMENT_ERROR)
|
78
|
+
assert_type(String, interface_address, INTERFACE_ARGUMENT_ERROR)
|
79
|
+
|
80
|
+
error = check_result ::MTLibuv::Ext.udp_set_membership(handle, multicast_address, interface_address, :uv_leave_group)
|
81
|
+
reject(error) if error
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
# Starts reading from the handle
|
86
|
+
# Renamed to match Stream
|
87
|
+
def start_read
|
88
|
+
return if @closed
|
89
|
+
error = check_result ::MTLibuv::Ext.udp_recv_start(handle, callback(:on_allocate), callback(:on_recv))
|
90
|
+
reject(error) if error
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
# Stops reading from the handle
|
95
|
+
# Renamed to match Stream
|
96
|
+
def stop_read
|
97
|
+
return if @closed
|
98
|
+
error = check_result ::MTLibuv::Ext.udp_recv_stop(handle)
|
99
|
+
reject(error) if error
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
def try_send(ip, port, data)
|
104
|
+
assert_type(String, ip, IP_ARGUMENT_ERROR)
|
105
|
+
assert_type(Integer, port, PORT_ARGUMENT_ERROR)
|
106
|
+
assert_type(String, data, SEND_DATA_ERROR)
|
107
|
+
|
108
|
+
sockaddr = create_sockaddr(ip, port)
|
109
|
+
|
110
|
+
buffer1 = ::FFI::MemoryPointer.from_string(data)
|
111
|
+
buffer = ::MTLibuv::Ext.buf_init(buffer1, data.respond_to?(:bytesize) ? data.bytesize : data.size)
|
112
|
+
|
113
|
+
result = ::MTLibuv::Ext.udp_try_send(
|
114
|
+
handle,
|
115
|
+
buffer,
|
116
|
+
1,
|
117
|
+
sockaddr
|
118
|
+
)
|
119
|
+
buffer1.free
|
120
|
+
|
121
|
+
error = check_result result
|
122
|
+
raise error if error
|
123
|
+
return result
|
124
|
+
end
|
125
|
+
|
126
|
+
def send(ip, port, data, wait: false)
|
127
|
+
# NOTE:: Similar to stream.rb -> write
|
128
|
+
deferred = @reactor.defer
|
129
|
+
if !@closed
|
130
|
+
begin
|
131
|
+
assert_type(String, ip, IP_ARGUMENT_ERROR)
|
132
|
+
assert_type(Integer, port, PORT_ARGUMENT_ERROR)
|
133
|
+
assert_type(String, data, SEND_DATA_ERROR)
|
134
|
+
|
135
|
+
sockaddr = create_sockaddr(ip, port)
|
136
|
+
|
137
|
+
# Save a reference to this request
|
138
|
+
req = send_req
|
139
|
+
buffer1 = ::FFI::MemoryPointer.from_string(data)
|
140
|
+
buffer = ::MTLibuv::Ext.buf_init(buffer1, data.respond_to?(:bytesize) ? data.bytesize : data.size)
|
141
|
+
@request_refs[req.address] = [deferred, buffer1]
|
142
|
+
|
143
|
+
# Save the callback and return the promise
|
144
|
+
error = check_result ::MTLibuv::Ext.udp_send(
|
145
|
+
req,
|
146
|
+
handle,
|
147
|
+
buffer,
|
148
|
+
1,
|
149
|
+
sockaddr,
|
150
|
+
callback(:send_complete, req.address)
|
151
|
+
)
|
152
|
+
if error
|
153
|
+
@request_refs.delete req.address
|
154
|
+
cleanup_callbacks req.address
|
155
|
+
::MTLibuv::Ext.free(req)
|
156
|
+
buffer1.free
|
157
|
+
deferred.reject(error)
|
158
|
+
reject(error) # close the handle
|
159
|
+
end
|
160
|
+
rescue StandardError => e
|
161
|
+
deferred.reject(e)
|
162
|
+
end
|
163
|
+
else
|
164
|
+
deferred.reject(RuntimeError.new(HANDLE_CLOSED_ERROR))
|
165
|
+
end
|
166
|
+
deferred.promise
|
167
|
+
|
168
|
+
if wait
|
169
|
+
return deferred.promise if wait == :promise
|
170
|
+
deferred.promise.value
|
171
|
+
end
|
172
|
+
|
173
|
+
self
|
174
|
+
end
|
175
|
+
|
176
|
+
def enable_multicast_reactor
|
177
|
+
return if @closed
|
178
|
+
error = check_result ::MTLibuv::Ext.udp_set_multicast_reactor(handle, 1)
|
179
|
+
reject(error) if error
|
180
|
+
self
|
181
|
+
end
|
182
|
+
|
183
|
+
def disable_multicast_reactor
|
184
|
+
return if @closed
|
185
|
+
error = check_result ::MTLibuv::Ext.udp_set_multicast_reactor(handle, 0)
|
186
|
+
reject(error) if error
|
187
|
+
self
|
188
|
+
end
|
189
|
+
|
190
|
+
def multicast_ttl=(ttl)
|
191
|
+
return if @closed
|
192
|
+
assert_type(Integer, ttl, TTL_ARGUMENT_ERROR)
|
193
|
+
error = check_result ::MTLibuv::Ext.udp_set_multicast_ttl(handle, ttl)
|
194
|
+
reject(error) if error
|
195
|
+
self
|
196
|
+
end
|
197
|
+
|
198
|
+
def enable_broadcast
|
199
|
+
return if @closed
|
200
|
+
error = check_result ::MTLibuv::Ext.udp_set_broadcast(handle, 1)
|
201
|
+
reject(error) if error
|
202
|
+
self
|
203
|
+
end
|
204
|
+
|
205
|
+
def disable_broadcast
|
206
|
+
return if @closed
|
207
|
+
error = check_result ::MTLibuv::Ext.udp_set_broadcast(handle, 0)
|
208
|
+
reject(error) if error
|
209
|
+
self
|
210
|
+
end
|
211
|
+
|
212
|
+
def ttl=(ttl)
|
213
|
+
return if @closed
|
214
|
+
assert_type(Integer, ttl, TTL_ARGUMENT_ERROR)
|
215
|
+
error = check_result ::MTLibuv::Ext.udp_set_ttl(handle, Integer(ttl))
|
216
|
+
reject(error) if error
|
217
|
+
self
|
218
|
+
end
|
219
|
+
|
220
|
+
def progress(&callback)
|
221
|
+
@progress = callback
|
222
|
+
self
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
private
|
227
|
+
|
228
|
+
|
229
|
+
def send_req
|
230
|
+
::MTLibuv::Ext.allocate_request_udp_send
|
231
|
+
end
|
232
|
+
|
233
|
+
def create_sockaddr(ip, port)
|
234
|
+
ips = IPAddr.new(ip)
|
235
|
+
if ips.ipv4?
|
236
|
+
addr = Ext::SockaddrIn.new
|
237
|
+
check_result! ::MTLibuv::Ext.ip4_addr(ip, port, addr)
|
238
|
+
addr
|
239
|
+
else
|
240
|
+
addr = Ext::SockaddrIn6.new
|
241
|
+
check_result! ::MTLibuv::Ext.ip6_addr(ip, port, addr)
|
242
|
+
addr
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
|
247
|
+
def on_close(pointer)
|
248
|
+
if @receive_buff
|
249
|
+
::MTLibuv::Ext.free(@receive_buff)
|
250
|
+
@receive_buff = nil
|
251
|
+
@receive_size = nil
|
252
|
+
end
|
253
|
+
|
254
|
+
super(pointer)
|
255
|
+
end
|
256
|
+
|
257
|
+
def on_allocate(client, suggested_size, buffer)
|
258
|
+
if @receive_buff.nil?
|
259
|
+
@receive_buff = ::MTLibuv::Ext.malloc(suggested_size)
|
260
|
+
@receive_size = suggested_size
|
261
|
+
end
|
262
|
+
|
263
|
+
buffer[:base] = @receive_buff
|
264
|
+
buffer[:len] = @receive_size
|
265
|
+
end
|
266
|
+
|
267
|
+
def on_recv(handle, nread, buf, sockaddr, flags)
|
268
|
+
e = check_result(nread)
|
269
|
+
|
270
|
+
if e
|
271
|
+
@reactor.exec { reject(e) } # Will call close
|
272
|
+
elsif nread > 0
|
273
|
+
data = @receive_buff.read_string(nread)
|
274
|
+
unless sockaddr.null?
|
275
|
+
ip, port = get_ip_and_port(sockaddr)
|
276
|
+
end
|
277
|
+
|
278
|
+
@reactor.exec do
|
279
|
+
begin
|
280
|
+
@progress.call data, ip, port, self
|
281
|
+
rescue Exception => e
|
282
|
+
@reactor.log e, 'performing UDP data received callback'
|
283
|
+
end
|
284
|
+
end
|
285
|
+
else
|
286
|
+
::MTLibuv::Ext.free(@receive_buff)
|
287
|
+
@receive_buff = nil
|
288
|
+
@receive_size = nil
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def send_complete(req, status)
|
293
|
+
deferred, buffer1 = @request_refs.delete req.address
|
294
|
+
cleanup_callbacks req.address
|
295
|
+
|
296
|
+
::MTLibuv::Ext.free(req)
|
297
|
+
buffer1.free
|
298
|
+
|
299
|
+
@reactor.exec { resolve(deferred, status) }
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MTLibuv
|
4
|
+
class Work < Q::DeferredPromise
|
5
|
+
include Resource, Listener
|
6
|
+
|
7
|
+
|
8
|
+
attr_reader :error
|
9
|
+
attr_reader :result
|
10
|
+
|
11
|
+
|
12
|
+
define_callback function: :on_work
|
13
|
+
define_callback function: :on_complete, params: [:pointer, :int]
|
14
|
+
|
15
|
+
|
16
|
+
# @param thread [::MTLibuv::Reactor] thread this work request will be associated
|
17
|
+
# @param work [Proc] callback to be called in the thread pool
|
18
|
+
def initialize(thread, &work)
|
19
|
+
super(thread, thread.defer)
|
20
|
+
|
21
|
+
@work = work
|
22
|
+
@complete = false
|
23
|
+
@pointer = ::MTLibuv::Ext.allocate_request_work
|
24
|
+
@error = nil # error in callback
|
25
|
+
|
26
|
+
@instance_id = @pointer.address
|
27
|
+
|
28
|
+
error = check_result ::MTLibuv::Ext.queue_work(@reactor, @pointer, callback(:on_work), callback(:on_complete))
|
29
|
+
if error
|
30
|
+
::MTLibuv::Ext.free(@pointer)
|
31
|
+
@complete = true
|
32
|
+
@defer.reject(error)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Attempt to cancel the pending work. Returns true if the work has completed or was canceled.
|
37
|
+
#
|
38
|
+
# @return [true, false]
|
39
|
+
def cancel
|
40
|
+
if not @complete
|
41
|
+
@complete = ::MTLibuv::Ext.cancel(@pointer) >= 0
|
42
|
+
end
|
43
|
+
@complete
|
44
|
+
end
|
45
|
+
|
46
|
+
# Indicates is the work has completed yet or not.
|
47
|
+
#
|
48
|
+
# @return [true, false]
|
49
|
+
def completed?
|
50
|
+
return @complete
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
|
57
|
+
def on_complete(req, status)
|
58
|
+
@complete = true
|
59
|
+
::MTLibuv::Ext.free(req)
|
60
|
+
|
61
|
+
@reactor.exec do
|
62
|
+
e = check_result(status)
|
63
|
+
if e
|
64
|
+
@defer.reject(e)
|
65
|
+
else
|
66
|
+
if @error
|
67
|
+
@defer.reject(@error)
|
68
|
+
else
|
69
|
+
@defer.resolve(@result)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Clean up references
|
75
|
+
cleanup_callbacks @instance_id
|
76
|
+
end
|
77
|
+
|
78
|
+
def on_work(req)
|
79
|
+
begin
|
80
|
+
@result = @work.call
|
81
|
+
rescue Exception => e
|
82
|
+
@error = e # Catch errors for promise resolution
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/mt-libuv.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'thread'
|
5
|
+
require 'ffi'
|
6
|
+
|
7
|
+
module MTLibuv
|
8
|
+
DefaultThread = Thread.current
|
9
|
+
|
10
|
+
require 'mt-libuv/ext/ext' # The libuv ffi ext
|
11
|
+
require 'mt-libuv/error' # List of errors (matching those in uv.h)
|
12
|
+
require 'mt-libuv/q' # The promise library
|
13
|
+
|
14
|
+
# -- The classes required for a reactor instance --
|
15
|
+
require 'mt-libuv/mixins/assertions' # Common code to check arguments
|
16
|
+
require 'mt-libuv/mixins/accessors' # Helper methods for accessing reactor functions
|
17
|
+
require 'mt-libuv/mixins/resource' # Common code to check for errors
|
18
|
+
require 'mt-libuv/mixins/listener' # Common callback code
|
19
|
+
|
20
|
+
require 'mt-libuv/handle' # Base class for most libuv functionality
|
21
|
+
require 'mt-libuv/prepare' # Called at the end of a reactor cycle
|
22
|
+
require 'mt-libuv/async' # Provide a threadsafe way to signal the event reactor
|
23
|
+
require 'mt-libuv/timer' # High resolution timer
|
24
|
+
require 'mt-libuv/reactor' # The libuv reactor or event reactor
|
25
|
+
require 'mt-libuv/coroutines' # Pause program execution until a result is returned
|
26
|
+
require 'mt-libuv/fiber_pool' # Fibers on jRuby and Rubinius are threads and expensive to re-create
|
27
|
+
# --
|
28
|
+
|
29
|
+
autoload :FsChecks, 'mt-libuv/mixins/fs_checks' # Common code to check file system results
|
30
|
+
autoload :Stream, 'mt-libuv/mixins/stream' # For all libuv streams (tcp, pipes, tty)
|
31
|
+
autoload :Net, 'mt-libuv/mixins/net' # Common functions for tcp and udp
|
32
|
+
|
33
|
+
autoload :Filesystem, 'mt-libuv/filesystem' # Async directory manipulation
|
34
|
+
autoload :FSEvent, 'mt-libuv/fs_event' # Notifies of changes to files and folders as they occur
|
35
|
+
autoload :Signal, 'mt-libuv/signal' # Used to handle OS signals
|
36
|
+
autoload :Spawn, 'mt-libuv/spawn' # Executes a child process
|
37
|
+
autoload :Check, 'mt-libuv/check' # Called before processing events on the reactor
|
38
|
+
autoload :File, 'mt-libuv/file' # Async file reading and writing
|
39
|
+
autoload :Idle, 'mt-libuv/idle' # Called when there are no events to process
|
40
|
+
autoload :Work, 'mt-libuv/work' # Provide work to be completed on another thread (thread pool)
|
41
|
+
autoload :UDP, 'mt-libuv/udp' # Communicate over UDP
|
42
|
+
autoload :Dns, 'mt-libuv/dns' # Async DNS lookup
|
43
|
+
|
44
|
+
# Streams
|
45
|
+
autoload :Pipe, 'mt-libuv/pipe' # Communicate over Pipes
|
46
|
+
autoload :TCP, 'mt-libuv/tcp' # Communicate over TCP
|
47
|
+
autoload :TTY, 'mt-libuv/tty' # Terminal output
|
48
|
+
|
49
|
+
|
50
|
+
# Returns the number of CPU cores on the host platform
|
51
|
+
#
|
52
|
+
# @return [Integer, nil] representing the number of CPU cores or nil if failed
|
53
|
+
def self.cpu_count
|
54
|
+
cpu_info = FFI::MemoryPointer.new(:pointer)
|
55
|
+
cpu_count = FFI::MemoryPointer.new(:int)
|
56
|
+
if ::MTLibuv::Ext.cpu_info(cpu_info, cpu_count) >= 0
|
57
|
+
count = cpu_count.read_int
|
58
|
+
::MTLibuv::Ext.free_cpu_info(cpu_info.read_pointer, count)
|
59
|
+
return count
|
60
|
+
else
|
61
|
+
return nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Include all the accessors at this level
|
66
|
+
extend Accessors
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
class Object
|
71
|
+
private
|
72
|
+
|
73
|
+
def reactor
|
74
|
+
if block_given?
|
75
|
+
MTLibuv.reactor { |thread| yield(thread) }
|
76
|
+
else
|
77
|
+
MTLibuv.reactor
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/mt-libuv.gemspec
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path("../lib/mt-libuv/version", __FILE__)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "mt-libuv"
|
7
|
+
gem.version = MTLibuv::VERSION
|
8
|
+
gem.license = 'MIT'
|
9
|
+
gem.authors = ["Giallombardo Nathan"]
|
10
|
+
gem.email = ["nathan.giallombardo@mapotempo.com"]
|
11
|
+
gem.homepage = "https://github.com/Mapotempo/mt-libuv"
|
12
|
+
gem.summary = "mt-libuv bindings for Ruby"
|
13
|
+
gem.description = "An opinionated wrapper around mt-libuv for Ruby"
|
14
|
+
|
15
|
+
gem.extensions << "ext/Rakefile"
|
16
|
+
|
17
|
+
gem.required_ruby_version = '>= 2.0.0'
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_runtime_dependency 'ffi', '~> 1.9'
|
21
|
+
gem.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
|
22
|
+
gem.add_runtime_dependency 'mt-ruby-tls', '~> 2.1'
|
23
|
+
|
24
|
+
gem.add_development_dependency 'rspec', '~> 3.5'
|
25
|
+
gem.add_development_dependency 'rake', '~> 11.2'
|
26
|
+
gem.add_development_dependency 'yard', '~> 0.9'
|
27
|
+
|
28
|
+
gem.files = `git ls-files`.split("\n")
|
29
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
30
|
+
|
31
|
+
if File.exist? 'ext/libuv.dll'
|
32
|
+
gem.files << 'ext/libuv.dll'
|
33
|
+
end
|
34
|
+
|
35
|
+
# Add the submodule to the gem
|
36
|
+
# relative_path = File.expand_path("../", __FILE__) + '/'
|
37
|
+
# `git submodule --quiet foreach pwd`.split($\).each do |submodule_path|
|
38
|
+
|
39
|
+
# if (ENV['OS'] == 'Windows_NT') && submodule_path[0] == '/'
|
40
|
+
# # Detect if cygwin path is being used by git
|
41
|
+
# submodule_path = submodule_path[1..-1]
|
42
|
+
# submodule_path.insert(1, ':')
|
43
|
+
# end
|
44
|
+
|
45
|
+
# # for each submodule, change working directory to that submodule
|
46
|
+
# Dir.chdir(submodule_path) do
|
47
|
+
# # Make the submodule path relative
|
48
|
+
# submodule_path = submodule_path.gsub(/#{relative_path}/i, '')
|
49
|
+
|
50
|
+
# # issue git ls-files in submodule's directory
|
51
|
+
# submodule_files = `git ls-files`.split($\)
|
52
|
+
|
53
|
+
# # prepend the submodule path to create relative file paths
|
54
|
+
# submodule_files_paths = submodule_files.map do |filename|
|
55
|
+
# File.join(submodule_path, filename)
|
56
|
+
# end
|
57
|
+
|
58
|
+
# # add relative paths to gem.files
|
59
|
+
# gem.files += submodule_files_paths
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
end
|
data/spec/async_spec.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'mt-libuv'
|
2
|
+
|
3
|
+
|
4
|
+
describe MTLibuv::Async do
|
5
|
+
before :each do
|
6
|
+
@log = []
|
7
|
+
@general_failure = []
|
8
|
+
|
9
|
+
@reactor = MTLibuv::Reactor.default
|
10
|
+
@call = @reactor.pipe
|
11
|
+
@timeout = @reactor.timer do
|
12
|
+
@reactor.stop
|
13
|
+
@general_failure << "test timed out"
|
14
|
+
end
|
15
|
+
@timeout.start(5000)
|
16
|
+
|
17
|
+
@reactor.all(@server, @client, @timeout).catch do |reason|
|
18
|
+
@general_failure << reason.inspect
|
19
|
+
puts "Failed with: #{reason.message}\n#{reason.backtrace.join("\n")}\n"
|
20
|
+
end
|
21
|
+
|
22
|
+
@reactor.notifier do |error, context|
|
23
|
+
begin
|
24
|
+
puts "Log called: #{context}\n#{error.message}\n#{error.backtrace.join("\n")}\n"
|
25
|
+
rescue Exception
|
26
|
+
puts 'error in logger'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
after :each do
|
32
|
+
@reactor.notifier
|
33
|
+
end
|
34
|
+
|
35
|
+
it "Should call the async function from the thread pool stopping the counter" do
|
36
|
+
@reactor.run { |reactor|
|
37
|
+
|
38
|
+
@count = 0
|
39
|
+
|
40
|
+
timer = @reactor.timer do
|
41
|
+
@count += 1
|
42
|
+
end
|
43
|
+
timer.start(0, 200)
|
44
|
+
|
45
|
+
callback = @reactor.async do
|
46
|
+
stopper = @reactor.timer do
|
47
|
+
timer.close
|
48
|
+
callback.close
|
49
|
+
stopper.close
|
50
|
+
@timeout.close
|
51
|
+
@reactor.stop
|
52
|
+
end
|
53
|
+
stopper.start(1000)
|
54
|
+
callback.close
|
55
|
+
end
|
56
|
+
|
57
|
+
@reactor.work {
|
58
|
+
callback.call
|
59
|
+
}.catch do |err|
|
60
|
+
@general_failure << err
|
61
|
+
end
|
62
|
+
}
|
63
|
+
|
64
|
+
expect(@general_failure).to eq([])
|
65
|
+
expect(@count < 7 && @count > 3).to eq(true)
|
66
|
+
end
|
67
|
+
end
|