rbgo 0.2.0 → 0.2.5
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/README.md +92 -0
- data/lib/rbgo/actor.rb +1 -3
- data/lib/rbgo/corun.rb +88 -12
- data/lib/rbgo/io_machine.rb +266 -0
- data/lib/rbgo/select_chan.rb +3 -2
- data/lib/rbgo/version.rb +1 -1
- data/lib/rbgo.rb +1 -0
- data/rbgo.gemspec +1 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d77329adc2e8773f5ffb22ef40da51ad2f4451f2032804e684a4e7287df6c387
|
4
|
+
data.tar.gz: dd92c1cd5c9ec8f0fa307cc1d8ae6731d51cb70e39d02fe9641aba0dd8d598f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b5519621d6a4c7f2354037157901a86d2a060ca71d459ca9ac42f692e6c9f8dab32138dfeb571ea3a562f640f4118bcbaa720a79175724200259a1cb51a8ab78
|
7
|
+
data.tar.gz: e41d09d68fbe4d61222408d6899625627edebf56f2141b6c13a1e5e45a4ad181149520361b8a27a7f236b283875e8d0042dc989a059511e205c0e7814f9e5f00
|
data/README.md
CHANGED
@@ -130,19 +130,111 @@ actor = Rbgo::Actor.new do|msg, actor|
|
|
130
130
|
actor.send_msg :msg1 #won't block
|
131
131
|
|
132
132
|
```
|
133
|
+
# IOMachine
|
134
|
+
IOMachine wrap nio4r to do IO operation asynchronously.
|
135
|
+
|
136
|
+
support platforms: MRI, JRuby.
|
137
|
+
|
138
|
+
*Not supported platform: Truffleruby*
|
139
|
+
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
require 'rbgo'
|
143
|
+
|
144
|
+
io_r, io_w = IO.pipe
|
145
|
+
machine = Rbgo::IOMachine.new
|
146
|
+
|
147
|
+
receipt1 = machine.do_read(io_r, length: 100) # when length > 0, result nil if have not read anything yet when read complete.
|
148
|
+
receipt2 = machine.do_read(io_r, length: 100) # when length >0, result data bytes length up to 100 if have read some when read complete.
|
149
|
+
receipt3 = machine.do_read(io_r, length: 0) # when length == 0, result ""
|
150
|
+
receipt4 = machine.do_read(io_r, length: nil) # when length == nil, read until EOF. return "" if have not read anything.
|
151
|
+
|
152
|
+
io_w.write("a"*100)
|
153
|
+
io_w.write("b"*100)
|
154
|
+
io_w.write("c"*100)
|
155
|
+
io_w.close # cause EOF
|
156
|
+
|
157
|
+
# if the same io object, and the same read/write operation, operations will complete in sequence.
|
158
|
+
# so receipt1 complete first, and the receipt2 ...
|
159
|
+
# if the same io object, but not the same read/write operation,
|
160
|
+
# or not the same io object, operations will complete in arbitrary order.
|
161
|
+
receipt1.wait
|
162
|
+
p receipt1.res # aaa...
|
163
|
+
receipt2.wait
|
164
|
+
p receipt2.res # bbb...
|
165
|
+
receipt3.wait
|
166
|
+
p receipt3.res # ""
|
167
|
+
receipt4.wait
|
168
|
+
p receipt4.res # ccc...
|
169
|
+
|
170
|
+
io_r, io_w = IO.pipe
|
171
|
+
receipt1 = machine.do_write(io_w, str: "hello world!")
|
172
|
+
|
173
|
+
receipt1.wait
|
174
|
+
p receipt1.res # number of bytes written, may be less than str.bytesize if exception raised
|
175
|
+
|
176
|
+
```
|
177
|
+
|
178
|
+
# yield_read / yield_write
|
179
|
+
use IOMachine to do IO operation asynchronously, but write code in a sequential way.
|
180
|
+
|
181
|
+
No callback and another callback...
|
182
|
+
```ruby
|
183
|
+
require 'rbgo'
|
184
|
+
using Rbgo::CoRunExtensions
|
185
|
+
|
186
|
+
io_r, io_w = IO.pipe
|
187
|
+
go do
|
188
|
+
data = io_r.yield_read(100) # have blocking semantics, but execute asynchronously
|
189
|
+
# this operation will *NOT* block the current thread
|
190
|
+
# when io operation complete, thread will resume to execute from this point.
|
191
|
+
p data
|
192
|
+
end
|
193
|
+
|
194
|
+
sleep 2
|
195
|
+
|
196
|
+
io_w.write("haha I'm crazy.")
|
197
|
+
io_w.close
|
198
|
+
|
199
|
+
# NOTICE yield_read / yield_write will do yield only in CoRun::Routine.new(*args, new_thread: false, &blk) block
|
200
|
+
# in other place yield_read / yield_write will do normal IO#read / IO#write
|
201
|
+
# for example:
|
202
|
+
# go do
|
203
|
+
# io_r.yield_read # will do yield
|
204
|
+
# end
|
205
|
+
#
|
206
|
+
# go do
|
207
|
+
# fiber = Fiber.new do
|
208
|
+
# io_r.yield_read # will not do yield. just do normal IO#read
|
209
|
+
# end
|
210
|
+
# fiber.resume
|
211
|
+
# end
|
212
|
+
#
|
213
|
+
# go! do
|
214
|
+
# io_r.yield_read # will not do yield. just do normal IO#read
|
215
|
+
# end
|
216
|
+
|
217
|
+
```
|
218
|
+
|
219
|
+
|
133
220
|
# NetworkService
|
134
221
|
|
135
222
|
open TCP or UDP service
|
136
223
|
|
137
224
|
Because service handles network request in async mode, it can handle many requests concurrently. If use some Non-GIL ruby implementations such as TruffleRuby or JRuby, it can utilize all your CPU cores.
|
138
225
|
|
226
|
+
Use IO#yield_read IO#yield_write to do IO operations
|
139
227
|
|
140
228
|
```ruby
|
141
229
|
require 'rbgo'
|
142
230
|
|
231
|
+
using Rbgo::CoRunExtensions
|
232
|
+
|
143
233
|
#localhost, port 3000
|
144
234
|
tcp_service = Rbgo::NetworkServiceFactory.open_tcp_service(3000) do|sock, clientAddrInfo|
|
145
235
|
p [sock, clientAddrInfo]
|
236
|
+
p sock.yield_read
|
237
|
+
sock.yield_write("hello")
|
146
238
|
sock.close #SHOULD CLOSE SOCK MANUALLY since version 0.2.0
|
147
239
|
end
|
148
240
|
|
data/lib/rbgo/actor.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
require 'thread'
|
2
|
-
require_relative 'corun'
|
3
2
|
|
4
|
-
using Rbgo::CoRunExtensions
|
5
3
|
|
6
4
|
module Rbgo
|
7
5
|
class Actor
|
@@ -37,7 +35,7 @@ module Rbgo
|
|
37
35
|
private
|
38
36
|
|
39
37
|
def start_msg_loop
|
40
|
-
|
38
|
+
CoRun::Routine.new(new_thread: true) do
|
41
39
|
while msg = mail_box.deq
|
42
40
|
handler.call(msg, self) rescue nil
|
43
41
|
end
|
data/lib/rbgo/corun.rb
CHANGED
@@ -2,9 +2,35 @@ require 'thread'
|
|
2
2
|
require 'fiber'
|
3
3
|
require 'system'
|
4
4
|
require 'singleton'
|
5
|
+
require_relative 'io_machine'
|
5
6
|
|
6
7
|
module Rbgo
|
7
8
|
module CoRun
|
9
|
+
IS_CORUN_FIBER = :is_corun_fiber_bbc0f70e
|
10
|
+
YIELD_IO_OPERATION = :yield_bbc0f70e
|
11
|
+
|
12
|
+
def self.is_in_corun_fiber?
|
13
|
+
!!Thread.current[IS_CORUN_FIBER]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.read_from(io, length: nil)
|
17
|
+
if is_in_corun_fiber?
|
18
|
+
return "" if length == 0
|
19
|
+
receipt = Scheduler.instance.io_machine.do_read(io, length: length)
|
20
|
+
Fiber.yield [YIELD_IO_OPERATION, receipt]
|
21
|
+
else
|
22
|
+
io.read(length)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.write_to(io, str:)
|
27
|
+
if is_in_corun_fiber?
|
28
|
+
receipt = Scheduler.instance.io_machine.do_write(io, str: str)
|
29
|
+
Fiber.yield [YIELD_IO_OPERATION, receipt]
|
30
|
+
else
|
31
|
+
io.write(str)
|
32
|
+
end
|
33
|
+
end
|
8
34
|
|
9
35
|
class Routine
|
10
36
|
attr_accessor :error
|
@@ -16,7 +42,7 @@ module Rbgo
|
|
16
42
|
|
17
43
|
private
|
18
44
|
|
19
|
-
attr_accessor :args, :blk, :fiber
|
45
|
+
attr_accessor :args, :blk, :fiber, :io_receipt
|
20
46
|
|
21
47
|
def initialize(*args, new_thread: false, &blk)
|
22
48
|
self.args = args
|
@@ -33,6 +59,7 @@ module Rbgo
|
|
33
59
|
end
|
34
60
|
rescue Exception => ex
|
35
61
|
self.error = ex
|
62
|
+
STDERR.puts ex
|
36
63
|
end
|
37
64
|
end
|
38
65
|
|
@@ -43,11 +70,28 @@ module Rbgo
|
|
43
70
|
|
44
71
|
def perform
|
45
72
|
self.fiber = Fiber.new do |args|
|
73
|
+
Thread.current[IS_CORUN_FIBER] = true
|
46
74
|
blk.call(*args)
|
47
75
|
end if fiber.nil?
|
48
76
|
|
49
77
|
if fiber.alive?
|
50
|
-
|
78
|
+
if io_receipt.nil?
|
79
|
+
obj = fiber.resume(*args)
|
80
|
+
else
|
81
|
+
if io_receipt.done_flag
|
82
|
+
obj = fiber.resume(io_receipt.res)
|
83
|
+
else
|
84
|
+
return nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
if obj.is_a?(Array) &&
|
88
|
+
obj.size == 2 &&
|
89
|
+
obj[0] == YIELD_IO_OPERATION &&
|
90
|
+
obj[1].is_a?(IOReceipt)
|
91
|
+
self.io_receipt = obj[1]
|
92
|
+
else
|
93
|
+
self.io_receipt = nil
|
94
|
+
end
|
51
95
|
end
|
52
96
|
nil
|
53
97
|
end
|
@@ -56,7 +100,7 @@ module Rbgo
|
|
56
100
|
class Scheduler
|
57
101
|
|
58
102
|
include Singleton
|
59
|
-
attr_accessor :num_thread, :check_interval
|
103
|
+
attr_accessor :num_thread, :check_interval, :io_machine
|
60
104
|
|
61
105
|
private
|
62
106
|
|
@@ -66,7 +110,7 @@ module Rbgo
|
|
66
110
|
attr_accessor :supervisor_thread
|
67
111
|
|
68
112
|
def initialize
|
69
|
-
self.num_thread
|
113
|
+
self.num_thread = System::CPU.count rescue 8
|
70
114
|
self.thread_pool = []
|
71
115
|
|
72
116
|
self.msg_queue = Queue.new
|
@@ -74,6 +118,8 @@ module Rbgo
|
|
74
118
|
|
75
119
|
self.check_interval = 0.1
|
76
120
|
|
121
|
+
self.io_machine = IOMachine.new
|
122
|
+
|
77
123
|
msg_queue << :init
|
78
124
|
create_supervisor_thread
|
79
125
|
generate_check_msg
|
@@ -85,22 +131,38 @@ module Rbgo
|
|
85
131
|
thread_pool << Thread.new do
|
86
132
|
Thread.current.report_on_exception = false
|
87
133
|
begin
|
88
|
-
should_exit
|
89
|
-
yield_task_queue
|
90
|
-
|
134
|
+
should_exit = false
|
135
|
+
yield_task_queue = Queue.new
|
136
|
+
pending_io_task_queue = Queue.new
|
137
|
+
local_task_queue = Queue.new
|
91
138
|
loop do
|
92
139
|
task = nil
|
93
140
|
if local_task_queue.empty?
|
94
141
|
task = task_queue.deq(true) rescue nil
|
95
142
|
if task.nil?
|
96
143
|
task = yield_task_queue.deq unless yield_task_queue.empty?
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
144
|
+
if task.nil?
|
145
|
+
task = pending_io_task_queue.deq unless pending_io_task_queue.empty?
|
146
|
+
if task.nil?
|
147
|
+
task = task_queue.deq
|
148
|
+
else
|
149
|
+
sleep 0.1 # only pending io tasks in queue
|
150
|
+
end
|
151
|
+
else
|
152
|
+
receipt = task.send(:io_receipt)
|
153
|
+
if receipt
|
154
|
+
pending_io_task_queue << task unless receipt.done_flag
|
155
|
+
next
|
156
|
+
end
|
157
|
+
end
|
102
158
|
end
|
159
|
+
|
160
|
+
local_task_queue << task
|
161
|
+
local_task_queue << yield_task_queue.deq unless yield_task_queue.empty?
|
162
|
+
local_task_queue << pending_io_task_queue.deq unless pending_io_task_queue.empty?
|
163
|
+
|
103
164
|
end
|
165
|
+
|
104
166
|
task = local_task_queue.deq
|
105
167
|
|
106
168
|
begin
|
@@ -108,6 +170,7 @@ module Rbgo
|
|
108
170
|
task.send :perform
|
109
171
|
rescue Exception => ex
|
110
172
|
task.error = ex
|
173
|
+
STDERR.puts(ex)
|
111
174
|
next
|
112
175
|
ensure
|
113
176
|
Thread.current.thread_variable_set(:performing, false)
|
@@ -119,6 +182,7 @@ module Rbgo
|
|
119
182
|
|
120
183
|
should_exit = Thread.current.thread_variable_get(:should_exit) &&
|
121
184
|
yield_task_queue.empty? &&
|
185
|
+
pending_io_task_queue.empty? &&
|
122
186
|
local_task_queue.empty?
|
123
187
|
break if should_exit
|
124
188
|
end
|
@@ -202,6 +266,8 @@ module Rbgo
|
|
202
266
|
end
|
203
267
|
end
|
204
268
|
|
269
|
+
CoRun.freeze
|
270
|
+
|
205
271
|
module CoRunExtensions
|
206
272
|
refine Object do
|
207
273
|
def go(*args, &blk)
|
@@ -212,5 +278,15 @@ module Rbgo
|
|
212
278
|
CoRun::Routine.new(*args, new_thread: true, &blk)
|
213
279
|
end
|
214
280
|
end
|
281
|
+
|
282
|
+
refine IO do
|
283
|
+
def yield_read(len = nil)
|
284
|
+
CoRun.read_from(self, length: len)
|
285
|
+
end
|
286
|
+
|
287
|
+
def yield_write(str)
|
288
|
+
CoRun.write_to(self, str: str)
|
289
|
+
end
|
290
|
+
end
|
215
291
|
end
|
216
292
|
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
require 'nio'
|
2
|
+
require_relative 'actor'
|
3
|
+
|
4
|
+
module Rbgo
|
5
|
+
class IOReceipt
|
6
|
+
attr_reader :registered_op, :done_flag
|
7
|
+
attr_accessor :res
|
8
|
+
|
9
|
+
def wait
|
10
|
+
mutex.synchronize do
|
11
|
+
until done_flag
|
12
|
+
cond.wait(mutex)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def notify
|
19
|
+
mutex.synchronize do
|
20
|
+
self.done_flag = true
|
21
|
+
cond.signal
|
22
|
+
end
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_accessor :mutex, :cond
|
29
|
+
attr_writer :registered_op, :done_flag
|
30
|
+
|
31
|
+
def initialize(op)
|
32
|
+
self.done_flag = false
|
33
|
+
self.mutex = Mutex.new
|
34
|
+
self.cond = ConditionVariable.new
|
35
|
+
self.registered_op = op
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
class IOMachine
|
41
|
+
def do_read(io, length: nil)
|
42
|
+
op = [:register_read, io, length]
|
43
|
+
receipt = IOReceipt.new(op)
|
44
|
+
actor.send_msg(receipt)
|
45
|
+
receipt
|
46
|
+
end
|
47
|
+
|
48
|
+
def do_write(io, str:)
|
49
|
+
op = [:register_write, io, str]
|
50
|
+
receipt = IOReceipt.new(op)
|
51
|
+
actor.send_msg(receipt)
|
52
|
+
receipt
|
53
|
+
end
|
54
|
+
|
55
|
+
def close
|
56
|
+
actor.close
|
57
|
+
selector.close
|
58
|
+
end
|
59
|
+
|
60
|
+
def closed?
|
61
|
+
actor.closed? && selector.closed?
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
attr_accessor :selector, :actor, :monitors
|
67
|
+
|
68
|
+
def initialize
|
69
|
+
self.selector = NIO::Selector.new
|
70
|
+
self.monitors = {}
|
71
|
+
|
72
|
+
self.actor = Actor.new do |msg, actor|
|
73
|
+
if msg == :do_select
|
74
|
+
handle_select_msg(actor)
|
75
|
+
next
|
76
|
+
end
|
77
|
+
|
78
|
+
receipt = msg
|
79
|
+
op = receipt.registered_op
|
80
|
+
|
81
|
+
case
|
82
|
+
when param_pattern_match([:register_read, IO, Integer], op)
|
83
|
+
handle_read_msg(receipt, actor)
|
84
|
+
when param_pattern_match([:register_read, IO, nil], op)
|
85
|
+
handle_read_msg(receipt, actor)
|
86
|
+
when param_pattern_match([:register_write, IO, String], op)
|
87
|
+
handle_write_msg(receipt, actor)
|
88
|
+
end
|
89
|
+
end #end of actor
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
#end of initialize
|
94
|
+
|
95
|
+
|
96
|
+
def handle_select_msg(actor)
|
97
|
+
return if selector.empty?
|
98
|
+
selector.select(0.1) do |monitor|
|
99
|
+
if monitor.readiness == :r
|
100
|
+
monitor.value[0].call
|
101
|
+
elsif monitor.readiness == :w
|
102
|
+
monitor.value[1].call
|
103
|
+
else
|
104
|
+
monitor.value[0].call
|
105
|
+
monitor.value[1].call
|
106
|
+
end
|
107
|
+
end
|
108
|
+
return if selector.empty?
|
109
|
+
actor.send_msg :do_select
|
110
|
+
nil
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
def handle_read_msg(receipt, actor)
|
115
|
+
op = receipt.registered_op
|
116
|
+
io = op[1]
|
117
|
+
len = op[2]
|
118
|
+
res = StringIO.new
|
119
|
+
buf_size = 1024 * 512
|
120
|
+
registered_monitor = monitors[io]
|
121
|
+
if registered_monitor && (registered_monitor.interests == :r || registered_monitor.interests == :rw)
|
122
|
+
actor.send_msg receipt
|
123
|
+
return
|
124
|
+
end
|
125
|
+
|
126
|
+
if registered_monitor
|
127
|
+
registered_monitor.add_interest(:r)
|
128
|
+
monitor = registered_monitor
|
129
|
+
else
|
130
|
+
monitor = selector.register(io, :r)
|
131
|
+
monitors[io] = monitor
|
132
|
+
end
|
133
|
+
|
134
|
+
monitor.value ||= []
|
135
|
+
monitor.value[0] = proc do
|
136
|
+
if len.nil?
|
137
|
+
notify_blk = proc do
|
138
|
+
monitors.delete(monitor.io)
|
139
|
+
monitor.close
|
140
|
+
receipt.res = res.string
|
141
|
+
receipt.notify
|
142
|
+
end
|
143
|
+
loop do
|
144
|
+
begin
|
145
|
+
buf = io.read_nonblock(buf_size, exception: false)
|
146
|
+
rescue Exception => ex
|
147
|
+
notify_blk.call
|
148
|
+
STDERR.puts ex
|
149
|
+
break
|
150
|
+
end
|
151
|
+
if buf == :wait_readable
|
152
|
+
break
|
153
|
+
elsif buf.nil?
|
154
|
+
notify_blk.call
|
155
|
+
break
|
156
|
+
end
|
157
|
+
res << buf
|
158
|
+
end
|
159
|
+
elsif len == 0
|
160
|
+
monitors.delete(monitor.io)
|
161
|
+
monitor.close
|
162
|
+
receipt.res = ""
|
163
|
+
receipt.notify
|
164
|
+
break
|
165
|
+
else
|
166
|
+
notify_blk = proc do
|
167
|
+
monitors.delete(monitor.io)
|
168
|
+
monitor.close
|
169
|
+
if res.string.length == 0
|
170
|
+
receipt.res = nil
|
171
|
+
else
|
172
|
+
receipt.res = res.string
|
173
|
+
end
|
174
|
+
receipt.notify
|
175
|
+
end
|
176
|
+
|
177
|
+
bytes_read_n = 0
|
178
|
+
loop do
|
179
|
+
need_read_bytes_n = len - bytes_read_n
|
180
|
+
if need_read_bytes_n <= 0
|
181
|
+
notify_blk.call
|
182
|
+
break
|
183
|
+
end
|
184
|
+
begin
|
185
|
+
buf = io.read_nonblock(need_read_bytes_n, exception: false)
|
186
|
+
rescue Exception => ex
|
187
|
+
notify_blk.call
|
188
|
+
STDERR.puts ex
|
189
|
+
break
|
190
|
+
end
|
191
|
+
if buf == :wait_readable
|
192
|
+
break
|
193
|
+
elsif buf.nil?
|
194
|
+
notify_blk.call
|
195
|
+
break
|
196
|
+
end
|
197
|
+
res << buf
|
198
|
+
bytes_read_n += buf.bytesize
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
actor.send_msg :do_select
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
def handle_write_msg(receipt, actor)
|
207
|
+
op = receipt.registered_op
|
208
|
+
io = op[1]
|
209
|
+
str = op[2].to_s
|
210
|
+
|
211
|
+
registered_monitor = monitors[io]
|
212
|
+
if registered_monitor && (registered_monitor.interests == :w || registered_monitor.interests == :rw)
|
213
|
+
actor.send_msg receipt
|
214
|
+
return
|
215
|
+
end
|
216
|
+
|
217
|
+
if registered_monitor
|
218
|
+
registered_monitor.add_interest(:w)
|
219
|
+
monitor = registered_monitor
|
220
|
+
else
|
221
|
+
monitor = selector.register(io, :w)
|
222
|
+
monitors[io] = monitor
|
223
|
+
end
|
224
|
+
|
225
|
+
buf = NIO::ByteBuffer.new(str.bytesize)
|
226
|
+
buf << str
|
227
|
+
buf.flip
|
228
|
+
bytes_written_n = 0
|
229
|
+
monitor.value ||= []
|
230
|
+
monitor.value[1] = proc do
|
231
|
+
begin
|
232
|
+
if buf.remaining > 0
|
233
|
+
n = buf.write_to(io)
|
234
|
+
bytes_written_n += n
|
235
|
+
else
|
236
|
+
monitors.delete(monitor.io)
|
237
|
+
monitor.close
|
238
|
+
receipt.res = str.bytesize
|
239
|
+
receipt.notify
|
240
|
+
end
|
241
|
+
rescue Exception => ex
|
242
|
+
monitors.delete(monitor.io)
|
243
|
+
monitor.close
|
244
|
+
receipt.res = bytes_written_n
|
245
|
+
receipt.notify
|
246
|
+
STDERR.puts ex
|
247
|
+
end
|
248
|
+
end
|
249
|
+
actor.send_msg :do_select
|
250
|
+
end
|
251
|
+
|
252
|
+
|
253
|
+
def param_pattern_match(pattern, params)
|
254
|
+
return false unless pattern.is_a?(Array) && params.is_a?(Array)
|
255
|
+
return false if pattern.size != params.size
|
256
|
+
match = true
|
257
|
+
pattern.zip(params) do |type, param|
|
258
|
+
unless type === param
|
259
|
+
match = false
|
260
|
+
break
|
261
|
+
end
|
262
|
+
end
|
263
|
+
match
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
data/lib/rbgo/select_chan.rb
CHANGED
@@ -19,6 +19,7 @@ module Rbgo
|
|
19
19
|
|
20
20
|
def register(io)
|
21
21
|
register_mutex.synchronize do
|
22
|
+
ios.delete_if { |io| io.closed? }
|
22
23
|
ios << io
|
23
24
|
end
|
24
25
|
end
|
@@ -53,7 +54,7 @@ module Rbgo
|
|
53
54
|
self.have_enq_waiting_flag = false
|
54
55
|
self.have_deq_waiting_flag = false
|
55
56
|
|
56
|
-
self.ios
|
57
|
+
self.ios = []
|
57
58
|
self.register_mutex = Mutex.new
|
58
59
|
end
|
59
60
|
|
@@ -202,7 +203,7 @@ module Rbgo
|
|
202
203
|
super(max)
|
203
204
|
@mutex = Mutex.new
|
204
205
|
|
205
|
-
self.ios
|
206
|
+
self.ios = []
|
206
207
|
self.register_mutex = Mutex.new
|
207
208
|
end
|
208
209
|
|
data/lib/rbgo/version.rb
CHANGED
data/lib/rbgo.rb
CHANGED
data/rbgo.gemspec
CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
|
|
20
20
|
In MRI write program to run concurrently even not parallelly is also important.
|
21
21
|
END
|
22
22
|
s.add_dependency "system", "~> 0.1.3"
|
23
|
+
s.add_dependency "nio4r", "~> 2.4"
|
23
24
|
s.files = `git ls-files`.split("\n")
|
24
25
|
s.license = 'MIT'
|
25
26
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbgo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wang Yin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-08-
|
11
|
+
date: 2019-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: system
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.1.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nio4r
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.4'
|
27
41
|
description: " Write concurrent program with Ruby in Golang style.
|
28
42
|
\ \n\n You can produce a light weight routine easily with a method
|
29
43
|
'go' and communicate with each routine by channel.\n\n In MRI
|
@@ -44,6 +58,7 @@ files:
|
|
44
58
|
- lib/rbgo.rb
|
45
59
|
- lib/rbgo/actor.rb
|
46
60
|
- lib/rbgo/corun.rb
|
61
|
+
- lib/rbgo/io_machine.rb
|
47
62
|
- lib/rbgo/network_service.rb
|
48
63
|
- lib/rbgo/once.rb
|
49
64
|
- lib/rbgo/select_chan.rb
|