lightio 0.1.1 → 0.2.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 +4 -4
- data/.travis.yml +1 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +19 -1
- data/LICENSE.txt +1 -1
- data/README.md +10 -25
- data/examples/echo_server.rb +38 -0
- data/examples/echo_server_with_raw_socket.rb +2 -2
- data/lib/lightio.rb +1 -0
- data/lib/lightio/core.rb +5 -5
- data/lib/lightio/core/backend/nio.rb +1 -3
- data/lib/lightio/core/beam.rb +18 -0
- data/lib/lightio/core/future.rb +6 -2
- data/lib/lightio/core/ioloop.rb +9 -2
- data/lib/lightio/library.rb +5 -3
- data/lib/lightio/library/io.rb +79 -0
- data/lib/lightio/library/socket.rb +93 -0
- data/lib/lightio/version.rb +1 -1
- data/lib/lightio/watchers.rb +4 -4
- data/lib/lightio/watchers/io.rb +43 -46
- data/lib/lightio/wrap.rb +132 -0
- data/lightio.gemspec +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 743e461666313c581ce9bea53674e037a237d3a9
|
4
|
+
data.tar.gz: 381120d60e506278cb77c80ad8d2384fed8f51a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68d02d089da02ecf3a7aaeb2562c48a6e8c64d14b7c56ec88859fb7d0c8e926054bdc92af26057ffdad0da3fedf6697b26d6c8da59931b05c532cd2aa8aa1864
|
7
|
+
data.tar.gz: 230877f84e6c54a89f7afbeaa47cc8328e52b6b8d51936bd91c11f86954c061ee8772f5d65cae014bc2c82fcae5c8f826fbbf089e31f4688697efb6300bab4aa
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,21 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
lightio (0.
|
4
|
+
lightio (0.2.0)
|
5
5
|
nio4r (~> 2.1)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
+
coveralls (0.8.21)
|
11
|
+
json (>= 1.8, < 3)
|
12
|
+
simplecov (~> 0.14.1)
|
13
|
+
term-ansicolor (~> 1.3)
|
14
|
+
thor (~> 0.19.4)
|
15
|
+
tins (~> 1.6)
|
10
16
|
diff-lcs (1.3)
|
17
|
+
docile (1.1.5)
|
18
|
+
json (2.1.0)
|
11
19
|
nio4r (2.1.0)
|
12
20
|
rake (10.1.0)
|
13
21
|
rspec (3.7.0)
|
@@ -23,12 +31,22 @@ GEM
|
|
23
31
|
diff-lcs (>= 1.2.0, < 2.0)
|
24
32
|
rspec-support (~> 3.7.0)
|
25
33
|
rspec-support (3.7.0)
|
34
|
+
simplecov (0.14.1)
|
35
|
+
docile (~> 1.1.0)
|
36
|
+
json (>= 1.8, < 3)
|
37
|
+
simplecov-html (~> 0.10.0)
|
38
|
+
simplecov-html (0.10.2)
|
39
|
+
term-ansicolor (1.6.0)
|
40
|
+
tins (~> 1.0)
|
41
|
+
thor (0.19.4)
|
42
|
+
tins (1.16.3)
|
26
43
|
|
27
44
|
PLATFORMS
|
28
45
|
ruby
|
29
46
|
|
30
47
|
DEPENDENCIES
|
31
48
|
bundler (~> 1.16)
|
49
|
+
coveralls
|
32
50
|
lightio!
|
33
51
|
rake (~> 10.0)
|
34
52
|
rspec (~> 3.0)
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
|
4
4
|
[](http://rubygems.org/gems/lightio)
|
5
|
-
[](https://travis-ci.org/socketry/lightio)
|
6
|
+
[](https://coveralls.io/github/socketry/lightio?branch=master)
|
6
7
|
[](https://github.com/jjyr/lightio/blob/master/LICENSE.txt)
|
7
8
|
|
8
9
|
LightIO is a ruby networking library, that combines ruby fiber and fast IO event loop.
|
@@ -30,33 +31,15 @@ Or install it yourself as:
|
|
30
31
|
|
31
32
|
$ gem install lightio
|
32
33
|
|
33
|
-
## Usage
|
34
|
-
|
35
|
-
``` ruby
|
36
|
-
require 'lightio'
|
37
|
-
|
38
|
-
start = Time.now
|
39
|
-
|
40
|
-
beams = 1000.times.map do
|
41
|
-
# LightIO::Beam is a thread-like executor, use it instead Thread
|
42
|
-
LightIO::Beam.new do
|
43
|
-
# do some io operations in beam
|
44
|
-
LightIO.sleep(1)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
beams.each(&:join)
|
49
|
-
seconds = Time.now - start
|
50
|
-
puts "1000 beams take #{seconds - 1} seconds to create"
|
51
|
-
```
|
52
|
-
|
53
|
-
View more [examples](/examples).
|
54
|
-
|
55
34
|
## Documentation
|
56
35
|
|
57
|
-
|
36
|
+
[Please see LightIO Wiki](https://github.com/jjyr/lightio/wiki) for more information.
|
58
37
|
|
59
|
-
|
38
|
+
The following documentations is also usable:
|
39
|
+
|
40
|
+
* [Basic usage](https://github.com/socketry/lightio/wiki/Basic-Usage)
|
41
|
+
* [YARD documentation](http://www.rubydoc.info/gems/lightio/frames)
|
42
|
+
* [Examples](/examples)
|
60
43
|
|
61
44
|
## Discussion
|
62
45
|
|
@@ -76,6 +59,8 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/jjyr/l
|
|
76
59
|
|
77
60
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
78
61
|
|
62
|
+
Copyright, 2017, by [Jiang Jinyang](http://justjjy.com/)
|
63
|
+
|
79
64
|
## Code of Conduct
|
80
65
|
|
81
66
|
Everyone interacting in the Lightio project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/lightio/blob/master/CODE_OF_CONDUCT.md).
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Example from https://github.com/socketry/nio4r/blob/master/examples/echo_server.rb
|
2
|
+
# rewrite it in lightio for demonstrate
|
3
|
+
|
4
|
+
require 'lightio'
|
5
|
+
|
6
|
+
class EchoServer
|
7
|
+
def initialize(host, port)
|
8
|
+
@server = LightIO::TCPServer.new(host, port)
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
while (socket = @server.accept)
|
13
|
+
_, port, host = socket.peeraddr
|
14
|
+
puts "accept connection from #{host}:#{port}"
|
15
|
+
|
16
|
+
# LightIO::Beam is lightweight executor, provide thread-like interface
|
17
|
+
# just start new beam for per socket
|
18
|
+
LightIO::Beam.new(socket) do |socket|
|
19
|
+
loop do
|
20
|
+
echo(socket)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def echo(socket)
|
27
|
+
data = socket.readpartial(4096)
|
28
|
+
socket.write(data)
|
29
|
+
rescue EOFError
|
30
|
+
_, port, host = socket.peeraddr
|
31
|
+
puts "*** #{host}:#{port} disconnected"
|
32
|
+
socket.close
|
33
|
+
raise
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
EchoServer.new('localhost', 3000).run if __FILE__ == $0
|
@@ -13,7 +13,7 @@ class EchoServer
|
|
13
13
|
def run
|
14
14
|
# wait server until readable
|
15
15
|
server_watcher = LightIO::Watchers::IO.new(@server, :r)
|
16
|
-
while server_watcher.
|
16
|
+
while server_watcher.wait_readable
|
17
17
|
socket = @server.accept
|
18
18
|
_, port, host = socket.peeraddr
|
19
19
|
puts "accept connection from #{host}:#{port}"
|
@@ -23,7 +23,7 @@ class EchoServer
|
|
23
23
|
LightIO::Beam.new(socket) do |socket|
|
24
24
|
socket_watcher = LightIO::Watchers::IO.new(socket, :r)
|
25
25
|
begin
|
26
|
-
while socket_watcher.
|
26
|
+
while socket_watcher.wait_readable
|
27
27
|
echo(socket)
|
28
28
|
end
|
29
29
|
rescue EOFError
|
data/lib/lightio.rb
CHANGED
data/lib/lightio/core.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# Core
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
require_relative 'core/ioloop'
|
3
|
+
require_relative 'core/light_fiber'
|
4
|
+
require_relative 'core/future'
|
5
|
+
require_relative 'core/beam'
|
6
6
|
|
7
7
|
# LightIO::Core include core classes: Beam, IOloop, Future
|
8
8
|
#
|
9
9
|
# view examples for clues
|
10
10
|
module LightIO::Core
|
11
|
-
end
|
11
|
+
end
|
data/lib/lightio/core/beam.rb
CHANGED
@@ -17,6 +17,16 @@ module LightIO::Core
|
|
17
17
|
# b.alive? # false
|
18
18
|
class Beam < LightFiber
|
19
19
|
|
20
|
+
# special class for simulate Thread#raise for Beam
|
21
|
+
class BeamError
|
22
|
+
attr_reader :error, :parent
|
23
|
+
|
24
|
+
def initialize(error)
|
25
|
+
@error = error
|
26
|
+
@parent = Beam.current
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
20
30
|
# Create a new beam
|
21
31
|
#
|
22
32
|
# Beam is light-weight executor, provide thread-like interface
|
@@ -94,6 +104,14 @@ module LightIO::Core
|
|
94
104
|
self
|
95
105
|
end
|
96
106
|
|
107
|
+
# Fiber not provide raise method, so we have to simulate one
|
108
|
+
# @param [BeamError] error currently only support raise BeamError
|
109
|
+
def raise(error)
|
110
|
+
super unless error.is_a?(BeamError)
|
111
|
+
self.parent = error.parent if error.parent
|
112
|
+
raise error.error
|
113
|
+
end
|
114
|
+
|
97
115
|
class << self
|
98
116
|
|
99
117
|
# Schedule beams
|
data/lib/lightio/core/future.rb
CHANGED
@@ -22,18 +22,22 @@ module LightIO::Core
|
|
22
22
|
#
|
23
23
|
# use this method to set back result
|
24
24
|
def transfer(value=nil)
|
25
|
-
raise Error, "state error" if done?
|
25
|
+
raise LightIO::Error, "state error" if done?
|
26
26
|
@value = value
|
27
27
|
done!
|
28
28
|
@light_fiber.transfer if @light_fiber
|
29
29
|
end
|
30
30
|
|
31
|
+
def value=(value)
|
32
|
+
transfer(value)
|
33
|
+
end
|
34
|
+
|
31
35
|
# Get value
|
32
36
|
#
|
33
37
|
# this method will block current beam/fiber, until future result is set.
|
34
38
|
def value
|
35
39
|
return @value if done?
|
36
|
-
raise Error, 'already used' if @light_fiber
|
40
|
+
raise LightIO::Error, 'already used' if @light_fiber
|
37
41
|
@light_fiber = LightFiber.current
|
38
42
|
@ioloop.transfer
|
39
43
|
@value
|
data/lib/lightio/core/ioloop.rb
CHANGED
@@ -38,13 +38,20 @@ module LightIO::Core
|
|
38
38
|
future = Future.new
|
39
39
|
# add watcher to loop
|
40
40
|
id = Object.new
|
41
|
-
watcher.set_callback {future.transfer
|
41
|
+
watcher.set_callback {|err| future.transfer([id, err])}
|
42
42
|
watcher.start(self)
|
43
43
|
# trigger a fiber switch
|
44
44
|
# wait until watcher is ok
|
45
45
|
# then do work
|
46
|
-
|
46
|
+
response_id, err = future.value
|
47
|
+
if response_id != id
|
47
48
|
raise InvalidTransferError, "expect #{id}, but get #{result}"
|
49
|
+
elsif err
|
50
|
+
# if future return a err
|
51
|
+
# simulate Thread#raise to Beam , that we can shutdown beam blocking by socket accepting
|
52
|
+
# transfer back to which beam occur this err
|
53
|
+
# not sure this is a right way to do it
|
54
|
+
LightIO::Core::Beam.current.raise(err)
|
48
55
|
end
|
49
56
|
end
|
50
57
|
|
data/lib/lightio/library.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require_relative 'library/queue'
|
2
|
+
require_relative 'library/kernel_ext'
|
3
|
+
require_relative 'library/timeout'
|
4
|
+
require_relative 'library/io'
|
5
|
+
require_relative 'library/socket'
|
4
6
|
|
5
7
|
module LightIO
|
6
8
|
# Library include modules can cooperative with LightIO::Beam
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
module LightIO::Library
|
3
|
+
class IO
|
4
|
+
include LightIO::Wrap::Wrapper
|
5
|
+
wrap ::IO
|
6
|
+
|
7
|
+
extend Forwardable
|
8
|
+
def_delegators :@io_watcher, :wait, :wait_readable, :wait_writable
|
9
|
+
|
10
|
+
wrap_blocking_methods :read, :write, exception_symbol: false
|
11
|
+
|
12
|
+
alias_method :<<, :write
|
13
|
+
|
14
|
+
def read(length=nil, outbuf=nil)
|
15
|
+
raise ArgumentError, "negative length #{length} given" if length && length < 0
|
16
|
+
(outbuf ||= "").clear
|
17
|
+
loop do
|
18
|
+
readlen = length.nil? ? 4096 : length - outbuf.size
|
19
|
+
begin
|
20
|
+
outbuf << wait_nonblock(:read_nonblock, readlen, exception_symbol: false)
|
21
|
+
if length == outbuf.size
|
22
|
+
return outbuf
|
23
|
+
end
|
24
|
+
rescue EOFError
|
25
|
+
return outbuf
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def readpartial(maxlen, outbuf=nil)
|
31
|
+
(outbuf ||= "").clear
|
32
|
+
outbuf << wait_nonblock(:read_nonblock, maxlen, exception_symbol: false)
|
33
|
+
outbuf
|
34
|
+
end
|
35
|
+
|
36
|
+
class << self
|
37
|
+
def open(*args)
|
38
|
+
io = self.new(*args)
|
39
|
+
yield io
|
40
|
+
ensure
|
41
|
+
io.close if io.respond_to? :close
|
42
|
+
end
|
43
|
+
|
44
|
+
def pipe(*args)
|
45
|
+
r, w = raw_class.pipe
|
46
|
+
if block_given?
|
47
|
+
begin
|
48
|
+
return yield r, w
|
49
|
+
ensure
|
50
|
+
w.close
|
51
|
+
r.close
|
52
|
+
end
|
53
|
+
end
|
54
|
+
[IO._wrap(r), IO._wrap(w)]
|
55
|
+
end
|
56
|
+
|
57
|
+
def select(read_fds, write_fds=nil, _except_fds=nil, timeout=nil)
|
58
|
+
timer = 0
|
59
|
+
# run once ioloop
|
60
|
+
LightIO.sleep 0
|
61
|
+
loop do
|
62
|
+
r_fds = (read_fds || []).select {|fd| fd.closed? ? raise(IOError, 'closed stream') : fd.instance_variable_get(:@io_watcher).readable?}
|
63
|
+
w_fds = (write_fds || []).select {|fd| fd.closed? ? raise(IOError, 'closed stream') : fd.instance_variable_get(:@io_watcher).writable?}
|
64
|
+
e_fds = []
|
65
|
+
if r_fds.empty? && w_fds.empty?
|
66
|
+
interval = 0.1
|
67
|
+
LightIO.sleep interval
|
68
|
+
timer += interval
|
69
|
+
if timeout && timer > timeout
|
70
|
+
return nil
|
71
|
+
end
|
72
|
+
else
|
73
|
+
return [r_fds, w_fds, e_fds]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'socket'
|
2
|
+
module LightIO::Library
|
3
|
+
|
4
|
+
class BasicSocket < IO
|
5
|
+
include LightIO::Wrap::Wrapper
|
6
|
+
wrap ::BasicSocket
|
7
|
+
|
8
|
+
wrap_blocking_methods :recv, :recvmsg, :sendmsg
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def for_fd(fd)
|
12
|
+
Socket._wrap(raw_class.for_fd(fd))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Socket < BasicSocket
|
18
|
+
include ::Socket::Constants
|
19
|
+
include LightIO::Wrap::Wrapper
|
20
|
+
wrap ::Socket
|
21
|
+
|
22
|
+
wrap_blocking_methods :connect, :recvfrom
|
23
|
+
|
24
|
+
## implement ::Socket instance methods
|
25
|
+
def accept
|
26
|
+
socket, addrinfo = wait_nonblock(:accept_nonblock)
|
27
|
+
[Socket._wrap(socket), LightIO::Library::Addrinfo._wrap(addrinfo)]
|
28
|
+
end
|
29
|
+
|
30
|
+
def sys_accept
|
31
|
+
@io_watcher.wait_readable
|
32
|
+
@io.sys_accept
|
33
|
+
end
|
34
|
+
|
35
|
+
# bind addr?
|
36
|
+
|
37
|
+
class << self
|
38
|
+
## implement ::Socket class methods
|
39
|
+
wrap_methods_run_in_threads_pool :getaddrinfo, :gethostbyaddr, :gethostbyname, :gethostname, :getifaddrs,
|
40
|
+
:getnameinfo, :getservbyname
|
41
|
+
|
42
|
+
def socketpair(domain, type, protocol)
|
43
|
+
raw_class.socketpair(domain, type, protocol).map {|s| _wrap(s)}
|
44
|
+
end
|
45
|
+
|
46
|
+
alias_method :pair, :socketpair
|
47
|
+
|
48
|
+
def unix_server_socket(path)
|
49
|
+
if block_given?
|
50
|
+
raw_class.unix_server_socket(path) {|s| yield _wrap(s)}
|
51
|
+
else
|
52
|
+
_wrap(raw_class.unix_server_socket(path))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def ip_sockets_port0(ai_list, reuseaddr)
|
57
|
+
raw_class.ip_sockets_port0(ai_list, reuseaddr).map {|s| _wrap(s)}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
class IPSocket < BasicSocket
|
64
|
+
include LightIO::Wrap::Wrapper
|
65
|
+
wrap ::IPSocket
|
66
|
+
|
67
|
+
class << self
|
68
|
+
wrap_methods_run_in_threads_pool :getaddress
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class TCPSocket < IPSocket
|
73
|
+
include LightIO::Wrap::Wrapper
|
74
|
+
wrap ::TCPSocket
|
75
|
+
wrap_methods_run_in_threads_pool :gethostbyname
|
76
|
+
end
|
77
|
+
|
78
|
+
class TCPServer < TCPSocket
|
79
|
+
include LightIO::Wrap::Wrapper
|
80
|
+
wrap ::TCPServer
|
81
|
+
|
82
|
+
## implement ::Socket instance methods
|
83
|
+
def accept
|
84
|
+
socket = wait_nonblock(:accept_nonblock)
|
85
|
+
Socket._wrap(socket)
|
86
|
+
end
|
87
|
+
|
88
|
+
def sys_accept
|
89
|
+
@io_watcher.wait_readable
|
90
|
+
@io.sys_accept
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/lightio/version.rb
CHANGED
data/lib/lightio/watchers.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
require_relative 'watchers/watcher'
|
2
|
+
require_relative 'watchers/timer'
|
3
|
+
require_relative 'watchers/schedule'
|
4
|
+
require_relative 'watchers/io'
|
5
5
|
|
6
6
|
# Watcher is a abstract struct, for libraries to interact with ioloop
|
7
7
|
# see IOloop#wait method
|
data/lib/lightio/watchers/io.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module LightIO::Watchers
|
2
4
|
# LightIO::Watchers::IO provide a NIO::Monitor wrap to manage 'raw' socket / io
|
3
5
|
#
|
@@ -15,14 +17,14 @@ module LightIO::Watchers
|
|
15
17
|
# @param [Socket] io An IO-able object
|
16
18
|
# @param [Symbol] interests :r, :w, :rw - Is io readable? writeable? or both
|
17
19
|
# @return [LightIO::Watchers::IO]
|
18
|
-
def initialize(io, interests)
|
20
|
+
def initialize(io, interests=:rw)
|
19
21
|
@io = io
|
20
22
|
@ioloop = LightIO::Core::IOloop.current
|
21
23
|
@waiting = false
|
22
|
-
@wait_for = nil
|
23
24
|
# NIO monitor
|
24
25
|
@monitor = @ioloop.add_io_wait(@io, interests) {callback_on_waiting}
|
25
26
|
ObjectSpace.define_finalizer(self, self.class.finalizer(@monitor))
|
27
|
+
@error = nil
|
26
28
|
end
|
27
29
|
|
28
30
|
class << self
|
@@ -31,63 +33,56 @@ module LightIO::Watchers
|
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
# Replace current interests
|
39
|
-
def interests=(interests)
|
40
|
-
@monitor.interests = interests
|
41
|
-
end
|
36
|
+
extend Forwardable
|
37
|
+
def_delegators :@monitor, :interests, :interests=, :closed?, :readable?, :writable?, :writeable?
|
42
38
|
|
43
|
-
# Blocking until io interests is satisfied
|
44
|
-
def wait_for(interests)
|
45
|
-
if (self.interests == :w || self.interests == :r) && interests != self.interests
|
46
|
-
raise ArgumentError, "IO interests is #{self.interests}, can't waiting for #{interests}"
|
47
|
-
end
|
48
|
-
@wait_for = interests
|
49
|
-
wait
|
50
|
-
end
|
51
39
|
|
52
40
|
# Blocking until io is readable
|
53
41
|
# @param [Numeric] timeout return nil after timeout seconds, otherwise return self
|
54
42
|
# @return [LightIO::Watchers::IO, nil]
|
55
|
-
def
|
56
|
-
|
57
|
-
wait_for :r
|
58
|
-
self
|
59
|
-
end
|
60
|
-
rescue Timeout::Error
|
61
|
-
nil
|
43
|
+
def wait_readable(timeout=nil)
|
44
|
+
wait timeout, :read
|
62
45
|
end
|
63
46
|
|
64
|
-
# Blocking until io is
|
47
|
+
# Blocking until io is writable
|
65
48
|
# @param [Numeric] timeout return nil after timeout seconds, otherwise return self
|
66
49
|
# @return [LightIO::Watchers::IO, nil]
|
67
|
-
def
|
50
|
+
def wait_writable(timeout=nil)
|
51
|
+
wait timeout, :write
|
52
|
+
end
|
53
|
+
|
54
|
+
def wait(timeout=nil, mode=:read)
|
68
55
|
LightIO::Timeout.timeout(timeout) do
|
69
|
-
|
56
|
+
interests = {read: :r, write: :w, read_write: :rw}[mode]
|
57
|
+
self.interests = interests
|
58
|
+
wait_in_ioloop
|
70
59
|
self
|
71
60
|
end
|
72
61
|
rescue Timeout::Error
|
73
62
|
nil
|
74
63
|
end
|
75
64
|
|
65
|
+
def close
|
66
|
+
return if closed?
|
67
|
+
@monitor.close
|
68
|
+
@error = IOError.new('closed stream')
|
69
|
+
callback_on_waiting
|
70
|
+
end
|
71
|
+
|
76
72
|
|
73
|
+
# just implement IOloop#wait watcher interface
|
77
74
|
def start(ioloop)
|
78
75
|
# do nothing
|
79
76
|
end
|
80
77
|
|
81
|
-
|
82
|
-
|
83
|
-
@monitor.close
|
78
|
+
def set_callback(&blk)
|
79
|
+
@callback = blk
|
84
80
|
end
|
85
81
|
|
86
|
-
|
87
|
-
@monitor.close?
|
88
|
-
end
|
82
|
+
private
|
89
83
|
|
90
|
-
|
84
|
+
# Blocking until io interests is satisfied
|
85
|
+
def wait_in_ioloop
|
91
86
|
raise LightIO::Error, "Watchers::IO can't cross threads" if @ioloop != LightIO::Core::IOloop.current
|
92
87
|
raise EOFError, "can't wait closed IO watcher" if @monitor.closed?
|
93
88
|
@waiting = true
|
@@ -95,22 +90,24 @@ module LightIO::Watchers
|
|
95
90
|
@waiting = false
|
96
91
|
end
|
97
92
|
|
98
|
-
def set_callback(&blk)
|
99
|
-
@callback = blk
|
100
|
-
end
|
101
|
-
|
102
|
-
private
|
103
|
-
|
104
93
|
def callback_on_waiting
|
105
94
|
# only call callback on waiting
|
106
|
-
|
95
|
+
return unless @waiting && io_is_ready?
|
96
|
+
if @error
|
97
|
+
# if error occurred in io waiting, send it to callback, see IOloop#wait
|
98
|
+
callback.call(LightIO::Core::Beam::BeamError.new(@error))
|
99
|
+
else
|
100
|
+
callback.call
|
101
|
+
end
|
107
102
|
end
|
108
103
|
|
109
104
|
def io_is_ready?
|
110
|
-
if
|
111
|
-
|
112
|
-
elsif
|
113
|
-
|
105
|
+
if interests == :r
|
106
|
+
readable?
|
107
|
+
elsif interests == :w
|
108
|
+
writeable?
|
109
|
+
else
|
110
|
+
readable? || writeable?
|
114
111
|
end
|
115
112
|
end
|
116
113
|
end
|
data/lib/lightio/wrap.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
module LightIO::Wrap
|
2
|
+
WRAPPERS = {}
|
3
|
+
module Wrapper
|
4
|
+
# wrap raw ruby io objects
|
5
|
+
#
|
6
|
+
# @param [IO, Socket] io raw ruby io object
|
7
|
+
def initialize(io=nil)
|
8
|
+
@io ||= io
|
9
|
+
@io_watcher ||= LightIO::Watchers::IO.new(@io)
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(method, *args)
|
13
|
+
@io.public_send(method, *args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def close(*args)
|
17
|
+
# close watcher before io closed
|
18
|
+
@io_watcher.close
|
19
|
+
@io.close(*args)
|
20
|
+
end
|
21
|
+
|
22
|
+
def shutdown(*args)
|
23
|
+
# close watcher before io shutdown
|
24
|
+
@io_watcher.close
|
25
|
+
@io.shutdown(*args)
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
def _wrap(io=nil)
|
30
|
+
@io ||= io
|
31
|
+
@io_watcher ||= LightIO::Watchers::IO.new(@io)
|
32
|
+
end
|
33
|
+
|
34
|
+
# wait io nonblock method
|
35
|
+
#
|
36
|
+
# @param [Symbol] method method name, example: wait_nonblock
|
37
|
+
# @param [args] args arguments pass to method
|
38
|
+
def wait_nonblock(method, *args, exception_symbol: true)
|
39
|
+
loop do
|
40
|
+
begin
|
41
|
+
result = if RUBY_VERSION > "2.3" && exception_symbol
|
42
|
+
@io.__send__(method, *args, exception: false)
|
43
|
+
else
|
44
|
+
@io.__send__(method, *args)
|
45
|
+
end
|
46
|
+
case result
|
47
|
+
when :wait_readable
|
48
|
+
@io_watcher.wait_readable
|
49
|
+
when :wait_writable
|
50
|
+
@io_watcher.wait_writable
|
51
|
+
else
|
52
|
+
return result
|
53
|
+
end
|
54
|
+
rescue IO::WaitReadable
|
55
|
+
@io_watcher.wait_readable
|
56
|
+
rescue IO::WaitWritable
|
57
|
+
@io_watcher.wait_writable
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# both works in class scope and singleton class scope
|
63
|
+
module SingletonClassCommonMethods
|
64
|
+
protected
|
65
|
+
# run method in thread pool for performance
|
66
|
+
def wrap_methods_run_in_threads_pool(*args)
|
67
|
+
#TODO
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module ClassMethods
|
72
|
+
# Wrap raw io objects
|
73
|
+
def _wrap(io)
|
74
|
+
# In case ruby stdlib return already patched Sockets, just do nothing
|
75
|
+
if io.is_a? self
|
76
|
+
io
|
77
|
+
else
|
78
|
+
# old new
|
79
|
+
obj = allocate
|
80
|
+
obj.send(:initialize, io)
|
81
|
+
obj
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# override new method, return wrapped class
|
86
|
+
def new(*args)
|
87
|
+
io = raw_class.new(*args)
|
88
|
+
_wrap(io)
|
89
|
+
end
|
90
|
+
|
91
|
+
include SingletonClassCommonMethods
|
92
|
+
|
93
|
+
protected
|
94
|
+
# wrap blocking method with "#{method}_nonblock"
|
95
|
+
#
|
96
|
+
# @param [Symbol] method method name, example: wait
|
97
|
+
def wrap_blocking_method(method, exception_symbol: true)
|
98
|
+
define_method method do |*args|
|
99
|
+
wait_nonblock(:"#{method}_nonblock", *args, exception_symbol: exception_symbol)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def wrap_blocking_methods(*methods, exception_symbol: true)
|
104
|
+
methods.each {|m| wrap_blocking_method(m, exception_symbol: exception_symbol)}
|
105
|
+
end
|
106
|
+
|
107
|
+
attr_reader :raw_class
|
108
|
+
|
109
|
+
# Set wrapped class
|
110
|
+
def wrap(raw_class)
|
111
|
+
@raw_class=raw_class
|
112
|
+
WRAPPERS[raw_class] = self
|
113
|
+
end
|
114
|
+
|
115
|
+
def method_missing(method, *args)
|
116
|
+
raw_class.public_send(method, *args)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class << self
|
121
|
+
def new_and_wrap(*args)
|
122
|
+
io = raw_class.new(*args)
|
123
|
+
_wrap(io)
|
124
|
+
end
|
125
|
+
|
126
|
+
def included(base)
|
127
|
+
base.send :extend, ClassMethods
|
128
|
+
base.singleton_class.send :extend, SingletonClassCommonMethods
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lightio.gemspec
CHANGED
@@ -6,7 +6,7 @@ require "lightio/version"
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "lightio"
|
8
8
|
spec.version = LightIO::VERSION
|
9
|
-
spec.authors = ["
|
9
|
+
spec.authors = ["Jiang Jinyang"]
|
10
10
|
spec.email = ["jjyruby@gmail.com"]
|
11
11
|
|
12
12
|
spec.summary = %q{LightIO is a ruby networking library, that combines ruby fiber and fast IO event loop.}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lightio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Jiang Jinyang
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-12-
|
11
|
+
date: 2017-12-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nio4r
|
@@ -87,6 +87,7 @@ files:
|
|
87
87
|
- bin/console
|
88
88
|
- bin/setup
|
89
89
|
- examples/beams.rb
|
90
|
+
- examples/echo_server.rb
|
90
91
|
- examples/echo_server_with_raw_socket.rb
|
91
92
|
- lib/lightio.rb
|
92
93
|
- lib/lightio/core.rb
|
@@ -97,8 +98,10 @@ files:
|
|
97
98
|
- lib/lightio/core/light_fiber.rb
|
98
99
|
- lib/lightio/errors.rb
|
99
100
|
- lib/lightio/library.rb
|
101
|
+
- lib/lightio/library/io.rb
|
100
102
|
- lib/lightio/library/kernel_ext.rb
|
101
103
|
- lib/lightio/library/queue.rb
|
104
|
+
- lib/lightio/library/socket.rb
|
102
105
|
- lib/lightio/library/timeout.rb
|
103
106
|
- lib/lightio/version.rb
|
104
107
|
- lib/lightio/watchers.rb
|
@@ -106,6 +109,7 @@ files:
|
|
106
109
|
- lib/lightio/watchers/schedule.rb
|
107
110
|
- lib/lightio/watchers/timer.rb
|
108
111
|
- lib/lightio/watchers/watcher.rb
|
112
|
+
- lib/lightio/wrap.rb
|
109
113
|
- lightio.gemspec
|
110
114
|
homepage: https://github.com/jjyr/lightio
|
111
115
|
licenses:
|