mt-libuv 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|