arf 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/LICENSE.txt +21 -0
  4. data/Rakefile +12 -0
  5. data/lib/arf/configuration.rb +67 -0
  6. data/lib/arf/context.rb +170 -0
  7. data/lib/arf/errors.rb +10 -0
  8. data/lib/arf/io/buffer.rb +25 -0
  9. data/lib/arf/io/compression.rb +44 -0
  10. data/lib/arf/io/limit_reader.rb +30 -0
  11. data/lib/arf/observer.rb +29 -0
  12. data/lib/arf/proto/array.rb +31 -0
  13. data/lib/arf/proto/boolean.rb +15 -0
  14. data/lib/arf/proto/bytes.rb +35 -0
  15. data/lib/arf/proto/decoder.rb +31 -0
  16. data/lib/arf/proto/encoder.rb +71 -0
  17. data/lib/arf/proto/float.rb +48 -0
  18. data/lib/arf/proto/map.rb +49 -0
  19. data/lib/arf/proto/registry.rb +27 -0
  20. data/lib/arf/proto/scalar.rb +55 -0
  21. data/lib/arf/proto/string.rb +36 -0
  22. data/lib/arf/proto/struct.rb +84 -0
  23. data/lib/arf/proto/types.rb +67 -0
  24. data/lib/arf/proto/union.rb +25 -0
  25. data/lib/arf/proto.rb +17 -0
  26. data/lib/arf/reactor.rb +270 -0
  27. data/lib/arf/rpc/base_message.rb +119 -0
  28. data/lib/arf/rpc/client_base.rb +110 -0
  29. data/lib/arf/rpc/end_stream.rb +9 -0
  30. data/lib/arf/rpc/enum.rb +51 -0
  31. data/lib/arf/rpc/message_kind.rb +27 -0
  32. data/lib/arf/rpc/metadata.rb +120 -0
  33. data/lib/arf/rpc/method_meta.rb +42 -0
  34. data/lib/arf/rpc/request.rb +39 -0
  35. data/lib/arf/rpc/responder.rb +186 -0
  36. data/lib/arf/rpc/response.rb +46 -0
  37. data/lib/arf/rpc/service_base.rb +137 -0
  38. data/lib/arf/rpc/start_stream.rb +9 -0
  39. data/lib/arf/rpc/stream_error.rb +23 -0
  40. data/lib/arf/rpc/stream_item.rb +16 -0
  41. data/lib/arf/rpc/stream_metadata.rb +16 -0
  42. data/lib/arf/rpc/struct.rb +255 -0
  43. data/lib/arf/rpc.rb +19 -0
  44. data/lib/arf/server.rb +123 -0
  45. data/lib/arf/status.rb +75 -0
  46. data/lib/arf/types/array_type.rb +24 -0
  47. data/lib/arf/types/base_type.rb +14 -0
  48. data/lib/arf/types/coercion.rb +36 -0
  49. data/lib/arf/types/in_out_stream.rb +28 -0
  50. data/lib/arf/types/input_stream.rb +8 -0
  51. data/lib/arf/types/map_type.rb +32 -0
  52. data/lib/arf/types/mixin.rb +29 -0
  53. data/lib/arf/types/output_stream.rb +8 -0
  54. data/lib/arf/types/streamer.rb +21 -0
  55. data/lib/arf/types.rb +69 -0
  56. data/lib/arf/version.rb +5 -0
  57. data/lib/arf/wire/base_connection.rb +177 -0
  58. data/lib/arf/wire/client.rb +101 -0
  59. data/lib/arf/wire/encoding.rb +49 -0
  60. data/lib/arf/wire/error_code.rb +35 -0
  61. data/lib/arf/wire/errors.rb +88 -0
  62. data/lib/arf/wire/frame.rb +111 -0
  63. data/lib/arf/wire/frame_kind.rb +23 -0
  64. data/lib/arf/wire/frame_reader.rb +104 -0
  65. data/lib/arf/wire/frames/base_frame.rb +108 -0
  66. data/lib/arf/wire/frames/configuration_frame.rb +33 -0
  67. data/lib/arf/wire/frames/data_frame.rb +21 -0
  68. data/lib/arf/wire/frames/go_away_frame.rb +29 -0
  69. data/lib/arf/wire/frames/make_stream_frame.rb +15 -0
  70. data/lib/arf/wire/frames/ping_frame.rb +18 -0
  71. data/lib/arf/wire/frames/reset_stream_frame.rb +19 -0
  72. data/lib/arf/wire/frames.rb +9 -0
  73. data/lib/arf/wire/server/peer.rb +85 -0
  74. data/lib/arf/wire/server.rb +63 -0
  75. data/lib/arf/wire/stream/state.rb +69 -0
  76. data/lib/arf/wire/stream.rb +128 -0
  77. data/lib/arf/wire/wait_signal.rb +24 -0
  78. data/lib/arf/wire.rb +14 -0
  79. data/lib/arf.rb +46 -0
  80. metadata +195 -0
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module Proto
5
+ EMPTY_MAP_MASK = 0x01 << 4
6
+
7
+ def self.encode_map(v)
8
+ t = TYPE_MAP
9
+ if v.empty?
10
+ t |= EMPTY_MAP_MASK
11
+ return [t].pack("C*")
12
+ end
13
+
14
+ keys = []
15
+ values = []
16
+ v.each_pair do |key, value|
17
+ keys << encode(key)
18
+ values << encode(value)
19
+ end
20
+
21
+ encoded_len = encode_uint64(v.length)
22
+ keys = keys.join
23
+ values = values.join
24
+
25
+ IO::Buffer.new
26
+ .write(t)
27
+ .write_raw(encode_uint64(keys.length + values.length + encoded_len.length))
28
+ .write_raw(encoded_len)
29
+ .write_raw(keys)
30
+ .write_raw(values)
31
+ .string
32
+ end
33
+
34
+ def self.decode_map(header, io)
35
+ return {} if header.anybits?(EMPTY_MAP_MASK)
36
+
37
+ decode_uint64(io) # Discard full length
38
+
39
+ pairs_len = decode_uint64(io)
40
+ keys = []
41
+ values = []
42
+
43
+ pairs_len.times { keys << decode(io) }
44
+ pairs_len.times { values << decode(io) }
45
+
46
+ keys.zip(values).to_h
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module Proto
5
+ module Registry
6
+ def self.register!(cls)
7
+ @structs ||= {}
8
+ id = cls.arf_struct_id
9
+ fields = Proto.fields_from_struct(cls)
10
+ @structs[id] = {
11
+ id:,
12
+ fields:,
13
+ type: cls
14
+ }
15
+ end
16
+
17
+ def self.reset!
18
+ @structs = {}
19
+ end
20
+
21
+ def self.find(id)
22
+ @structs ||= {}
23
+ @structs[id]
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module Proto
5
+ NUMERIC_SIGNED_MASK = 0x01 << 4
6
+ NUMERIC_ZERO_MASK = 0x01 << 5
7
+ NUMERIC_NEGATIVE_MASK = 0x01 << 6
8
+
9
+ def self.encode_scalar(v, signed: false)
10
+ type = TYPE_SCALAR
11
+ type |= NUMERIC_SIGNED_MASK if signed
12
+ type |= NUMERIC_ZERO_MASK if v.zero?
13
+ if v.negative?
14
+ type |= NUMERIC_NEGATIVE_MASK
15
+ v *= -1
16
+ end
17
+
18
+ [
19
+ [type].pack("C*"),
20
+ v.zero? ? nil : encode_uint64(v)
21
+ ].compact.join
22
+ end
23
+
24
+ def self.decode_scalar(header, io)
25
+ return 0 if header.anybits?(NUMERIC_ZERO_MASK)
26
+
27
+ v = decode_uint64(io)
28
+ v *= -1 if header.anybits?(NUMERIC_NEGATIVE_MASK)
29
+ v
30
+ end
31
+
32
+ def self.encode_uint64(v)
33
+ bytes = []
34
+ while v >= 0x80
35
+ bytes << ((v & 0xFF) | 0x80)
36
+ v >>= 7
37
+ end
38
+ bytes << v
39
+ bytes.pack("C*")
40
+ end
41
+
42
+ def self.decode_uint64(io)
43
+ x = 0
44
+ s = 0
45
+ b = 0
46
+ loop do
47
+ b = io.read(1).getbyte(0)
48
+ return (x | (b << s)) if b < 0x80
49
+
50
+ x |= ((b & 0x7f) << s)
51
+ s += 7
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module Proto
5
+ STRING_EMPTY_MASK = 0x01 << 4
6
+
7
+ def self.encode_string(s)
8
+ t = TYPE_STRING
9
+ if s.nil? || s.empty?
10
+ t |= STRING_EMPTY_MASK
11
+ return [t].pack("C*")
12
+ end
13
+
14
+ s = s.to_s.encode("UTF-8")
15
+
16
+ [
17
+ [t].pack("C*"),
18
+ encode_uint64(s.bytesize),
19
+ s
20
+ ].join
21
+ end
22
+
23
+ def self.decode_string(header, io)
24
+ return "" if header.anybits?(STRING_EMPTY_MASK)
25
+
26
+ size = decode_uint64(io)
27
+ data = StringIO.new
28
+ until size.zero?
29
+ read = io.readpartial(size)
30
+ data.write(read)
31
+ size -= read.length
32
+ end
33
+ data.string.encode("UTF-8")
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module Proto
5
+ def self.fields_from_struct(v)
6
+ base = v.is_a?(Class) ? v : v.class
7
+ fields = []
8
+ base.fields.each do |f|
9
+ if f[:id].is_a? Integer
10
+ fields << if f[:type].is_a?(Symbol) ||
11
+ f[:type].is_a?(Arf::Types::ArrayType) ||
12
+ f[:type].is_a?(Arf::Types::MapType)
13
+ f
14
+ else
15
+ {
16
+ id: f[:id],
17
+ name: f[:name],
18
+ type: base.find_type(f[:type])
19
+ }
20
+ end
21
+ else
22
+ raise UnsupportedNestedUnionError, "Nested unions are not supported" if base.union?
23
+
24
+ union_type = base.find_type(f[:type])
25
+ fields << {
26
+ id: union_type.fields.first[:id],
27
+ type: union_type,
28
+ name: f[:name]
29
+ }
30
+ end
31
+ end
32
+
33
+ fields.sort_by! { _1[:id] }
34
+ fields
35
+ end
36
+
37
+ def self.encode_struct(v)
38
+ return encode_union(v) if v.union?
39
+
40
+ struct_id = v.arf_struct_id
41
+ fields = fields_from_struct(v)
42
+ data = []
43
+ fields.each do |f|
44
+ data << encode_uint64(f[:id])
45
+ data << encode_as(v.instance_variable_get("@#{f[:name]}"), f[:type])
46
+ end
47
+
48
+ payload = data.join
49
+
50
+ [
51
+ [TYPE_STRUCT].pack("C*"),
52
+ encode_string(struct_id),
53
+ encode_uint64(payload.length),
54
+ payload
55
+ ].join
56
+ end
57
+
58
+ def self.decode_struct(_header, io)
59
+ id_type, id_header = read_type(io)
60
+ if id_type != TYPE_STRING
61
+ # :nocov:
62
+ raise DecodeFailedError, "cannot decode struct: expected String, found #{TYPE_NAME[id_type]}"
63
+ # :nocov:
64
+ end
65
+
66
+ struct_id = decode_string(id_header, io)
67
+ bytes_len = decode_uint64(io)
68
+ reader = IO::LimitReader.new(io, bytes_len)
69
+ fields = {}
70
+ loop do
71
+ id = decode_uint64(reader)
72
+ fields[id] = decode(reader)
73
+ rescue EOFError
74
+ break
75
+ end
76
+
77
+ meta_str = Registry.find(struct_id)
78
+ raise UnknownMeessageError, "Unknown message ID #{struct_id}" unless meta_str
79
+
80
+ inst = meta_str[:type].new
81
+ inst.decode_fields(fields)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module Proto
5
+ TYPE_VOID = 0b0000
6
+ TYPE_SCALAR = 0b0001
7
+ TYPE_BOOLEAN = 0b0010
8
+ TYPE_FLOAT = 0b0011
9
+ TYPE_STRING = 0b0100
10
+ TYPE_BYTES = 0b0101
11
+ TYPE_ARRAY = 0b0110
12
+ TYPE_MAP = 0b0111
13
+ TYPE_STRUCT = 0b1000
14
+ TYPE_UNION = 0b1001
15
+
16
+ ALL_PRIMITIVES = [
17
+ TYPE_VOID,
18
+ TYPE_SCALAR,
19
+ TYPE_BOOLEAN,
20
+ TYPE_FLOAT,
21
+ TYPE_STRING,
22
+ TYPE_BYTES,
23
+ TYPE_ARRAY,
24
+ TYPE_MAP,
25
+ TYPE_STRUCT,
26
+ TYPE_UNION
27
+ ].freeze
28
+
29
+ TYPE_NAME = {
30
+ TYPE_VOID => "Void",
31
+ TYPE_SCALAR => "Scalar",
32
+ TYPE_BOOLEAN => "Boolean",
33
+ TYPE_FLOAT => "Float",
34
+ TYPE_STRING => "String",
35
+ TYPE_BYTES => "Bytes",
36
+ TYPE_ARRAY => "Array",
37
+ TYPE_MAP => "Map",
38
+ TYPE_STRUCT => "Struct",
39
+ TYPE_UNION => "Union"
40
+ }.freeze
41
+
42
+ SIMPLE_PRIMITIVES = {
43
+ void: TYPE_VOID,
44
+ uint8: TYPE_SCALAR,
45
+ uint16: TYPE_SCALAR,
46
+ uint32: TYPE_SCALAR,
47
+ uint64: TYPE_SCALAR,
48
+ int8: TYPE_SCALAR,
49
+ int16: TYPE_SCALAR,
50
+ int32: TYPE_SCALAR,
51
+ int64: TYPE_SCALAR,
52
+ bool: TYPE_BOOLEAN,
53
+ float32: TYPE_FLOAT,
54
+ float64: TYPE_FLOAT,
55
+ string: TYPE_STRING,
56
+ bytes: TYPE_BYTES
57
+ }.freeze
58
+
59
+ def self.read_type(io)
60
+ b = io.read(1).getbyte(0)
61
+ decoded = b & 0xF
62
+ raise UnknownTypeError, format("Unknown type 0x%02x", b) unless ALL_PRIMITIVES.include? decoded
63
+
64
+ [decoded, b]
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ module Proto
5
+ def self.encode_union(v)
6
+ selected = v.__arf_union_set_id
7
+ f = fields_from_struct(v).find { _1[:id] == selected }
8
+ payload = encode_as(v.instance_variable_get("@#{f[:name]}"), f[:type])
9
+
10
+ [
11
+ [TYPE_UNION].pack("C*"),
12
+ encode_uint64(selected),
13
+ payload
14
+ ].join
15
+ end
16
+
17
+ def self.decode_union(_header, io)
18
+ {
19
+ union: true,
20
+ id: decode_uint64(io),
21
+ value: decode(io)
22
+ }
23
+ end
24
+ end
25
+ end
data/lib/arf/proto.rb ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "proto/types"
4
+ require_relative "proto/registry"
5
+
6
+ require_relative "proto/scalar"
7
+ require_relative "proto/boolean"
8
+ require_relative "proto/bytes"
9
+ require_relative "proto/float"
10
+ require_relative "proto/string"
11
+ require_relative "proto/array"
12
+ require_relative "proto/map"
13
+ require_relative "proto/struct"
14
+ require_relative "proto/union"
15
+
16
+ require_relative "proto/encoder"
17
+ require_relative "proto/decoder"
@@ -0,0 +1,270 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arf
4
+ class Reactor
5
+ BEAT_INTERVAL = 3
6
+
7
+ def self.instance = (@instance ||= new)
8
+ def self.connect(host, port, handler) = instance.connect(host, port, handler)
9
+ def self.post(task = nil, &) = instance.post(task, &)
10
+ def self.client_writes_pending(client) = instance.client_writes_pending(client)
11
+ def self.detach_client(client) = instance.detach_client(client)
12
+ def self.detach(io) = instance.detach(io)
13
+
14
+ def self.attach_server(server, handler_class, *handler_args)
15
+ instance.attach_server(server, handler_class, *handler_args)
16
+ end
17
+
18
+ def initialize
19
+ @nio = @executor = @thread = nil
20
+ @stopping = false
21
+ @todo = Queue.new
22
+ @spawn_lock = Monitor.new
23
+ @map = {}
24
+ @socket_source = {}
25
+ @log = Arf.configuration.logger.with_fields(subsystem: "Reactor")
26
+ end
27
+
28
+ def timer(interval, &block)
29
+ @log.debug("Attached timer", interval:, callable: block)
30
+ Concurrent::TimerTask.new(execution_interval: interval, &block).tap(&:execute)
31
+ end
32
+
33
+ def connect(host, port, handler)
34
+ @log.debug("Establishing connection", host:, port:, handler:)
35
+ handler = handler.new
36
+ @todo << lambda {
37
+ addr_info = Socket.getaddrinfo(host, port, nil, Socket::SOCK_STREAM).first
38
+ family = addr_info[4]
39
+ io = Socket.new(family, Socket::SOCK_STREAM, 0)
40
+ begin
41
+ io.connect_nonblock Socket.sockaddr_in(port, host)
42
+ rescue Errno::EINPROGRESS
43
+ @log.debug("Connection in progress", id: handler.object_id)
44
+ @map[io] = @nio.register(io, :w)
45
+ @map[io].value = [host, port, handler]
46
+ @socket_source[io] = :client
47
+ next
48
+ end
49
+ @log.debug("Connection succeeded", id: handler.object_id)
50
+ @map[io] = @nio.register(io, :r)
51
+ @map[io].value = handler
52
+ @socket_source[io] = :client
53
+ @log.debug("Async post_init dispatch", id: handler.object_id)
54
+ post { handler.registered! }
55
+ post { handler.post_init } if handler.respond_to?(:post_init)
56
+ }
57
+ wakeup
58
+ handler
59
+ end
60
+
61
+ def attach_server(server, handler_class, *handler_args)
62
+ @log.debug("Attach server", server:, handler_class:)
63
+ @todo << lambda {
64
+ @map[server] = @nio.register(server, :r)
65
+ @map[server].value = [handler_class, handler_args]
66
+ }
67
+ wakeup
68
+ end
69
+
70
+ def detach(io)
71
+ @log.debug("Detach IO", io:)
72
+ @todo << lambda {
73
+ @nio.deregister(io)
74
+ @map.delete(io)
75
+ @socket_source.delete(io)
76
+ io.close
77
+ }
78
+ wakeup
79
+ end
80
+
81
+ def detach_client(client)
82
+ @log.debug("Detach client", client: client.class.name, id: client.object_id)
83
+ @todo << lambda {
84
+ io = io_for_client(client)
85
+ next unless io
86
+
87
+ @nio.deregister(io)
88
+ @map.delete(io)
89
+ @socket_source.delete(io)
90
+ io.close
91
+ }
92
+ wakeup
93
+ end
94
+
95
+ def post(task = nil, &block)
96
+ task ||= block
97
+ spawn
98
+ source = caller[0]
99
+ @executor << lambda do
100
+ task.call
101
+ rescue Exception => e
102
+ @log.error("Async post execution failed", e, source:)
103
+ end
104
+ end
105
+
106
+ def io_for_client(client)
107
+ @map.each do |k, v|
108
+ return k if v.value == client
109
+ end
110
+ nil
111
+ end
112
+
113
+ def client_writes_pending(client)
114
+ @log.debug("Writes pending", client: client.class.name, id: client.object_id)
115
+ @todo << lambda {
116
+ if (monitor = @map[io_for_client(client)])
117
+ monitor.interests = :rw
118
+ else
119
+ @log.warn("No monitor for client", id: client.object_id)
120
+ end
121
+ }
122
+ wakeup
123
+ @log.debug("Writes pending registered")
124
+ end
125
+
126
+ private
127
+
128
+ def spawn
129
+ return if @thread&.alive?
130
+
131
+ @spawn_lock.synchronize do
132
+ return if @thread&.alive?
133
+
134
+ @nio ||= NIO::Selector.new
135
+
136
+ @executor ||= Concurrent::ThreadPoolExecutor.new(
137
+ min_threads: 1,
138
+ max_threads: 10,
139
+ max_queue: 0
140
+ )
141
+
142
+ @thread = Thread.new { run }
143
+ @thread.name = "Arf::Wire::Reactor loop"
144
+
145
+ setup_ping_timer
146
+ true
147
+ end
148
+ end
149
+
150
+ def setup_ping_timer
151
+ @setup_ping_timer ||= timer(BEAT_INTERVAL) do
152
+ post do
153
+ @map.each_value do |v|
154
+ if v.is_a? Wire::BaseConnection
155
+ @log.debug("Dispatch ping", client: v)
156
+ v.ping
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ def handle_server_monitor(monitor)
164
+ io = monitor.io
165
+ handler, handler_args = monitor.value
166
+ return unless monitor.readable?
167
+
168
+ client = io.accept_nonblock
169
+ inst = handler.new(*handler_args)
170
+ # Basically #attach, but without the round-trip.
171
+ @map[client] = @nio.register(client, :r)
172
+ @map[client].value = inst
173
+ @socket_source[client] = :server
174
+ post { inst.registered! }
175
+ post { inst.post_init } if inst.respond_to? :post_init
176
+ end
177
+
178
+ def handle_socket_monitor(monitor)
179
+ io = monitor.io
180
+ client = monitor.value
181
+
182
+ if client.is_a? Array
183
+ return unless monitor.writable?
184
+
185
+ # This is a socket waiting for connection
186
+ host, port, handler = client
187
+ begin
188
+ io.connect_nonblock Socket.sockaddr_in(port, host)
189
+ rescue Errno::EISCONN
190
+ monitor.value = handler
191
+ monitor.interests = :r
192
+ @log.debug("Async post_init dispatch", id: handler.object_id)
193
+ post { handler.registered! }
194
+ post { handler.post_init } if handler.respond_to?(:post_init)
195
+ end
196
+ return
197
+ end
198
+
199
+ begin
200
+ if monitor.writable?
201
+ if client.flush_write_buffer(io)
202
+ monitor.interests = :r
203
+ detach(io) if client.arf_close_after_writing?
204
+ end
205
+ return unless monitor.readable?
206
+ end
207
+
208
+ incoming = io.read_nonblock(4096, exception: false)
209
+ case incoming
210
+ when :wait_readable
211
+ nil
212
+ when nil
213
+ post do
214
+ client.close
215
+ rescue Exception => e
216
+ @log.error("Failed closing client", e, id: client.object_id)
217
+ @nio.deregister(io)
218
+ @map.delete(io)
219
+ @socket_source.delete(io)
220
+ end
221
+ else
222
+ post do
223
+ client.recv(incoming)
224
+ rescue Exception => e
225
+ @log.error("Failed calling client.recv", e, id: client.object_id)
226
+ begin
227
+ client.close
228
+ rescue Exception
229
+ # noop
230
+ ensure
231
+ @nio.deregister(io)
232
+ @map.delete(io)
233
+ @socket_source.delete(io)
234
+ end
235
+ end
236
+ end
237
+ rescue Errno::EPIPE, Errno::ECONNRESET => e
238
+ @log.error("Failed reading from client", e, id: client.object_id)
239
+ @nio.deregister(io)
240
+ @map.delete(io)
241
+ @socket_source.delete(io)
242
+ end
243
+ end
244
+
245
+ def wakeup
246
+ spawn || @nio.wakeup
247
+ end
248
+
249
+ def run
250
+ loop do
251
+ if @stopping
252
+ @nio.close
253
+ break
254
+ end
255
+
256
+ @todo.pop(true).call until @todo.empty?
257
+
258
+ next unless (monitors = @nio.select)
259
+
260
+ monitors.each do |monitor|
261
+ case monitor.io
262
+ when TCPServer then handle_server_monitor(monitor)
263
+ when TCPSocket, Socket then handle_socket_monitor(monitor)
264
+ else raise "Unexpected monitor IO on reactor: #{monitor.io.inspect}"
265
+ end
266
+ end
267
+ end
268
+ end
269
+ end
270
+ end