mt-libuv 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.gitmodules +6 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +24 -0
  6. data/Gemfile +9 -0
  7. data/LICENSE +24 -0
  8. data/README.md +195 -0
  9. data/Rakefile +31 -0
  10. data/ext/README.md +6 -0
  11. data/ext/Rakefile +28 -0
  12. data/lib/mt-libuv/async.rb +51 -0
  13. data/lib/mt-libuv/check.rb +59 -0
  14. data/lib/mt-libuv/coroutines.rb +79 -0
  15. data/lib/mt-libuv/dns.rb +98 -0
  16. data/lib/mt-libuv/error.rb +88 -0
  17. data/lib/mt-libuv/ext/ext.rb +322 -0
  18. data/lib/mt-libuv/ext/platform/darwin_x64.rb +61 -0
  19. data/lib/mt-libuv/ext/platform/unix.rb +69 -0
  20. data/lib/mt-libuv/ext/platform/windows.rb +83 -0
  21. data/lib/mt-libuv/ext/tasks/mac.rb +24 -0
  22. data/lib/mt-libuv/ext/tasks/unix.rb +42 -0
  23. data/lib/mt-libuv/ext/tasks/win.rb +29 -0
  24. data/lib/mt-libuv/ext/tasks.rb +27 -0
  25. data/lib/mt-libuv/ext/types.rb +253 -0
  26. data/lib/mt-libuv/fiber_pool.rb +83 -0
  27. data/lib/mt-libuv/file.rb +309 -0
  28. data/lib/mt-libuv/filesystem.rb +263 -0
  29. data/lib/mt-libuv/fs_event.rb +37 -0
  30. data/lib/mt-libuv/handle.rb +108 -0
  31. data/lib/mt-libuv/idle.rb +59 -0
  32. data/lib/mt-libuv/mixins/accessors.rb +41 -0
  33. data/lib/mt-libuv/mixins/assertions.rb +25 -0
  34. data/lib/mt-libuv/mixins/fs_checks.rb +96 -0
  35. data/lib/mt-libuv/mixins/listener.rb +69 -0
  36. data/lib/mt-libuv/mixins/net.rb +42 -0
  37. data/lib/mt-libuv/mixins/resource.rb +30 -0
  38. data/lib/mt-libuv/mixins/stream.rb +276 -0
  39. data/lib/mt-libuv/pipe.rb +217 -0
  40. data/lib/mt-libuv/prepare.rb +59 -0
  41. data/lib/mt-libuv/q.rb +475 -0
  42. data/lib/mt-libuv/reactor.rb +567 -0
  43. data/lib/mt-libuv/signal.rb +62 -0
  44. data/lib/mt-libuv/spawn.rb +113 -0
  45. data/lib/mt-libuv/tcp.rb +465 -0
  46. data/lib/mt-libuv/timer.rb +107 -0
  47. data/lib/mt-libuv/tty.rb +42 -0
  48. data/lib/mt-libuv/udp.rb +302 -0
  49. data/lib/mt-libuv/version.rb +5 -0
  50. data/lib/mt-libuv/work.rb +86 -0
  51. data/lib/mt-libuv.rb +80 -0
  52. data/mt-libuv.gemspec +62 -0
  53. data/spec/async_spec.rb +67 -0
  54. data/spec/coroutines_spec.rb +121 -0
  55. data/spec/cpu_spec.rb +10 -0
  56. data/spec/defer_spec.rb +906 -0
  57. data/spec/dns_spec.rb +110 -0
  58. data/spec/dsl_spec.rb +43 -0
  59. data/spec/filesystem_spec.rb +270 -0
  60. data/spec/idle_spec.rb +44 -0
  61. data/spec/pipe_spec.rb +151 -0
  62. data/spec/spawn_spec.rb +119 -0
  63. data/spec/tcp_spec.rb +272 -0
  64. data/spec/test.sh +4 -0
  65. data/spec/test_fail.sh +3 -0
  66. data/spec/test_read.sh +3 -0
  67. data/spec/timer_spec.rb +14 -0
  68. data/spec/udp_spec.rb +73 -0
  69. data/spec/zen_spec.rb +34 -0
  70. 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