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 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: