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
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MTLibuv
|
4
|
+
class Handle < Q::DeferredPromise
|
5
|
+
include Assertions, Resource, Listener
|
6
|
+
|
7
|
+
|
8
|
+
attr_accessor :storage # A place for general storage
|
9
|
+
attr_reader :closed
|
10
|
+
attr_reader :reactor
|
11
|
+
|
12
|
+
|
13
|
+
define_callback function: :on_close
|
14
|
+
|
15
|
+
|
16
|
+
def initialize(pointer, error)
|
17
|
+
@pointer = pointer
|
18
|
+
@instance_id = @pointer.address
|
19
|
+
|
20
|
+
# Initialise the promise
|
21
|
+
super(reactor, reactor.defer)
|
22
|
+
|
23
|
+
# clean up on init error (always raise here)
|
24
|
+
if error
|
25
|
+
::MTLibuv::Ext.free(pointer)
|
26
|
+
defer.reject(error)
|
27
|
+
@closed = true
|
28
|
+
raise error
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: Increment internal ref counter for the handle on the reactor. Useful for
|
33
|
+
# extending the reactor with custom watchers that need to make reactor not stop
|
34
|
+
#
|
35
|
+
# Returns self
|
36
|
+
def ref
|
37
|
+
return self if @closed
|
38
|
+
::MTLibuv::Ext.ref(handle)
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
# Public: Decrement internal ref counter for the handle on the reactor, useful to stop
|
43
|
+
# reactor even when there are outstanding open handles
|
44
|
+
#
|
45
|
+
# Returns self
|
46
|
+
def unref
|
47
|
+
return self if @closed
|
48
|
+
::MTLibuv::Ext.unref(handle)
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def close
|
53
|
+
return self if @closed
|
54
|
+
@closed = true
|
55
|
+
::MTLibuv::Ext.close(handle, callback(:on_close))
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def closed?
|
60
|
+
!!@closed
|
61
|
+
end
|
62
|
+
|
63
|
+
def active?
|
64
|
+
::MTLibuv::Ext.is_active(handle) > 0
|
65
|
+
end
|
66
|
+
|
67
|
+
def closing?
|
68
|
+
::MTLibuv::Ext.is_closing(handle) > 0
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
protected
|
73
|
+
|
74
|
+
|
75
|
+
def handle; @pointer; end
|
76
|
+
def defer; @defer; end
|
77
|
+
def instance_id; @instance_id; end
|
78
|
+
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
|
83
|
+
# Clean up and throw an error
|
84
|
+
def reject(reason)
|
85
|
+
@close_error = reason
|
86
|
+
close
|
87
|
+
end
|
88
|
+
|
89
|
+
def on_close(pointer)
|
90
|
+
::MTLibuv::Ext.free(pointer)
|
91
|
+
#clear_callbacks
|
92
|
+
cleanup_callbacks
|
93
|
+
|
94
|
+
@reactor.exec do
|
95
|
+
if @close_error
|
96
|
+
defer.reject(@close_error)
|
97
|
+
else
|
98
|
+
defer.resolve(nil)
|
99
|
+
end
|
100
|
+
|
101
|
+
if @coroutine
|
102
|
+
@coroutine.resolve(self)
|
103
|
+
@coroutine = nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MTLibuv
|
4
|
+
class Idle < Handle
|
5
|
+
|
6
|
+
|
7
|
+
define_callback function: :on_idle
|
8
|
+
|
9
|
+
|
10
|
+
# @param reactor [::MTLibuv::Reactor] reactor this idle handler will be associated
|
11
|
+
# @param callback [Proc] callback to be called when the reactor is idle
|
12
|
+
def initialize(reactor)
|
13
|
+
@reactor = reactor
|
14
|
+
|
15
|
+
idle_ptr = ::MTLibuv::Ext.allocate_handle_idle
|
16
|
+
error = check_result(::MTLibuv::Ext.idle_init(reactor.handle, idle_ptr))
|
17
|
+
|
18
|
+
super(idle_ptr, error)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Enables the idle handler.
|
22
|
+
def start
|
23
|
+
return if @closed
|
24
|
+
error = check_result ::MTLibuv::Ext.idle_start(handle, callback(:on_idle))
|
25
|
+
reject(error) if error
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
# Disables the idle handler.
|
30
|
+
def stop
|
31
|
+
return if @closed
|
32
|
+
error = check_result ::MTLibuv::Ext.idle_stop(handle)
|
33
|
+
reject(error) if error
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
# Used to update the callback that will be triggered on idle
|
38
|
+
#
|
39
|
+
# @param callback [Proc] the callback to be called on idle trigger
|
40
|
+
def progress(&callback)
|
41
|
+
@callback = callback
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
|
49
|
+
def on_idle(handle)
|
50
|
+
@reactor.exec do
|
51
|
+
begin
|
52
|
+
@callback.call
|
53
|
+
rescue Exception => e
|
54
|
+
@reactor.log e, 'performing idle callback'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MTLibuv
|
4
|
+
module Accessors
|
5
|
+
def reactor
|
6
|
+
thread = MTLibuv::Reactor.current
|
7
|
+
if thread.nil?
|
8
|
+
thread = MTLibuv::Reactor.default
|
9
|
+
if thread.reactor_running?
|
10
|
+
raise 'No reactor available on this thread'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
thread.run { yield(thread) } if block_given?
|
14
|
+
thread
|
15
|
+
end
|
16
|
+
|
17
|
+
Functions = [
|
18
|
+
:defer, :all, :any, :finally, :update_time, :now, :lookup_error, :tcp,
|
19
|
+
:udp, :tty, :pipe, :timer, :prepare, :check, :idle, :async, :signal,
|
20
|
+
:work, :lookup, :fs_event, :file, :filesystem, :schedule, :next_tick,
|
21
|
+
:stop, :reactor_thread?, :reactor_running?, :run
|
22
|
+
].freeze
|
23
|
+
|
24
|
+
Functions.each do |function|
|
25
|
+
define_method function do |*args|
|
26
|
+
thread = MTLibuv::Reactor.current
|
27
|
+
|
28
|
+
if thread
|
29
|
+
thread.send(function, *args)
|
30
|
+
else
|
31
|
+
thread = MTLibuv::Reactor.default
|
32
|
+
if thread.reactor_running?
|
33
|
+
raise 'attempted MTLibuv::Reactor access on non-reactor thread'
|
34
|
+
else
|
35
|
+
thread.send(function, *args)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MTLibuv
|
4
|
+
module Assertions
|
5
|
+
MSG_NO_PROC = 'no block given'
|
6
|
+
|
7
|
+
def assert_block(proc, msg = MSG_NO_PROC)
|
8
|
+
raise ArgumentError, msg, caller unless proc.respond_to? :call
|
9
|
+
end
|
10
|
+
|
11
|
+
def assert_type(type, actual, msg = nil)
|
12
|
+
if not actual.kind_of?(type)
|
13
|
+
msg ||= "value #{actual.inspect} is not a valid #{type}"
|
14
|
+
raise ArgumentError, msg, caller
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def assert_boolean(actual, msg = nil)
|
19
|
+
if not (actual.kind_of?(TrueClass) || actual.kind_of?(FalseClass))
|
20
|
+
msg ||= "value #{actual.inspect} is not a valid Boolean"
|
21
|
+
raise ArgumentError, msg, caller
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MTLibuv
|
4
|
+
module FsChecks
|
5
|
+
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def fs_lookup(ref)
|
9
|
+
ref.to_ptr.address
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(base)
|
14
|
+
base.extend(ClassMethods)
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def stat(wait: true)
|
19
|
+
@stat_deferred = @reactor.defer
|
20
|
+
|
21
|
+
request = ::MTLibuv::Ext.allocate_request_fs
|
22
|
+
pre_check @stat_deferred, request, ::MTLibuv::Ext.fs_fstat(@reactor.handle, request, @fileno, callback(:on_stat, request.address))
|
23
|
+
promise = @stat_deferred.promise
|
24
|
+
|
25
|
+
wait ? promise.value : promise
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
|
32
|
+
def respond(wait, promise)
|
33
|
+
if wait
|
34
|
+
promise.value
|
35
|
+
self
|
36
|
+
else
|
37
|
+
promise
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
|
45
|
+
def on_stat(req)
|
46
|
+
if post_check(req, @stat_deferred)
|
47
|
+
uv_stat = req[:stat]
|
48
|
+
uv_members = uv_stat.members
|
49
|
+
|
50
|
+
stats = {}
|
51
|
+
uv_members.each do |key|
|
52
|
+
stats[key] = uv_stat[key]
|
53
|
+
end
|
54
|
+
|
55
|
+
cleanup(req)
|
56
|
+
@reactor.exec { @stat_deferred.resolve(stats) }
|
57
|
+
end
|
58
|
+
@stat_deferred = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def pre_check(deferrable, request, result)
|
62
|
+
error = check_result result
|
63
|
+
if error
|
64
|
+
@request_refs.delete request.address
|
65
|
+
::MTLibuv::Ext.free(request)
|
66
|
+
deferrable.reject(error)
|
67
|
+
end
|
68
|
+
deferrable.promise
|
69
|
+
end
|
70
|
+
|
71
|
+
def cleanup(req)
|
72
|
+
cleanup_callbacks req.to_ptr.address
|
73
|
+
|
74
|
+
::MTLibuv::Ext.fs_req_cleanup(req)
|
75
|
+
::MTLibuv::Ext.free(req)
|
76
|
+
end
|
77
|
+
|
78
|
+
def post_check(req, deferrable)
|
79
|
+
error = check_result(req[:result])
|
80
|
+
if error
|
81
|
+
cleanup(req)
|
82
|
+
|
83
|
+
@reactor.exec do
|
84
|
+
deferrable.reject(error)
|
85
|
+
if @coroutine
|
86
|
+
@coroutine.resolve(deferrable.promise)
|
87
|
+
@coroutine = nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
false
|
91
|
+
else
|
92
|
+
true
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'concurrent'
|
4
|
+
|
5
|
+
module MTLibuv
|
6
|
+
module Listener
|
7
|
+
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def dispatch_callback(func_name, lookup, args)
|
14
|
+
instance_id = __send__(lookup, *args)
|
15
|
+
inst = @callback_lookup[instance_id]
|
16
|
+
inst.__send__(func_name, *args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def define_callback(function:, params: [:pointer], ret_val: :void, lookup: :default_lookup)
|
20
|
+
@callback_funcs[function] = ::FFI::Function.new(ret_val, params) do |*args|
|
21
|
+
dispatch_callback(function, lookup, args)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Much like include to support inheritance properly
|
26
|
+
# We keep existing callbacks and inherit the lookup (as this will never clash)
|
27
|
+
def inherited(subclass)
|
28
|
+
subclass.instance_variable_set(:@callback_funcs, {}.merge(@callback_funcs))
|
29
|
+
subclass.instance_variable_set(:@callback_lookup, @callback_lookup)
|
30
|
+
subclass.instance_variable_set(:@callback_lock, @callback_lock)
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
# Provide accessor methods to the class level instance variables
|
35
|
+
attr_reader :callback_lookup, :callback_funcs, :callback_lock
|
36
|
+
|
37
|
+
|
38
|
+
# This function is used to work out the instance the callback is for
|
39
|
+
def default_lookup(req, *args)
|
40
|
+
req.address
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.included(base)
|
45
|
+
base.instance_variable_set(:@callback_funcs, {})
|
46
|
+
base.instance_variable_set(:@callback_lookup, ::Concurrent::Hash.new)
|
47
|
+
base.instance_variable_set(:@callback_lock, ::Mutex.new)
|
48
|
+
base.extend(ClassMethods)
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
def callback(name, instance_id = @instance_id)
|
54
|
+
klass = self.class
|
55
|
+
klass.callback_lock.synchronize do
|
56
|
+
klass.callback_lookup[instance_id] = self
|
57
|
+
end
|
58
|
+
klass.callback_funcs[name]
|
59
|
+
end
|
60
|
+
|
61
|
+
def cleanup_callbacks(instance_id = @instance_id)
|
62
|
+
klass = self.class
|
63
|
+
klass.callback_lock.synchronize do
|
64
|
+
inst = klass.callback_lookup[instance_id]
|
65
|
+
klass.callback_lookup.delete(instance_id) if inst == self
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
module MTLibuv
|
6
|
+
module Net
|
7
|
+
|
8
|
+
|
9
|
+
IP_ARGUMENT_ERROR = "ip must be a String" # Arguments specifying an IP address
|
10
|
+
PORT_ARGUMENT_ERROR = "port must be an Integer" # Arguments specifying an IP port
|
11
|
+
INET_ADDRSTRLEN = 16
|
12
|
+
INET6_ADDRSTRLEN = 46
|
13
|
+
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
|
18
|
+
def get_sockaddr_and_len
|
19
|
+
sockaddr = FFI::MemoryPointer.new(::MTLibuv::Ext::Sockaddr)
|
20
|
+
len = FFI::MemoryPointer.new(:int)
|
21
|
+
len.put_int(0, ::MTLibuv::Ext::Sockaddr.size)
|
22
|
+
[sockaddr, len]
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_ip_and_port(sockaddr, len = nil)
|
26
|
+
if sockaddr[:sa_family] == Socket::Constants::AF_INET6
|
27
|
+
len ||= INET6_ADDRSTRLEN
|
28
|
+
sockaddr_in6 = ::MTLibuv::Ext::SockaddrIn6.new(sockaddr.pointer)
|
29
|
+
ip_ptr = FFI::MemoryPointer.new(:char, len)
|
30
|
+
::MTLibuv::Ext.ip6_name(sockaddr_in6, ip_ptr, len)
|
31
|
+
port = ::MTLibuv::Ext.ntohs(sockaddr_in6[:sin6_port])
|
32
|
+
else
|
33
|
+
len ||= INET_ADDRSTRLEN
|
34
|
+
sockaddr_in = ::MTLibuv::Ext::SockaddrIn.new(sockaddr.pointer)
|
35
|
+
ip_ptr = FFI::MemoryPointer.new(:char, len)
|
36
|
+
::MTLibuv::Ext.ip4_name(sockaddr_in, ip_ptr, len)
|
37
|
+
port = ::MTLibuv::Ext.ntohs(sockaddr_in[:sin_port])
|
38
|
+
end
|
39
|
+
[ip_ptr.read_string, port]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MTLibuv
|
4
|
+
module Resource
|
5
|
+
|
6
|
+
|
7
|
+
def resolve(deferred, rc)
|
8
|
+
if rc && rc < 0
|
9
|
+
deferred.reject(@reactor.lookup_error(rc))
|
10
|
+
else
|
11
|
+
deferred.resolve(nil)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def check_result!(rc)
|
16
|
+
e = @reactor.lookup_error(rc) unless rc.nil? || rc >= 0
|
17
|
+
raise e if e
|
18
|
+
end
|
19
|
+
|
20
|
+
def check_result(rc)
|
21
|
+
@reactor.lookup_error(rc) unless rc.nil? || rc >= 0
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_ptr
|
25
|
+
@pointer
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|