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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 55ed231748185b35f9e88cb285439d40bc75172b4c0133badf659b6a70be6969
|
4
|
+
data.tar.gz: 51622861571ac5be5a93e06ee86e64f5c92ed3b4bbd37236717999694c60b5cc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3e1d126ffe76a1eb330bdb3203293088d0826270151c7e15cd6db1c09874304a2d745f5ba5909ab48268d9775a909da75f920e799a2c291229a280c1463099da
|
7
|
+
data.tar.gz: c30c8f7243fc969d1ae70b1e587a89f31ec49f552a085eccdf45ab031989f27731423373eb633a4e11f7def2d7ab4f2b3874ba2599d73f77c35a94a4f950494a
|
data/.gitignore
ADDED
data/.gitmodules
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--format progress
|
data/.travis.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- ruby-2.3.5
|
4
|
+
- ruby-2.4.2
|
5
|
+
- ruby-head
|
6
|
+
- rubinius
|
7
|
+
- jruby-9.1.13.0
|
8
|
+
- jruby-head
|
9
|
+
branches:
|
10
|
+
only:
|
11
|
+
- master
|
12
|
+
before_install:
|
13
|
+
- git submodule update --init --recursive
|
14
|
+
- gem install ffi
|
15
|
+
before_script:
|
16
|
+
- rake compile
|
17
|
+
sudo: false
|
18
|
+
matrix:
|
19
|
+
allow_failures:
|
20
|
+
- rvm: jruby-head
|
21
|
+
- rvm: ruby-head
|
22
|
+
- rvm: rubinius
|
23
|
+
sudo: required
|
24
|
+
dist: trusty
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Copyright (c) 2004-2013 Cotag Media
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is furnished
|
8
|
+
to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
20
|
+
|
21
|
+
===
|
22
|
+
|
23
|
+
This license applies to all parts of uvrb (Ruby FFI bindings for libuv only)
|
24
|
+
Libuv itself [is using Node license](https://github.com/joyent/libuv/blob/master/LICENSE)
|
data/README.md
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
# Libuv FFI bindings for Ruby
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/cotag/libuv.svg?branch=master)](https://travis-ci.org/cotag/libuv)
|
4
|
+
|
5
|
+
[Libuv](https://github.com/libuv/libuv) is a cross platform asynchronous IO implementation that powers NodeJS. It supports sockets, both UDP and TCP, filesystem watch, TTY, Pipes and other asynchronous primitives like timer, check, prepare and idle.
|
6
|
+
|
7
|
+
The Libuv gem contains Libuv and a Ruby wrapper that implements [pipelined promises](http://en.wikipedia.org/wiki/Futures_and_promises#Promise_pipelining) for asynchronous flow control and [coroutines](http://en.wikipedia.org/wiki/Coroutine) / [futures](https://en.wikipedia.org/wiki/Futures_and_promises) for untangling evented code
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
Libuv supports multiple reactors that can run on different threads.
|
12
|
+
|
13
|
+
For convenience the thread local or default reactor can be accessed via the `reactor` method
|
14
|
+
You can pass a block to be executed on the reactor and the reactor will run until there is nothing left to do.
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
require 'libuv'
|
18
|
+
|
19
|
+
reactor do |reactor|
|
20
|
+
reactor.timer {
|
21
|
+
puts "5 seconds passed"
|
22
|
+
}.start(5000)
|
23
|
+
end
|
24
|
+
|
25
|
+
puts "reactor stopped. No more IO to process"
|
26
|
+
```
|
27
|
+
|
28
|
+
Promises are used to simplify code flow.
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
require 'libuv'
|
32
|
+
|
33
|
+
reactor do |reactor|
|
34
|
+
reactor.tcp { |data, socket|
|
35
|
+
puts "received: #{data}"
|
36
|
+
socket.close
|
37
|
+
}
|
38
|
+
.connect('127.0.0.1', 3000) { |socket|
|
39
|
+
socket.start_read
|
40
|
+
.write("GET / HTTP/1.1\r\n\r\n")
|
41
|
+
}
|
42
|
+
.catch { |error|
|
43
|
+
puts "error: #{error}"
|
44
|
+
}
|
45
|
+
.finally {
|
46
|
+
puts "socket closed"
|
47
|
+
}
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
Continuations are used if callbacks are not defined
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
require 'libuv'
|
55
|
+
|
56
|
+
reactor do |reactor|
|
57
|
+
begin
|
58
|
+
reactor.tcp { |data, socket|
|
59
|
+
puts "received: #{data}"
|
60
|
+
socket.close
|
61
|
+
}
|
62
|
+
.connect('127.0.0.1', 3000)
|
63
|
+
.start_read
|
64
|
+
.write("GET / HTTP/1.1\r\n\r\n")
|
65
|
+
rescue => error
|
66
|
+
puts "error: #{error}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
Any promise can be converted into a continuation
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
require 'libuv'
|
75
|
+
|
76
|
+
reactor do |reactor|
|
77
|
+
# Perform work on the thread pool with promises
|
78
|
+
reactor.work {
|
79
|
+
10 * 2
|
80
|
+
}.then { |result|
|
81
|
+
puts "result using a promise #{result}"
|
82
|
+
}
|
83
|
+
|
84
|
+
# Use the coroutine helper to obtain the result without a callback
|
85
|
+
result = reactor.work {
|
86
|
+
10 * 3
|
87
|
+
}.value
|
88
|
+
puts "no additional callbacks here #{result}"
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
|
93
|
+
Check out the [yard documentation](http://rubydoc.info/gems/libuv/Libuv/Reactor)
|
94
|
+
|
95
|
+
|
96
|
+
## Installation
|
97
|
+
|
98
|
+
```shell
|
99
|
+
gem install libuv
|
100
|
+
```
|
101
|
+
|
102
|
+
or
|
103
|
+
|
104
|
+
```shell
|
105
|
+
git clone https://github.com/cotag/libuv.git
|
106
|
+
cd libuv
|
107
|
+
bundle install
|
108
|
+
rake compile
|
109
|
+
```
|
110
|
+
|
111
|
+
### Prerequisites
|
112
|
+
|
113
|
+
* The installation on BSD/Linux requires [python 2.x](http://www.python.org/getit/) to be installed and available on the PATH
|
114
|
+
* setting the environmental variable `USE_GLOBAL_LIBUV` will prevent compiling the packaged version.
|
115
|
+
* if you have a compatible `libuv.(so | dylib | dll)` on the PATH already
|
116
|
+
|
117
|
+
On Windows the GEM ships with a pre-compiled binary. If you would like to build yourself:
|
118
|
+
|
119
|
+
- A copy of Visual Studio 2017. [Visual Studio Build Tools](https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2017) works fine.
|
120
|
+
- Windows 10 SDK
|
121
|
+
- C++/CLI Support
|
122
|
+
- C++ tools for CMake
|
123
|
+
- A copy of [OpenSSL](http://slproweb.com/products/Win32OpenSSL.html) x64 - ~30MB installs
|
124
|
+
- ruby 2.4+ x64 with MSYS2 is preferred
|
125
|
+
- Add the env var `set GYP_MSVS_VERSION=2017`
|
126
|
+
- If using jRuby then [GCC](http://win-builds.org/stable/) is also required
|
127
|
+
- Setup the paths as described on the gcc page
|
128
|
+
- Add required environmental variable `set LIBRARY_PATH=X:\win-builds-64\lib;X:\win-builds-64\x86_64-w64-mingw32\lib`
|
129
|
+
- `rake compile`
|
130
|
+
|
131
|
+
|
132
|
+
|
133
|
+
## Features
|
134
|
+
|
135
|
+
* TCP (with TLS support)
|
136
|
+
* UDP
|
137
|
+
* TTY
|
138
|
+
* Pipes
|
139
|
+
* Timer
|
140
|
+
* Prepare
|
141
|
+
* Check
|
142
|
+
* Idle
|
143
|
+
* Signals
|
144
|
+
* Async callbacks
|
145
|
+
* Async DNS Resolution
|
146
|
+
* Filesystem Events
|
147
|
+
* Filesystem manipulation
|
148
|
+
* File manipulation
|
149
|
+
* Errors (with a catch-all fallback for anything unhandled on the event reactor)
|
150
|
+
* Work queue (thread pool)
|
151
|
+
* Coroutines / futures (makes use of Fibers)
|
152
|
+
|
153
|
+
### Server Name Indication
|
154
|
+
|
155
|
+
You can host a TLS enabled server with multiple hostnames using SNI.
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
server = reactor.tcp
|
159
|
+
server.bind('0.0.0.0', 3000, **{
|
160
|
+
hosts: [{
|
161
|
+
private_key: '/blah.key',
|
162
|
+
cert_chain: '/blah.crt',
|
163
|
+
host_name: 'somehost.com',
|
164
|
+
},
|
165
|
+
{
|
166
|
+
private_key: '/blah2.key',
|
167
|
+
cert_chain: '/blah2.crt',
|
168
|
+
host_name: 'somehost2.com'
|
169
|
+
},
|
170
|
+
{
|
171
|
+
private_key: '/blah3.key',
|
172
|
+
cert_chain: '/blah3.crt',
|
173
|
+
host_name: 'somehost3.com'
|
174
|
+
}]
|
175
|
+
}) do |client|
|
176
|
+
client.start_tls
|
177
|
+
client.start_read
|
178
|
+
end
|
179
|
+
|
180
|
+
# at some point later
|
181
|
+
server.add_host(private_key: '/blah4.key', cert_chain: '/blah4.crt', host_name: 'somehost4.com')
|
182
|
+
server.remove_host('somehost2.com')
|
183
|
+
```
|
184
|
+
|
185
|
+
You don't have to specify any hosts at binding time.
|
186
|
+
|
187
|
+
|
188
|
+
## Protocols and 3rd party plugins
|
189
|
+
|
190
|
+
* [HTTP](https://github.com/cotag/uv-rays - with SNI [server name indication] support)
|
191
|
+
* [Faraday plugin](https://github.com/cotag/uv-rays/blob/master/lib/faraday/adapter/libuv.rb)
|
192
|
+
* [HTTPI plugin](https://github.com/cotag/uv-rays/blob/master/lib/httpi/adapter/libuv.rb)
|
193
|
+
* [HTTP2](https://github.com/igrigorik/http-2)
|
194
|
+
* [SOAP](https://github.com/savonrb/savon) (using HTTPI plugin)
|
195
|
+
* [SNMP](https://github.com/acaprojects/ruby-engine/blob/master/lib/protocols/snmp.rb)
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rspec/core/rake_task' # testing framework
|
3
|
+
require 'yard' # yard documentation
|
4
|
+
require 'ffi' # loads the extension
|
5
|
+
require 'rake/clean' # for the :clobber rake task
|
6
|
+
require File.expand_path('../lib/mt-libuv/ext/tasks', __FILE__) # platform specific rake tasks used by compile
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
# By default we don't run network tests
|
11
|
+
task :default => :limited_spec
|
12
|
+
RSpec::Core::RakeTask.new(:limited_spec) do |t|
|
13
|
+
# Exclude network tests
|
14
|
+
t.rspec_opts = "--tag ~network"
|
15
|
+
end
|
16
|
+
RSpec::Core::RakeTask.new(:spec)
|
17
|
+
|
18
|
+
|
19
|
+
desc "Run all tests"
|
20
|
+
task :test => [:spec]
|
21
|
+
|
22
|
+
|
23
|
+
YARD::Rake::YardocTask.new do |t|
|
24
|
+
t.files = ['lib/**/*.rb', '-', 'ext/README.md', 'README.md']
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
desc "Compile libuv from submodule"
|
29
|
+
task :compile => ["ext/libuv/lib/libuv.#{FFI::Platform::LIBSUFFIX}"]
|
30
|
+
|
31
|
+
CLOBBER.include("ext/libuv/lib/libuv.#{FFI::Platform::LIBSUFFIX}")
|
data/ext/README.md
ADDED
data/ext/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
if ENV.has_key?('USE_GLOBAL_LIBUV')
|
5
|
+
exit(0)
|
6
|
+
else
|
7
|
+
require 'rubygems'
|
8
|
+
require 'ffi'
|
9
|
+
require 'rake/clean'
|
10
|
+
require '../lib/libuv/ext/tasks'
|
11
|
+
|
12
|
+
Dir.chdir File.expand_path("../", __FILE__)
|
13
|
+
Dir.chdir '..'
|
14
|
+
|
15
|
+
task :default => :libuv
|
16
|
+
|
17
|
+
if FFI::Platform.windows?
|
18
|
+
task :libuv do
|
19
|
+
FileUtils.mkdir('ext/libuv/lib')
|
20
|
+
FileUtils.cp 'ext/libuv.dll', 'ext/libuv/lib/libuv.dll'
|
21
|
+
end
|
22
|
+
else
|
23
|
+
desc "Compile libuv from submodule"
|
24
|
+
task :libuv => ["ext/libuv/lib/libuv.#{FFI::Platform::LIBSUFFIX}"]
|
25
|
+
|
26
|
+
CLOBBER.include("ext/libuv/lib/libuv.#{FFI::Platform::LIBSUFFIX}")
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MTLibuv
|
4
|
+
class Async < Handle
|
5
|
+
|
6
|
+
|
7
|
+
define_callback function: :on_async
|
8
|
+
|
9
|
+
|
10
|
+
# @param reactor [::MTLibuv::Reactor] reactor this async callback will be associated
|
11
|
+
def initialize(reactor)
|
12
|
+
@reactor = reactor
|
13
|
+
|
14
|
+
async_ptr = ::MTLibuv::Ext.allocate_handle_async
|
15
|
+
on_async = callback(:on_async, async_ptr.address)
|
16
|
+
error = check_result(::MTLibuv::Ext.async_init(reactor.handle, async_ptr, on_async))
|
17
|
+
|
18
|
+
super(async_ptr, error)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Triggers a notify event, calling everything in the notify chain
|
22
|
+
def call
|
23
|
+
return if @closed
|
24
|
+
error = check_result ::MTLibuv::Ext.async_send(handle)
|
25
|
+
reject(error) if error
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
# Used to update the callback that will be triggered when async is called
|
30
|
+
#
|
31
|
+
# @param callback [Proc] the callback to be called on reactor prepare
|
32
|
+
def progress(&callback)
|
33
|
+
@callback = callback
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
|
41
|
+
def on_async(handle)
|
42
|
+
@reactor.exec do
|
43
|
+
begin
|
44
|
+
@callback.call
|
45
|
+
rescue Exception => e
|
46
|
+
@reactor.log e, 'performing async callback'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MTLibuv
|
4
|
+
class Check < Handle
|
5
|
+
|
6
|
+
|
7
|
+
define_callback function: :on_check
|
8
|
+
|
9
|
+
|
10
|
+
# @param reactor [::MTLibuv::Reactor] reactor this check will be associated
|
11
|
+
# @param callback [Proc] callback to be called on reactor check
|
12
|
+
def initialize(reactor)
|
13
|
+
@reactor = reactor
|
14
|
+
|
15
|
+
check_ptr = ::MTLibuv::Ext.allocate_handle_check
|
16
|
+
error = check_result(::MTLibuv::Ext.check_init(reactor.handle, check_ptr))
|
17
|
+
|
18
|
+
super(check_ptr, error)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Enables the check handler.
|
22
|
+
def start
|
23
|
+
return if @closed
|
24
|
+
error = check_result ::MTLibuv::Ext.check_start(handle, callback(:on_check))
|
25
|
+
reject(error) if error
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
# Disables the check handler.
|
30
|
+
def stop
|
31
|
+
return if @closed
|
32
|
+
error = check_result ::MTLibuv::Ext.check_stop(handle)
|
33
|
+
reject(error) if error
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
# Used to update the callback that will be triggered on reactor check
|
38
|
+
#
|
39
|
+
# @param callback [Proc] the callback to be called on reactor check
|
40
|
+
def progress(&callback)
|
41
|
+
@callback = callback
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
|
49
|
+
def on_check(handle)
|
50
|
+
@reactor.exec do
|
51
|
+
begin
|
52
|
+
@callback.call
|
53
|
+
rescue Exception => e
|
54
|
+
@reactor.log e, 'performing check callback'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fiber'
|
4
|
+
|
5
|
+
class CoroutineRejection < RuntimeError
|
6
|
+
attr_accessor :value
|
7
|
+
end
|
8
|
+
|
9
|
+
module MTLibuv
|
10
|
+
# Takes a Promise response and turns it into a co-routine
|
11
|
+
# for code execution without using callbacks
|
12
|
+
#
|
13
|
+
# @param *promises [::MTLibuv::Q::Promise] a number of promises that will be combined into a single promise
|
14
|
+
# @return [Object] Returns the result of a single promise or an array of results if provided multiple promises
|
15
|
+
# @raise [Exception] if the promise is rejected
|
16
|
+
def co(*yieldable)
|
17
|
+
on_reactor = MTLibuv::Reactor.current
|
18
|
+
raise 'must be running on a reactor thread to use coroutines' unless on_reactor
|
19
|
+
|
20
|
+
f = Fiber.current
|
21
|
+
wasError = false
|
22
|
+
|
23
|
+
# Convert the input into a promise on the current reactor
|
24
|
+
if yieldable.length == 1
|
25
|
+
promise = yieldable[0]
|
26
|
+
# Passed independently as this is often overwritten for performance
|
27
|
+
promise.progress &Proc.new if block_given?
|
28
|
+
else
|
29
|
+
promise = on_reactor.all(*yieldable)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Use the promise to resume the Fiber
|
33
|
+
promise.then(proc { |res|
|
34
|
+
if MTLibuv::Reactor.current == on_reactor
|
35
|
+
f.resume res
|
36
|
+
else
|
37
|
+
on_reactor.schedule { f.resume(res) }
|
38
|
+
end
|
39
|
+
}, proc { |err|
|
40
|
+
wasError = true
|
41
|
+
if MTLibuv::Reactor.current == on_reactor
|
42
|
+
f.resume err
|
43
|
+
else
|
44
|
+
on_reactor.schedule { f.resume(err) }
|
45
|
+
end
|
46
|
+
})
|
47
|
+
|
48
|
+
# We want to prevent the reactor from stopping while we are waiting on a response
|
49
|
+
on_reactor.ref
|
50
|
+
result = Fiber.yield # Assign the result from the resume
|
51
|
+
on_reactor.unref
|
52
|
+
|
53
|
+
# Either return the result or raise an error
|
54
|
+
if wasError
|
55
|
+
if result.is_a?(Exception)
|
56
|
+
backtrace = caller
|
57
|
+
backtrace.shift
|
58
|
+
if result.respond_to?(:backtrace) && result.backtrace
|
59
|
+
backtrace << '---- continuation ----'
|
60
|
+
backtrace.concat(result.backtrace)
|
61
|
+
end
|
62
|
+
result.set_backtrace(backtrace)
|
63
|
+
raise result
|
64
|
+
else
|
65
|
+
e = case result
|
66
|
+
when String, Symbol
|
67
|
+
CoroutineRejection.new(result.to_s)
|
68
|
+
else
|
69
|
+
CoroutineRejection.new
|
70
|
+
end
|
71
|
+
e.value = result
|
72
|
+
raise e
|
73
|
+
end
|
74
|
+
end
|
75
|
+
result
|
76
|
+
end
|
77
|
+
|
78
|
+
module_function :co
|
79
|
+
end
|
data/lib/mt-libuv/dns.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MTLibuv
|
4
|
+
class Dns < Q::DeferredPromise
|
5
|
+
include Resource, Listener, Net
|
6
|
+
|
7
|
+
|
8
|
+
define_callback function: :on_complete, params: [:pointer, :int, Ext::UvAddrinfo.by_ref]
|
9
|
+
|
10
|
+
|
11
|
+
attr_reader :results
|
12
|
+
attr_reader :domain
|
13
|
+
attr_reader :port
|
14
|
+
attr_reader :hint
|
15
|
+
|
16
|
+
|
17
|
+
HINTS = {
|
18
|
+
:IPv4 => ::MTLibuv::Ext::UvAddrinfo.new,
|
19
|
+
:IPv6 => ::MTLibuv::Ext::UvAddrinfo.new
|
20
|
+
}
|
21
|
+
HINTS[:IPv4].tap do |hint|
|
22
|
+
hint[:family] = FFI::Platform.windows? ? 2 : Socket::Constants::AF_INET
|
23
|
+
hint[:socktype] = Socket::Constants::SOCK_STREAM
|
24
|
+
hint[:protocol] = Socket::Constants::IPPROTO_TCP
|
25
|
+
end
|
26
|
+
HINTS[:IPv6].tap do |hint|
|
27
|
+
hint[:family] = FFI::Platform.windows? ? 23 : Socket::Constants::AF_INET6
|
28
|
+
hint[:socktype] = Socket::Constants::SOCK_STREAM
|
29
|
+
hint[:protocol] = Socket::Constants::IPPROTO_TCP
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
# @param reactor [::MTLibuv::Reactor] reactor this work request will be associated
|
34
|
+
# @param domain [String] the domain name to resolve
|
35
|
+
# @param port [Integer, String] the port we wish to use
|
36
|
+
def initialize(reactor, domain, port, hint = :IPv4, wait: true)
|
37
|
+
super(reactor, reactor.defer)
|
38
|
+
|
39
|
+
@domain = domain
|
40
|
+
@port = port
|
41
|
+
@hint = hint
|
42
|
+
@complete = false
|
43
|
+
@pointer = ::MTLibuv::Ext.allocate_request_getaddrinfo
|
44
|
+
@error = nil # error in callback
|
45
|
+
|
46
|
+
@instance_id = @pointer.address
|
47
|
+
error = check_result ::MTLibuv::Ext.getaddrinfo(@reactor, @pointer, callback(:on_complete), domain, port.to_s, HINTS[hint])
|
48
|
+
|
49
|
+
if error
|
50
|
+
::MTLibuv::Ext.free(@pointer)
|
51
|
+
@complete = true
|
52
|
+
@defer.reject(error)
|
53
|
+
end
|
54
|
+
|
55
|
+
@defer.promise.value if wait
|
56
|
+
end
|
57
|
+
|
58
|
+
# Indicates if the lookup has completed yet or not.
|
59
|
+
#
|
60
|
+
# @return [true, false]
|
61
|
+
def completed?
|
62
|
+
return @complete
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
|
69
|
+
def on_complete(req, status, addrinfo)
|
70
|
+
@complete = true
|
71
|
+
::MTLibuv::Ext.free(req)
|
72
|
+
|
73
|
+
e = check_result(status)
|
74
|
+
|
75
|
+
@reactor.exec do
|
76
|
+
if e
|
77
|
+
@defer.reject(e)
|
78
|
+
else
|
79
|
+
begin
|
80
|
+
current = addrinfo
|
81
|
+
@results = []
|
82
|
+
while !current.null?
|
83
|
+
@results << get_ip_and_port(current[:addr])
|
84
|
+
current = current[:next]
|
85
|
+
end
|
86
|
+
@defer.resolve(@results)
|
87
|
+
rescue Exception => e
|
88
|
+
@defer.reject(e)
|
89
|
+
end
|
90
|
+
::MTLibuv::Ext.freeaddrinfo(addrinfo)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Clean up references
|
95
|
+
cleanup_callbacks
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|