lightio 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/lightio.svg)](http://rubygems.org/gems/lightio)
|
5
|
-
[![Build Status](https://travis-ci.org/
|
5
|
+
[![Build Status](https://travis-ci.org/socketry/lightio.svg?branch=master)](https://travis-ci.org/socketry/lightio)
|
6
|
+
[![Coverage Status](https://coveralls.io/repos/github/socketry/lightio/badge.svg?branch=master)](https://coveralls.io/github/socketry/lightio?branch=master)
|
6
7
|
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](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:
|