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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c471b6d1a0f049a6db03568e4f2ab5c0ac718f7c
4
- data.tar.gz: 6cf6282f1f363bb1f01eea73fa7443e485b64e0e
3
+ metadata.gz: 743e461666313c581ce9bea53674e037a237d3a9
4
+ data.tar.gz: 381120d60e506278cb77c80ad8d2384fed8f51a0
5
5
  SHA512:
6
- metadata.gz: 430b7f1839c6d72ec0fa5e28164965b55a3a3f616d8d94f5f3a5070b97f5c7e5905b78b859520d5f7012e323b0c54a034dc3ac526e961703a0d8f798992fb29b
7
- data.tar.gz: ba69751cc11872bd57fe32f8aa8f5e6202cd0fce3331ba8e8182e9523ac2f8dcdff0f80aa65d9024ff063a5aa436c2c63f5f7fa24fb8197f9c66c0cfff3cdb47
6
+ metadata.gz: 68d02d089da02ecf3a7aaeb2562c48a6e8c64d14b7c56ec88859fb7d0c8e926054bdc92af26057ffdad0da3fedf6697b26d6c8da59931b05c532cd2aa8aa1864
7
+ data.tar.gz: 230877f84e6c54a89f7afbeaa47cc8328e52b6b8d51936bd91c11f86954c061ee8772f5d65cae014bc2c82fcae5c8f826fbbf089e31f4688697efb6300bab4aa
@@ -10,6 +10,7 @@ rvm:
10
10
  env:
11
11
  global:
12
12
  - JRUBY_OPTS="--dev -J-Djruby.launch.inproc=true -J-Xmx1024M"
13
+ - COVERAGE=true
13
14
 
14
15
  matrix:
15
16
  allow_failures:
data/Gemfile CHANGED
@@ -4,3 +4,8 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in lightio.gemspec
6
6
  gemspec
7
+
8
+ group :test do
9
+ gem 'coveralls', require: false
10
+ end
11
+
@@ -1,13 +1,21 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lightio (0.1.1)
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)
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2017 jjy
3
+ Copyright (c) 2017 Jiang Jinyang
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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/jjyr/lightio.svg?branch=master)](https://travis-ci.org/jjyr/lightio)
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
- See [wiki](https://github.com/jjyr/lightio/wiki) for more information
36
+ [Please see LightIO Wiki](https://github.com/jjyr/lightio/wiki) for more information.
58
37
 
59
- [API Documentation](http://www.rubydoc.info/gems/lightio/frames)
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.wait_read
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.wait_read
26
+ while socket_watcher.wait_readable
27
27
  echo(socket)
28
28
  end
29
29
  rescue EOFError
@@ -3,6 +3,7 @@ require 'lightio/version'
3
3
  require 'lightio/errors'
4
4
  require 'lightio/core'
5
5
  require 'lightio/watchers'
6
+ require 'lightio/wrap'
6
7
  require 'lightio/library'
7
8
 
8
9
  # LightIO provide light-weight executor: LightIO::Beam and batch io libraries,
@@ -1,11 +1,11 @@
1
1
  # Core
2
- require 'lightio/core/ioloop'
3
- require 'lightio/core/light_fiber'
4
- require 'lightio/core/future'
5
- require 'lightio/core/beam'
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
@@ -107,9 +107,7 @@ module LightIO::Core
107
107
  def handle_selectables
108
108
  @selector.select(0) do |monitor|
109
109
  # invoke callback if io is ready
110
- if monitor.readiness
111
- monitor.value.call(monitor.io)
112
- end
110
+ monitor.value.call(monitor.io)
113
111
  end
114
112
  end
115
113
 
@@ -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
@@ -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
@@ -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 id}
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
- if (result = future.value) != id
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
 
@@ -1,6 +1,8 @@
1
- require 'lightio/library/queue'
2
- require 'lightio/library/kernel_ext'
3
- require 'lightio/library/timeout'
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
@@ -1,3 +1,3 @@
1
1
  module LightIO
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,7 +1,7 @@
1
- require 'lightio/watchers/watcher'
2
- require 'lightio/watchers/timer'
3
- require 'lightio/watchers/schedule'
4
- require 'lightio/watchers/io'
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
@@ -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
- def interests
35
- @monitor.interests
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 wait_read(timeout=nil)
56
- LightIO::Timeout.timeout(timeout) do
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 writeable
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 wait_write(timeout=nil)
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
- wait_for :w
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
- # stop io listening
82
- def close
83
- @monitor.close
78
+ def set_callback(&blk)
79
+ @callback = blk
84
80
  end
85
81
 
86
- def close?
87
- @monitor.close?
88
- end
82
+ private
89
83
 
90
- def wait
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
- callback.call if @waiting && io_is_ready?
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 @wait_for == :r
111
- @monitor.readable?
112
- elsif @wait_for == :w
113
- @monitor.writeable?
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
@@ -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
@@ -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 = ["jjy"]
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.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
- - jjy
7
+ - Jiang Jinyang
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-12-11 00:00:00.000000000 Z
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: