ori-rb 0.4.2 → 0.4.4
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/.ruby-version +1 -1
- data/lib/ori/lazy.rb +1 -0
- data/lib/ori/scope.rb +121 -7
- data/lib/ori/version.rb +1 -1
- data/mise.toml +1 -1
- data/sorbet/shims/fiber.rbi +7 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd8326ee1d0ef86eff23c6b472b82ee72d2253e41d1367f053aeb878ed926320
|
|
4
|
+
data.tar.gz: 02b872fdd48ef913857e022f9333f5a447c59493a2dd5ccdbccee5486c479d0d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 102ff1557da066713c20b08e87f29e01fff6c07978c9a384d657df6dfb2bfc4f6e95edb0f966e58b68ff44b4430232a95d23be846bba66838848792e8f3dcd86
|
|
7
|
+
data.tar.gz: 76e6f9e1e3ed9d2dfbf8078325560a4ca40dcc770c5a2ba6ec16e58ed3809203e18a15d859ead27d236f0d9439a342326b64fe31edf81ff426e1b85926ed5107
|
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4.0.1
|
data/lib/ori/lazy.rb
CHANGED
data/lib/ori/scope.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# typed: true
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
|
|
3
4
|
require "nio"
|
|
5
|
+
require "io/nonblock"
|
|
4
6
|
require "random/formatter"
|
|
5
7
|
require "ori/lazy"
|
|
8
|
+
require "English"
|
|
6
9
|
|
|
7
10
|
module Ori
|
|
8
11
|
class Scope
|
|
@@ -51,6 +54,11 @@ module Ori
|
|
|
51
54
|
@cancelled = false
|
|
52
55
|
@closed = false
|
|
53
56
|
|
|
57
|
+
# Cross-thread wakeup mechanism for unblock
|
|
58
|
+
@wakeup_mutex = ::Mutex.new
|
|
59
|
+
@wakeup_queue = [] #: Array[Fiber]
|
|
60
|
+
@wakeup_reader, @wakeup_writer = IO.pipe
|
|
61
|
+
|
|
54
62
|
# Instead, use thread-local storage
|
|
55
63
|
thread_local_state[object_id] = ThreadLocalState.new
|
|
56
64
|
|
|
@@ -228,19 +236,102 @@ module Ori
|
|
|
228
236
|
end
|
|
229
237
|
|
|
230
238
|
def unblock(blocker, fiber)
|
|
231
|
-
unless fiber_ids.key?(
|
|
239
|
+
unless fiber_ids.key?(fiber)
|
|
232
240
|
return @parent_scope.unblock(blocker, fiber) if @parent_scope
|
|
233
241
|
end
|
|
234
242
|
|
|
235
|
-
|
|
243
|
+
# Thread-safe: enqueue the fiber and signal the event loop
|
|
244
|
+
# via the wakeup pipe. unblock may be called from any thread.
|
|
245
|
+
@wakeup_mutex.synchronize { @wakeup_queue << fiber }
|
|
246
|
+
@wakeup_writer.write_nonblock(".") rescue nil # rubocop:disable Style/RescueModifier
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def io_read(io, buffer, length, offset)
|
|
250
|
+
return @parent_scope.io_read(io, buffer, length, offset) unless fiber_ids.key?(Fiber.current)
|
|
251
|
+
|
|
252
|
+
io.nonblock = true unless io.closed?
|
|
253
|
+
total = 0 #: Integer
|
|
254
|
+
|
|
255
|
+
loop do
|
|
256
|
+
maximum_size = buffer.size - offset - total
|
|
257
|
+
break if maximum_size <= 0
|
|
258
|
+
|
|
259
|
+
case result = Fiber.blocking { io.read_nonblock(maximum_size, exception: false) }
|
|
260
|
+
when :wait_readable
|
|
261
|
+
break if total > 0
|
|
262
|
+
|
|
263
|
+
io_wait(io, IO::READABLE)
|
|
264
|
+
when nil # EOF
|
|
265
|
+
break
|
|
266
|
+
else
|
|
267
|
+
buffer.set_string(result, offset + total)
|
|
268
|
+
total += result.bytesize
|
|
269
|
+
break if length == 0 || total >= length
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
total
|
|
274
|
+
rescue SystemCallError => e
|
|
275
|
+
(total || 0) > 0 ? total : -(e.errno || 0)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def io_write(io, buffer, length, offset)
|
|
279
|
+
return @parent_scope.io_write(io, buffer, length, offset) unless fiber_ids.key?(Fiber.current)
|
|
280
|
+
|
|
281
|
+
io.nonblock = true unless io.closed?
|
|
282
|
+
total = 0 #: Integer
|
|
283
|
+
max_bytes = buffer.size - offset
|
|
284
|
+
|
|
285
|
+
while total < max_bytes
|
|
286
|
+
chunk = buffer.get_string(offset + total, max_bytes - total)
|
|
287
|
+
case result = Fiber.blocking { io.write_nonblock(chunk, exception: false) }
|
|
288
|
+
when :wait_writable
|
|
289
|
+
io_wait(io, IO::WRITABLE)
|
|
290
|
+
else
|
|
291
|
+
total += result
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
total
|
|
296
|
+
rescue SystemCallError => e
|
|
297
|
+
(total || 0) > 0 ? total : -(e.errno || 0)
|
|
236
298
|
end
|
|
237
299
|
|
|
238
|
-
# def io_write(...) = ()
|
|
239
300
|
# def io_pread(...) = ()
|
|
240
301
|
# def io_pwrite(...) = ()
|
|
241
302
|
|
|
303
|
+
def process_wait(pid, flags)
|
|
304
|
+
return @parent_scope.process_wait(pid, flags) unless fiber_ids.key?(Fiber.current)
|
|
305
|
+
|
|
306
|
+
fiber = Fiber.current
|
|
307
|
+
id = fiber_ids[fiber]
|
|
308
|
+
@tracer&.record(id, :waiting_process, "pid=#{pid}")
|
|
309
|
+
|
|
310
|
+
# Bridge thread-based process waiting into the IO event loop.
|
|
311
|
+
# A pipe signals completion; the thread closes its end when done.
|
|
312
|
+
reader, writer = IO.pipe
|
|
313
|
+
|
|
314
|
+
thread = Thread.new(writer) do |w|
|
|
315
|
+
::Process.wait(pid, flags)
|
|
316
|
+
$CHILD_STATUS # return the Process::Status
|
|
317
|
+
ensure
|
|
318
|
+
w.syswrite(".") rescue nil # rubocop:disable Style/RescueModifier
|
|
319
|
+
w.close rescue nil # rubocop:disable Style/RescueModifier
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Register directly on the event loop's readable set
|
|
323
|
+
readable[reader].add(fiber)
|
|
324
|
+
Fiber.yield
|
|
325
|
+
|
|
326
|
+
thread.value
|
|
327
|
+
ensure
|
|
328
|
+
readable[reader]&.delete(fiber) if reader
|
|
329
|
+
readable.delete(reader) if reader && readable[reader]&.empty?
|
|
330
|
+
reader&.close unless reader&.closed?
|
|
331
|
+
writer&.close unless writer&.closed?
|
|
332
|
+
end
|
|
333
|
+
|
|
242
334
|
# TODO: Implement these
|
|
243
|
-
# def process_wait(...) = ()
|
|
244
335
|
# def timeout_after(...) = ()
|
|
245
336
|
# def address_resolve(...) = ()
|
|
246
337
|
|
|
@@ -262,6 +353,7 @@ module Ori
|
|
|
262
353
|
def pending_work?
|
|
263
354
|
return false if closed?
|
|
264
355
|
|
|
356
|
+
return true if @wakeup_mutex.synchronize { @wakeup_queue.any? }
|
|
265
357
|
return true if pending.any?(&:alive?)
|
|
266
358
|
return true if waiting.any? { |fiber, _| fiber.alive? }
|
|
267
359
|
return true if blocked.any? { |fiber, _| fiber.alive? }
|
|
@@ -362,9 +454,24 @@ module Ori
|
|
|
362
454
|
end
|
|
363
455
|
|
|
364
456
|
def process_io_operations(now = nil)
|
|
365
|
-
|
|
457
|
+
has_io = readable.any? || writable.any?
|
|
458
|
+
has_wakeup = @wakeup_mutex.synchronize { @wakeup_queue.any? }
|
|
459
|
+
|
|
460
|
+
# Process any already-queued wakeups before selecting
|
|
461
|
+
drain_wakeup_queue if has_wakeup
|
|
462
|
+
|
|
463
|
+
return unless has_io
|
|
366
464
|
|
|
367
|
-
|
|
465
|
+
select_readable = readable.keys
|
|
466
|
+
select_readable << @wakeup_reader
|
|
467
|
+
|
|
468
|
+
readable_out, writable_out = IO.select(select_readable, writable.keys, [], next_timeout(now))
|
|
469
|
+
|
|
470
|
+
# Drain wakeup pipe if signaled
|
|
471
|
+
if readable_out&.delete(@wakeup_reader)
|
|
472
|
+
@wakeup_reader.read_nonblock(256) rescue nil # rubocop:disable Style/RescueModifier
|
|
473
|
+
drain_wakeup_queue
|
|
474
|
+
end
|
|
368
475
|
|
|
369
476
|
process_ready_io(readable_out, readable)
|
|
370
477
|
process_ready_io(writable_out, writable)
|
|
@@ -378,10 +485,17 @@ module Ori
|
|
|
378
485
|
end
|
|
379
486
|
end
|
|
380
487
|
|
|
488
|
+
def drain_wakeup_queue
|
|
489
|
+
fibers = @wakeup_mutex.synchronize { @wakeup_queue.shift(@wakeup_queue.size) }
|
|
490
|
+
fibers.each { |fiber| resume_fiber(fiber) if fiber.alive? }
|
|
491
|
+
end
|
|
492
|
+
|
|
381
493
|
def close_scope
|
|
382
494
|
@closed = true
|
|
383
495
|
@tracer&.record_scope(@scope_id, :closed)
|
|
384
496
|
thread_local_state&.delete(object_id)
|
|
497
|
+
@wakeup_reader&.close unless @wakeup_reader&.closed?
|
|
498
|
+
@wakeup_writer&.close unless @wakeup_writer&.closed?
|
|
385
499
|
end
|
|
386
500
|
|
|
387
501
|
# Timeouts and deadlines
|
|
@@ -428,7 +542,7 @@ module Ori
|
|
|
428
542
|
end
|
|
429
543
|
|
|
430
544
|
def next_timeout(now = nil)
|
|
431
|
-
timeouts =
|
|
545
|
+
timeouts = [] #: Array[Numeric]
|
|
432
546
|
timeouts.concat(waiting.values.compact) unless waiting.empty?
|
|
433
547
|
timeouts << @deadline_at if @deadline_at
|
|
434
548
|
|
data/lib/ori/version.rb
CHANGED
data/mise.toml
CHANGED
data/sorbet/shims/fiber.rbi
CHANGED
|
@@ -11,6 +11,13 @@ class Fiber
|
|
|
11
11
|
|
|
12
12
|
sig { returns(T.untyped) }
|
|
13
13
|
def current_scheduler; end
|
|
14
|
+
|
|
15
|
+
sig do
|
|
16
|
+
type_parameters(:T)
|
|
17
|
+
.params(block: T.proc.returns(T.type_parameter(:T)))
|
|
18
|
+
.returns(T.type_parameter(:T))
|
|
19
|
+
end
|
|
20
|
+
def blocking(&block); end
|
|
14
21
|
end
|
|
15
22
|
|
|
16
23
|
sig { params(block: T.proc.void).void }
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ori-rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jahfer Husain
|
|
@@ -152,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
152
152
|
- !ruby/object:Gem::Version
|
|
153
153
|
version: '0'
|
|
154
154
|
requirements: []
|
|
155
|
-
rubygems_version:
|
|
155
|
+
rubygems_version: 4.0.3
|
|
156
156
|
specification_version: 4
|
|
157
157
|
summary: A library for building concurrent applications.
|
|
158
158
|
test_files: []
|