lightio 0.2.2 → 0.3.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 +2 -1
- data/Gemfile +6 -0
- data/Gemfile.lock +47 -3
- data/Guardfile +10 -0
- data/README.md +1 -1
- data/bin/_guard-core +21 -0
- data/bin/guard +21 -0
- data/lib/lightio/core/backend/nio.rb +14 -2
- data/lib/lightio/core/beam.rb +22 -5
- data/lib/lightio/core/ioloop.rb +14 -26
- data/lib/lightio/core/light_fiber.rb +14 -0
- data/lib/lightio/library/io.rb +87 -14
- data/lib/lightio/library/kernel_ext.rb +2 -2
- data/lib/lightio/library/mutex.rb +62 -0
- data/lib/lightio/library/queue.rb +2 -2
- data/lib/lightio/library/sized_queue.rb +64 -0
- data/lib/lightio/library/socket.rb +32 -3
- data/lib/lightio/library/thread.rb +295 -0
- data/lib/lightio/library/threads_wait.rb +59 -0
- data/lib/lightio/library.rb +3 -0
- data/lib/lightio/version.rb +1 -1
- data/lib/lightio/watchers/io.rb +68 -15
- data/lib/lightio/wrap.rb +13 -23
- data/lightio.gemspec +1 -1
- metadata +11 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2da560fd469b39e6d55ee4acbe7ef0bae81602ae
|
4
|
+
data.tar.gz: 665f6fc976e45793695b3daa53810096f7db88a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b1dbaf889b0b2649f110786e75aba70b42a02c0b887f3c9e5aff27bc5ee8049ffaec5ca911765492ddbff05ca24e773d0dcbff6859e05b40f1e64b571f0bad8c
|
7
|
+
data.tar.gz: e3bf2ff085faf5c582d6e57be7c9aff14a144f614b259720c86663d4fa71f23da895499152d4746fe5f28212984c0367520e2d0c9e06adfb8fb04aeb3461908c
|
data/.travis.yml
CHANGED
@@ -2,9 +2,9 @@ sudo: false
|
|
2
2
|
language: ruby
|
3
3
|
rvm:
|
4
4
|
- jruby-9.1.15.0 # latest stable
|
5
|
-
- 2.2.7
|
6
5
|
- 2.3.4
|
7
6
|
- 2.4.1
|
7
|
+
- 2.5.0
|
8
8
|
- ruby-head
|
9
9
|
|
10
10
|
env:
|
@@ -16,4 +16,5 @@ matrix:
|
|
16
16
|
allow_failures:
|
17
17
|
- rvm: ruby-head
|
18
18
|
- rvm: jruby-9.1.15.0
|
19
|
+
- rvm: 2.5.0
|
19
20
|
fast_finish: true
|
data/Gemfile
CHANGED
@@ -5,6 +5,12 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
|
5
5
|
# Specify your gem's dependencies in lightio.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
+
group :development do
|
9
|
+
gem 'guard'
|
10
|
+
gem 'guard-rspec'
|
11
|
+
gem 'guard-bundler'
|
12
|
+
end
|
13
|
+
|
8
14
|
group :test do
|
9
15
|
gem 'coveralls', require: false
|
10
16
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
lightio (0.
|
5
|
-
nio4r (~> 2.
|
4
|
+
lightio (0.3.0)
|
5
|
+
nio4r (~> 2.2)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
+
coderay (1.1.1)
|
10
11
|
coveralls (0.8.21)
|
11
12
|
json (>= 1.8, < 3)
|
12
13
|
simplecov (~> 0.14.1)
|
@@ -15,9 +16,46 @@ GEM
|
|
15
16
|
tins (~> 1.6)
|
16
17
|
diff-lcs (1.3)
|
17
18
|
docile (1.1.5)
|
19
|
+
ffi (1.9.18)
|
20
|
+
formatador (0.2.5)
|
21
|
+
guard (2.14.1)
|
22
|
+
formatador (>= 0.2.4)
|
23
|
+
listen (>= 2.7, < 4.0)
|
24
|
+
lumberjack (~> 1.0)
|
25
|
+
nenv (~> 0.1)
|
26
|
+
notiffany (~> 0.0)
|
27
|
+
pry (>= 0.9.12)
|
28
|
+
shellany (~> 0.0)
|
29
|
+
thor (>= 0.18.1)
|
30
|
+
guard-bundler (2.1.0)
|
31
|
+
bundler (~> 1.0)
|
32
|
+
guard (~> 2.2)
|
33
|
+
guard-compat (~> 1.1)
|
34
|
+
guard-compat (1.2.1)
|
35
|
+
guard-rspec (4.7.3)
|
36
|
+
guard (~> 2.1)
|
37
|
+
guard-compat (~> 1.1)
|
38
|
+
rspec (>= 2.99.0, < 4.0)
|
18
39
|
json (2.1.0)
|
19
|
-
|
40
|
+
listen (3.1.5)
|
41
|
+
rb-fsevent (~> 0.9, >= 0.9.4)
|
42
|
+
rb-inotify (~> 0.9, >= 0.9.7)
|
43
|
+
ruby_dep (~> 1.2)
|
44
|
+
lumberjack (1.0.12)
|
45
|
+
method_source (0.8.2)
|
46
|
+
nenv (0.3.0)
|
47
|
+
nio4r (2.2.0)
|
48
|
+
notiffany (0.1.1)
|
49
|
+
nenv (~> 0.1)
|
50
|
+
shellany (~> 0.0)
|
51
|
+
pry (0.10.4)
|
52
|
+
coderay (~> 1.1.0)
|
53
|
+
method_source (~> 0.8.1)
|
54
|
+
slop (~> 3.4)
|
20
55
|
rake (10.1.0)
|
56
|
+
rb-fsevent (0.10.2)
|
57
|
+
rb-inotify (0.9.10)
|
58
|
+
ffi (>= 0.5.0, < 2)
|
21
59
|
rspec (3.7.0)
|
22
60
|
rspec-core (~> 3.7.0)
|
23
61
|
rspec-expectations (~> 3.7.0)
|
@@ -31,11 +69,14 @@ GEM
|
|
31
69
|
diff-lcs (>= 1.2.0, < 2.0)
|
32
70
|
rspec-support (~> 3.7.0)
|
33
71
|
rspec-support (3.7.0)
|
72
|
+
ruby_dep (1.5.0)
|
73
|
+
shellany (0.0.1)
|
34
74
|
simplecov (0.14.1)
|
35
75
|
docile (~> 1.1.0)
|
36
76
|
json (>= 1.8, < 3)
|
37
77
|
simplecov-html (~> 0.10.0)
|
38
78
|
simplecov-html (0.10.2)
|
79
|
+
slop (3.6.0)
|
39
80
|
term-ansicolor (1.6.0)
|
40
81
|
tins (~> 1.0)
|
41
82
|
thor (0.19.4)
|
@@ -47,6 +88,9 @@ PLATFORMS
|
|
47
88
|
DEPENDENCIES
|
48
89
|
bundler (~> 1.16)
|
49
90
|
coveralls
|
91
|
+
guard
|
92
|
+
guard-bundler
|
93
|
+
guard-rspec
|
50
94
|
lightio!
|
51
95
|
rake (~> 10.0)
|
52
96
|
rspec (~> 3.0)
|
data/Guardfile
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
directories %w(. lib spec) \
|
2
|
+
.select {|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
3
|
+
|
4
|
+
guard :bundler do
|
5
|
+
watch('Gemfile')
|
6
|
+
end
|
7
|
+
|
8
|
+
guard :rspec, all_after_pass: false, all_on_start: false, failed_mode: :keep, cmd: 'bundle exec rspec' do
|
9
|
+
watch(%r{^(lib|spec)/(.+?)(_spec)?\.rb}) {|m| "spec/#{m[2]}_spec.rb"}
|
10
|
+
end
|
data/README.md
CHANGED
@@ -38,7 +38,7 @@ Or install it yourself as:
|
|
38
38
|
The following documentations is also usable:
|
39
39
|
|
40
40
|
* [Basic usage](https://github.com/socketry/lightio/wiki/Basic-Usage)
|
41
|
-
* [YARD documentation](http://www.rubydoc.info/
|
41
|
+
* [YARD documentation](http://www.rubydoc.info/github/socketry/lightio/master)
|
42
42
|
* [Examples](/examples)
|
43
43
|
|
44
44
|
## Discussion
|
data/bin/_guard-core
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application '_guard-core' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
12
|
+
load(bundle_binstub) if File.file?(bundle_binstub)
|
13
|
+
|
14
|
+
require "pathname"
|
15
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
16
|
+
Pathname.new(__FILE__).realpath)
|
17
|
+
|
18
|
+
require "rubygems"
|
19
|
+
require "bundler/setup"
|
20
|
+
|
21
|
+
load Gem.bin_path("guard", "_guard-core")
|
data/bin/guard
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'guard' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
12
|
+
load(bundle_binstub) if File.file?(bundle_binstub)
|
13
|
+
|
14
|
+
require "pathname"
|
15
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
16
|
+
Pathname.new(__FILE__).realpath)
|
17
|
+
|
18
|
+
require "rubygems"
|
19
|
+
require "bundler/setup"
|
20
|
+
|
21
|
+
load Gem.bin_path("guard", "guard")
|
@@ -55,7 +55,7 @@ module LightIO::Core
|
|
55
55
|
@running = false
|
56
56
|
@timers = Timers.new
|
57
57
|
@callbacks = []
|
58
|
-
@selector = ::NIO::Selector.new
|
58
|
+
@selector = ::NIO::Selector.new(env_backend)
|
59
59
|
end
|
60
60
|
|
61
61
|
def run
|
@@ -98,6 +98,15 @@ module LightIO::Core
|
|
98
98
|
raise
|
99
99
|
end
|
100
100
|
|
101
|
+
def backend
|
102
|
+
@selector.backend
|
103
|
+
end
|
104
|
+
|
105
|
+
def env_backend
|
106
|
+
key = 'LIGHTIO_BACKEND'.freeze
|
107
|
+
ENV.has_key?(key) ? ENV[key].to_sym : nil
|
108
|
+
end
|
109
|
+
|
101
110
|
private
|
102
111
|
|
103
112
|
def run_timers
|
@@ -112,7 +121,10 @@ module LightIO::Core
|
|
112
121
|
end
|
113
122
|
|
114
123
|
def run_callbacks
|
115
|
-
|
124
|
+
# prevent 'add new callbacks' during callback call, new callbacks will run in next turn
|
125
|
+
callbacks = @callbacks
|
126
|
+
@callbacks = []
|
127
|
+
while (callback = callbacks.shift)
|
116
128
|
callback.call
|
117
129
|
end
|
118
130
|
end
|
data/lib/lightio/core/beam.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module LightIO::Core
|
2
4
|
# Beam is light-weight executor, provide thread-like interface
|
3
5
|
#
|
@@ -20,6 +22,9 @@ module LightIO::Core
|
|
20
22
|
# special class for simulate Thread#raise for Beam
|
21
23
|
class BeamError
|
22
24
|
attr_reader :error, :parent
|
25
|
+
extend Forwardable
|
26
|
+
|
27
|
+
def_delegators :@error, :message, :backtrace
|
23
28
|
|
24
29
|
def initialize(error)
|
25
30
|
@error = error
|
@@ -27,6 +32,9 @@ module LightIO::Core
|
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
35
|
+
attr_reader :error
|
36
|
+
attr_accessor :on_dead
|
37
|
+
|
30
38
|
# Create a new beam
|
31
39
|
#
|
32
40
|
# Beam is light-weight executor, provide thread-like interface
|
@@ -72,9 +80,9 @@ module LightIO::Core
|
|
72
80
|
#
|
73
81
|
# @param [Numeric] limit wait limit seconds if limit > 0, return nil if beam still alive, else return beam self
|
74
82
|
# @return [Beam, nil]
|
75
|
-
def join(limit=
|
83
|
+
def join(limit=nil)
|
76
84
|
# try directly get result
|
77
|
-
if !alive? || limit <= 0
|
85
|
+
if !alive? || limit.nil? || limit <= 0
|
78
86
|
# call value to raise error
|
79
87
|
value
|
80
88
|
return self
|
@@ -106,10 +114,18 @@ module LightIO::Core
|
|
106
114
|
|
107
115
|
# Fiber not provide raise method, so we have to simulate one
|
108
116
|
# @param [BeamError] error currently only support raise BeamError
|
109
|
-
def raise(error)
|
110
|
-
|
117
|
+
def raise(error, message=nil, backtrace=nil)
|
118
|
+
unless error.is_a?(BeamError)
|
119
|
+
message ||= error.respond_to?(:message) ? error.message : nil
|
120
|
+
backtrace ||= error.respond_to?(:backtrace) ? error.backtrace : nil
|
121
|
+
super(error, message, backtrace)
|
122
|
+
end
|
111
123
|
self.parent = error.parent if error.parent
|
112
|
-
|
124
|
+
if Beam.current == self
|
125
|
+
raise(error.error, message, backtrace)
|
126
|
+
else
|
127
|
+
@error ||= error
|
128
|
+
end
|
113
129
|
end
|
114
130
|
|
115
131
|
class << self
|
@@ -130,6 +146,7 @@ module LightIO::Core
|
|
130
146
|
# mark beam as dead
|
131
147
|
def dead
|
132
148
|
@alive = false
|
149
|
+
on_dead.call(self) if on_dead
|
133
150
|
end
|
134
151
|
|
135
152
|
# Beam transfer back to parent after schedule
|
data/lib/lightio/core/ioloop.rb
CHANGED
@@ -1,36 +1,21 @@
|
|
1
1
|
require 'lightio/core/backend/nio'
|
2
|
+
require 'forwardable'
|
3
|
+
|
2
4
|
module LightIO::Core
|
3
5
|
# IOloop like a per-threaded EventMachine (cause fiber cannot resume cross threads)
|
4
6
|
#
|
5
7
|
# IOloop handle io waiting and schedule beams, user do not supposed to directly use this class
|
6
8
|
class IOloop
|
7
9
|
|
10
|
+
RAW_THREAD = ::Thread
|
11
|
+
|
8
12
|
def initialize
|
9
13
|
@fiber = Fiber.new {run}
|
10
14
|
@backend = Backend::NIO.new
|
11
15
|
end
|
12
16
|
|
13
|
-
|
14
|
-
|
15
|
-
# start io loop and never return...
|
16
|
-
@backend.run
|
17
|
-
end
|
18
|
-
|
19
|
-
def add_timer(timer)
|
20
|
-
@backend.add_timer(timer)
|
21
|
-
end
|
22
|
-
|
23
|
-
def add_callback(&blk)
|
24
|
-
@backend.add_callback(&blk)
|
25
|
-
end
|
26
|
-
|
27
|
-
def add_io_wait(io, interests, &blk)
|
28
|
-
@backend.add_io_wait(io, interests, &blk)
|
29
|
-
end
|
30
|
-
|
31
|
-
def cancel_io_wait(io)
|
32
|
-
@backend.cancel_io_wait(io)
|
33
|
-
end
|
17
|
+
extend Forwardable
|
18
|
+
def_delegators :@backend, :run, :add_timer, :add_callback, :add_io_wait, :cancel_io_wait, :backend
|
34
19
|
|
35
20
|
# Wait a watcher, watcher can be a timer or socket.
|
36
21
|
# see LightIO::Watchers module for detail
|
@@ -44,15 +29,18 @@ module LightIO::Core
|
|
44
29
|
# wait until watcher is ok
|
45
30
|
# then do work
|
46
31
|
response_id, err = future.value
|
32
|
+
current_beam = LightIO::Core::Beam.current
|
47
33
|
if response_id != id
|
48
|
-
raise InvalidTransferError, "expect #{id}, but get #{
|
34
|
+
raise LightIO::InvalidTransferError, "expect #{id}, but get #{response_id}"
|
49
35
|
elsif err
|
50
36
|
# if future return a err
|
51
37
|
# simulate Thread#raise to Beam , that we can shutdown beam blocking by socket accepting
|
52
38
|
# transfer back to which beam occur this err
|
53
39
|
# not sure this is a right way to do it
|
54
|
-
LightIO::Core::Beam
|
40
|
+
current_beam.raise(err) if current_beam.is_a?(LightIO::Core::Beam)
|
55
41
|
end
|
42
|
+
# check beam error after wait
|
43
|
+
current_beam.send(:check_and_raise_error) if current_beam.is_a?(LightIO::Core::Beam)
|
56
44
|
end
|
57
45
|
|
58
46
|
def transfer
|
@@ -63,10 +51,10 @@ module LightIO::Core
|
|
63
51
|
# return current ioloop or create new one
|
64
52
|
def current
|
65
53
|
key = :"lightio.ioloop"
|
66
|
-
unless
|
67
|
-
|
54
|
+
unless RAW_THREAD.current.thread_variable?(key)
|
55
|
+
RAW_THREAD.current.thread_variable_set(key, IOloop.new)
|
68
56
|
end
|
69
|
-
|
57
|
+
RAW_THREAD.current.thread_variable_get(key)
|
70
58
|
end
|
71
59
|
end
|
72
60
|
end
|
@@ -6,10 +6,24 @@ module LightIO::Core
|
|
6
6
|
# SHOULD NOT BE USED DIRECTLY
|
7
7
|
class LightFiber < Fiber
|
8
8
|
attr_reader :ioloop
|
9
|
+
attr_accessor :on_transfer
|
10
|
+
|
11
|
+
ROOT_FIBER = Fiber.current
|
9
12
|
|
10
13
|
def initialize(ioloop: IOloop.current, &blk)
|
11
14
|
@ioloop = ioloop
|
12
15
|
super(&blk)
|
13
16
|
end
|
17
|
+
|
18
|
+
def transfer
|
19
|
+
on_transfer.call(LightFiber.current, self) if on_transfer
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
class << self
|
24
|
+
def is_root?(fiber)
|
25
|
+
ROOT_FIBER == fiber
|
26
|
+
end
|
27
|
+
end
|
14
28
|
end
|
15
29
|
end
|
data/lib/lightio/library/io.rb
CHANGED
@@ -4,10 +4,7 @@ module LightIO::Library
|
|
4
4
|
include LightIO::Wrap::IOWrapper
|
5
5
|
wrap ::IO
|
6
6
|
|
7
|
-
|
8
|
-
def_delegators :@io_watcher, :wait, :wait_readable, :wait_writable
|
9
|
-
|
10
|
-
wrap_blocking_methods :read, :write, exception_symbol: false
|
7
|
+
wrap_blocking_methods :read, :write
|
11
8
|
|
12
9
|
alias_method :<<, :write
|
13
10
|
|
@@ -16,23 +13,88 @@ module LightIO::Library
|
|
16
13
|
(outbuf ||= "").clear
|
17
14
|
loop do
|
18
15
|
readlen = length.nil? ? 4096 : length - outbuf.size
|
19
|
-
|
20
|
-
outbuf <<
|
16
|
+
if (data = wait_nonblock(:read_nonblock, readlen))
|
17
|
+
outbuf << data
|
21
18
|
if length == outbuf.size
|
22
19
|
return outbuf
|
23
20
|
end
|
24
|
-
|
25
|
-
return
|
21
|
+
else
|
22
|
+
return length.nil? ? '' : nil
|
26
23
|
end
|
27
24
|
end
|
28
25
|
end
|
29
26
|
|
30
27
|
def readpartial(maxlen, outbuf=nil)
|
31
28
|
(outbuf ||= "").clear
|
32
|
-
|
29
|
+
if (data = wait_nonblock(:read_nonblock, maxlen))
|
30
|
+
outbuf << data
|
31
|
+
else
|
32
|
+
raise EOFError, 'end of file reached'
|
33
|
+
end
|
33
34
|
outbuf
|
34
35
|
end
|
35
36
|
|
37
|
+
def getbyte
|
38
|
+
read(1)
|
39
|
+
end
|
40
|
+
|
41
|
+
def getc
|
42
|
+
wait_readable
|
43
|
+
@io.getc
|
44
|
+
end
|
45
|
+
|
46
|
+
def readline(*args)
|
47
|
+
line = gets(*args)
|
48
|
+
raise EOFError, 'end of file reached' if line.nil?
|
49
|
+
line
|
50
|
+
end
|
51
|
+
|
52
|
+
def readlines(*args)
|
53
|
+
result = []
|
54
|
+
until eof?
|
55
|
+
result << readline(*args)
|
56
|
+
end
|
57
|
+
result
|
58
|
+
end
|
59
|
+
|
60
|
+
def readchar
|
61
|
+
c = getc
|
62
|
+
raise EOFError, 'end of file reached' if c.nil?
|
63
|
+
c
|
64
|
+
end
|
65
|
+
|
66
|
+
def readbyte
|
67
|
+
b = getbyte
|
68
|
+
raise EOFError, 'end of file reached' if b.nil?
|
69
|
+
b
|
70
|
+
end
|
71
|
+
|
72
|
+
def eof
|
73
|
+
wait_readable
|
74
|
+
@io.eof?
|
75
|
+
end
|
76
|
+
|
77
|
+
alias eof? eof
|
78
|
+
|
79
|
+
def gets(*args)
|
80
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..2)" if args.size > 2
|
81
|
+
return nil if eof?
|
82
|
+
sep = $/
|
83
|
+
if args[0].is_a?(Numeric)
|
84
|
+
limit = args.shift
|
85
|
+
else
|
86
|
+
sep = args.shift if args.size > 0
|
87
|
+
limit = args.shift if args.first.is_a?(Numeric)
|
88
|
+
end
|
89
|
+
s = ''
|
90
|
+
while (c = getc)
|
91
|
+
s << c
|
92
|
+
break if limit && s.size == limit
|
93
|
+
break if c == sep
|
94
|
+
end
|
95
|
+
$_ = s
|
96
|
+
end
|
97
|
+
|
36
98
|
def close(*args)
|
37
99
|
# close watcher before io closed
|
38
100
|
@io_watcher.close
|
@@ -43,6 +105,19 @@ module LightIO::Library
|
|
43
105
|
self
|
44
106
|
end
|
45
107
|
|
108
|
+
private
|
109
|
+
def wait_readable
|
110
|
+
# if IO is already readable, continue wait_readable may block it forever
|
111
|
+
# so use getbyte detect this situation
|
112
|
+
# Maybe move getc and gets to thread pool is a good idea
|
113
|
+
b = getbyte
|
114
|
+
if b
|
115
|
+
ungetbyte(b)
|
116
|
+
return
|
117
|
+
end
|
118
|
+
@io_watcher.wait_readable
|
119
|
+
end
|
120
|
+
|
46
121
|
class << self
|
47
122
|
def open(*args)
|
48
123
|
io = self.new(*args)
|
@@ -65,7 +140,7 @@ module LightIO::Library
|
|
65
140
|
end
|
66
141
|
|
67
142
|
def select(read_fds, write_fds=nil, _except_fds=nil, timeout=nil)
|
68
|
-
timer =
|
143
|
+
timer = timeout && Time.now
|
69
144
|
# run once ioloop
|
70
145
|
LightIO.sleep 0
|
71
146
|
loop do
|
@@ -73,10 +148,8 @@ module LightIO::Library
|
|
73
148
|
w_fds = (write_fds || []).select {|fd| fd.closed? ? raise(IOError, 'closed stream') : fd.instance_variable_get(:@io_watcher).writable?}
|
74
149
|
e_fds = []
|
75
150
|
if r_fds.empty? && w_fds.empty?
|
76
|
-
|
77
|
-
|
78
|
-
timer += interval
|
79
|
-
if timeout && timer > timeout
|
151
|
+
LightIO.sleep 0
|
152
|
+
if timeout && Time.now - timer > timeout
|
80
153
|
return nil
|
81
154
|
end
|
82
155
|
else
|
@@ -7,8 +7,8 @@ module LightIO::Library
|
|
7
7
|
LightIO::IOloop.current.transfer
|
8
8
|
end
|
9
9
|
duration = duration[0]
|
10
|
-
if duration.zero?
|
11
|
-
LightIO::Beam.
|
10
|
+
if duration.zero?
|
11
|
+
LightIO::Beam.pass
|
12
12
|
return
|
13
13
|
end
|
14
14
|
timer = LightIO::Watchers::Timer.new duration
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require_relative 'queue'
|
2
|
+
|
3
|
+
module LightIO::Library
|
4
|
+
class Thread
|
5
|
+
class Mutex
|
6
|
+
def initialize
|
7
|
+
@queue = Queue.new
|
8
|
+
@queue << true
|
9
|
+
@locked_thread = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def lock
|
13
|
+
raise ThreadError, "deadlock; recursive locking" if owner?
|
14
|
+
@queue.pop
|
15
|
+
@locked_thread = LightIO::Thread.current
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def unlock
|
20
|
+
raise ThreadError, "Attempt to unlock a mutex which is not locked" unless owner?
|
21
|
+
@locked_thread = nil
|
22
|
+
@queue << true
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def locked?
|
27
|
+
!@locked_thread.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
def owner?
|
31
|
+
@locked_thread == LightIO::Thread.current
|
32
|
+
end
|
33
|
+
|
34
|
+
def sleep(timeout=nil)
|
35
|
+
unlock
|
36
|
+
LightIO.sleep(timeout)
|
37
|
+
lock
|
38
|
+
end
|
39
|
+
|
40
|
+
def synchronize
|
41
|
+
raise ThreadError, 'must be called with a block' unless block_given?
|
42
|
+
lock
|
43
|
+
begin
|
44
|
+
yield
|
45
|
+
ensure
|
46
|
+
unlock
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def try_lock
|
51
|
+
if @locked_thread.nil?
|
52
|
+
lock
|
53
|
+
true
|
54
|
+
else
|
55
|
+
false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Mutex = Thread::Mutex
|
62
|
+
end
|
@@ -27,10 +27,10 @@ module LightIO::Library
|
|
27
27
|
# Pushes the given +object+ to the queue.
|
28
28
|
def push(object)
|
29
29
|
raise ClosedQueueError, "queue closed" if @close
|
30
|
-
if @waiters.
|
30
|
+
if (waiter = @waiters.shift)
|
31
31
|
future = LightIO::Future.new
|
32
32
|
LightIO::IOloop.current.add_callback {
|
33
|
-
|
33
|
+
waiter.transfer(object)
|
34
34
|
future.transfer
|
35
35
|
}
|
36
36
|
future.value
|