carbon_fiber 0.1.0-aarch64-linux
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 +7 -0
- data/LICENSE +21 -0
- data/README.md +320 -0
- data/lib/carbon_fiber/3.4.0/carbon_fiber_native.so +0 -0
- data/lib/carbon_fiber/4.0.0/carbon_fiber_native.so +0 -0
- data/lib/carbon_fiber/async.rb +256 -0
- data/lib/carbon_fiber/native/fallback.rb +387 -0
- data/lib/carbon_fiber/native.rb +41 -0
- data/lib/carbon_fiber/scheduler.rb +396 -0
- data/lib/carbon_fiber/version.rb +6 -0
- data/lib/carbon_fiber.rb +4 -0
- metadata +155 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Please note that this code is heavily AI-assisted.
|
|
4
|
+
|
|
5
|
+
require "resolv"
|
|
6
|
+
require "socket"
|
|
7
|
+
require "timeout"
|
|
8
|
+
require_relative "native"
|
|
9
|
+
|
|
10
|
+
# High-performance Ruby Fiber Scheduler backed by Zig and libxev.
|
|
11
|
+
#
|
|
12
|
+
# Carbon Fiber implements the Ruby Fiber Scheduler protocol with a native
|
|
13
|
+
# event loop: io_uring on Linux, kqueue on macOS. Install it as the thread's
|
|
14
|
+
# scheduler and existing blocking I/O code becomes concurrent automatically.
|
|
15
|
+
#
|
|
16
|
+
# @example Basic usage
|
|
17
|
+
# require "carbon_fiber"
|
|
18
|
+
#
|
|
19
|
+
# Fiber.set_scheduler(CarbonFiber::Scheduler.new)
|
|
20
|
+
# Fiber.schedule { Net::HTTP.get(URI(url)) }
|
|
21
|
+
#
|
|
22
|
+
# @example With the Async framework
|
|
23
|
+
# require "carbon_fiber/async"
|
|
24
|
+
# CarbonFiber::Async.default!
|
|
25
|
+
#
|
|
26
|
+
# Async { |task| task.sleep(1) }
|
|
27
|
+
#
|
|
28
|
+
# @see CarbonFiber::Scheduler
|
|
29
|
+
# @see CarbonFiber::Async
|
|
30
|
+
module CarbonFiber
|
|
31
|
+
# Implements the Ruby Fiber Scheduler interface.
|
|
32
|
+
#
|
|
33
|
+
# Delegates I/O and timer operations to a native Zig selector (io_uring on
|
|
34
|
+
# Linux, kqueue on macOS). Operations the native layer doesn't cover
|
|
35
|
+
# (DNS, process_wait) run on background threads.
|
|
36
|
+
#
|
|
37
|
+
# @example
|
|
38
|
+
# scheduler = CarbonFiber::Scheduler.new
|
|
39
|
+
# Fiber.set_scheduler(scheduler)
|
|
40
|
+
# Fiber.schedule { sleep 1; puts "done" }
|
|
41
|
+
# scheduler.run
|
|
42
|
+
# Fiber.set_scheduler(nil)
|
|
43
|
+
class Scheduler
|
|
44
|
+
# @param root_fiber [Fiber] the event loop fiber (defaults to current)
|
|
45
|
+
# @param selector [Class] native selector class to instantiate
|
|
46
|
+
def initialize(root_fiber = Fiber.current, selector: CarbonFiber::Native::Selector)
|
|
47
|
+
@root_fiber = root_fiber
|
|
48
|
+
@scheduler_thread = Thread.current
|
|
49
|
+
@selector = selector.new(root_fiber)
|
|
50
|
+
@active_fibers = 0
|
|
51
|
+
@background_count = 0
|
|
52
|
+
@closed = false
|
|
53
|
+
@closing = false
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Called by Ruby when +Fiber.set_scheduler(nil)+ is invoked.
|
|
57
|
+
def scheduler_close
|
|
58
|
+
close(true)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Drain pending work and release the native selector.
|
|
62
|
+
def close(internal = false)
|
|
63
|
+
return true if @closed || @closing
|
|
64
|
+
|
|
65
|
+
unless internal
|
|
66
|
+
return Fiber.set_scheduler(nil) if Fiber.scheduler == self
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
@closing = true
|
|
70
|
+
run
|
|
71
|
+
true
|
|
72
|
+
ensure
|
|
73
|
+
unless @closed
|
|
74
|
+
@selector&.destroy
|
|
75
|
+
@closed = true
|
|
76
|
+
@closing = false
|
|
77
|
+
freeze
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @return [Boolean] whether the scheduler has been closed
|
|
82
|
+
def closed?
|
|
83
|
+
@closed
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Monotonic clock used by the scheduler for timers.
|
|
87
|
+
# @return [Float] seconds since an arbitrary epoch
|
|
88
|
+
def current_time
|
|
89
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Create and schedule a non-blocking fiber.
|
|
93
|
+
# @yield the block to run inside the fiber
|
|
94
|
+
# @return [Fiber]
|
|
95
|
+
def fiber(&block)
|
|
96
|
+
fiber = Fiber.new(blocking: false) do
|
|
97
|
+
block.call
|
|
98
|
+
ensure
|
|
99
|
+
fiber_done
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
@active_fibers += 1
|
|
103
|
+
@selector.push(fiber)
|
|
104
|
+
@selector.wakeup unless Thread.current.equal?(@scheduler_thread)
|
|
105
|
+
|
|
106
|
+
fiber
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Transfer control to the next ready fiber or the event loop.
|
|
110
|
+
def transfer
|
|
111
|
+
@selector.transfer
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Re-enqueue the current fiber and transfer to the event loop.
|
|
115
|
+
def yield
|
|
116
|
+
@selector.yield
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Enqueue a fiber into the ready queue.
|
|
120
|
+
# @param fiber [Fiber]
|
|
121
|
+
def push(fiber)
|
|
122
|
+
@selector.push(fiber)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Resume a fiber, optionally passing a value.
|
|
126
|
+
# @param fiber [Fiber]
|
|
127
|
+
# @param arguments [Array] at most one value to pass to the fiber
|
|
128
|
+
def resume(fiber, *arguments)
|
|
129
|
+
if arguments.empty?
|
|
130
|
+
@selector.push(fiber)
|
|
131
|
+
else
|
|
132
|
+
@selector.resume(fiber, arguments.first)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Deliver an exception to a suspended fiber.
|
|
137
|
+
# @param fiber [Fiber]
|
|
138
|
+
# @param exception [Exception]
|
|
139
|
+
def raise(fiber, exception)
|
|
140
|
+
@selector.raise(fiber, exception)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Wake the event loop (thread-safe).
|
|
144
|
+
def wakeup
|
|
145
|
+
@selector.wakeup
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Run one iteration of the event loop.
|
|
149
|
+
# @param timeout [Float, nil] maximum seconds to wait
|
|
150
|
+
def select(timeout = nil)
|
|
151
|
+
@selector.select(timeout)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Suspend the current fiber until unblocked or timed out.
|
|
155
|
+
# @param _blocker [Object] unused, required by the protocol
|
|
156
|
+
# @param timeout [Float, nil] seconds before automatic resume
|
|
157
|
+
def block(_blocker, timeout = nil)
|
|
158
|
+
@selector.block(Fiber.current, timeout)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Resume a fiber previously suspended by {#block}.
|
|
162
|
+
# @param _blocker [Object] unused, required by the protocol
|
|
163
|
+
# @param fiber [Fiber]
|
|
164
|
+
def unblock(_blocker, fiber)
|
|
165
|
+
@selector.unblock(fiber)
|
|
166
|
+
true
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Intercept +Kernel#sleep+. Parks the fiber on a native timer.
|
|
170
|
+
# @param duration [Float, nil] seconds to sleep; nil sleeps forever
|
|
171
|
+
def kernel_sleep(duration = nil)
|
|
172
|
+
if duration.nil?
|
|
173
|
+
transfer
|
|
174
|
+
elsif duration <= 0
|
|
175
|
+
self.yield
|
|
176
|
+
else
|
|
177
|
+
block(nil, duration)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
true
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Wait for I/O readiness on a file descriptor.
|
|
184
|
+
# @param io [IO]
|
|
185
|
+
# @param events [Integer] bitmask of +IO::READABLE+, +IO::WRITABLE+
|
|
186
|
+
# @param timeout [Float, nil]
|
|
187
|
+
# @return [Integer, false] readiness bitmask, or false on timeout
|
|
188
|
+
def io_wait(io, events, timeout = nil)
|
|
189
|
+
return poll_io_now(io, events) if timeout == 0
|
|
190
|
+
|
|
191
|
+
# Native io_wait_object handles fileno extraction, Fiber.current,
|
|
192
|
+
# and nil/numeric timeout in Zig — skipping a Ruby frame + branch
|
|
193
|
+
# per call on Net::HTTP's hot read/write loop.
|
|
194
|
+
result = @selector.io_wait_object(io, events, timeout)
|
|
195
|
+
result.nil? ? await_background_operation { io_select_readiness(io, events, timeout) } : result
|
|
196
|
+
rescue NoMethodError, TypeError
|
|
197
|
+
await_background_operation { io_select_readiness(io, events, timeout) }
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Read from an IO into a buffer via the native selector.
|
|
201
|
+
# Falls back to a background thread for non-socket descriptors.
|
|
202
|
+
# @param io [IO]
|
|
203
|
+
# @param buffer [IO::Buffer]
|
|
204
|
+
# @param length [Integer]
|
|
205
|
+
# @param offset [Integer]
|
|
206
|
+
# @return [Integer] bytes read, or negative errno
|
|
207
|
+
def io_read(io, buffer, length, offset = 0)
|
|
208
|
+
# Native io_read_object extracts the descriptor in Zig, skipping a
|
|
209
|
+
# `respond_to?(:fileno)` + `io.fileno` method-send pair per call.
|
|
210
|
+
native_result = @selector.io_read_object(io, buffer, length, offset)
|
|
211
|
+
return native_result unless native_result.nil?
|
|
212
|
+
|
|
213
|
+
await_background_operation do
|
|
214
|
+
Fiber.blocking { buffer.read(io, length, offset) }
|
|
215
|
+
end
|
|
216
|
+
rescue NoMethodError, TypeError
|
|
217
|
+
await_background_operation do
|
|
218
|
+
Fiber.blocking { buffer.read(io, length, offset) }
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Write from a buffer to an IO via the native selector.
|
|
223
|
+
# Falls back to a background thread for non-socket descriptors.
|
|
224
|
+
# @param io [IO]
|
|
225
|
+
# @param buffer [IO::Buffer]
|
|
226
|
+
# @param length [Integer]
|
|
227
|
+
# @param offset [Integer]
|
|
228
|
+
# @return [Integer] bytes written, or negative errno
|
|
229
|
+
def io_write(io, buffer, length, offset = 0)
|
|
230
|
+
native_result = @selector.io_write_object(io, buffer, length, offset)
|
|
231
|
+
return native_result unless native_result.nil?
|
|
232
|
+
|
|
233
|
+
await_background_operation do
|
|
234
|
+
Fiber.blocking { buffer.write(io, length, offset) }
|
|
235
|
+
end
|
|
236
|
+
rescue NoMethodError, TypeError
|
|
237
|
+
await_background_operation do
|
|
238
|
+
Fiber.blocking { buffer.write(io, length, offset) }
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Blocking IO.select on a background thread.
|
|
243
|
+
def io_select(...)
|
|
244
|
+
await_background_operation do
|
|
245
|
+
Fiber.blocking { IO.select(...) }
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Cancel pending waiters on an IO and close the descriptor.
|
|
250
|
+
# @param io [IO]
|
|
251
|
+
def io_close(io)
|
|
252
|
+
descriptor = io.respond_to?(:to_i) ? io.to_i : io
|
|
253
|
+
@selector.io_close(descriptor, IOError.new("stream closed while waiting"))
|
|
254
|
+
|
|
255
|
+
Fiber.blocking do
|
|
256
|
+
target = io.is_a?(IO) ? io : IO.for_fd(descriptor.to_i)
|
|
257
|
+
target.close unless target.closed?
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
true
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Wait for a child process on a background thread.
|
|
264
|
+
# @param pid [Integer]
|
|
265
|
+
# @param flags [Integer] waitpid flags
|
|
266
|
+
# @return [Process::Status]
|
|
267
|
+
def process_wait(pid, flags)
|
|
268
|
+
# Ruby 4.0 bug: rb_process_status_wait re-enters the scheduler hook,
|
|
269
|
+
# so native process_wait produces an incorrect status. Background-thread
|
|
270
|
+
# waitpid avoids this because new threads have no scheduler installed.
|
|
271
|
+
await_background_operation do
|
|
272
|
+
if flags.zero?
|
|
273
|
+
Process::Status.wait(pid, flags)
|
|
274
|
+
else
|
|
275
|
+
_waited_pid, status = Process.waitpid2(pid, flags)
|
|
276
|
+
status
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Resolve a hostname to addresses via Resolv.
|
|
282
|
+
# @param hostname [String]
|
|
283
|
+
# @return [Array<String>]
|
|
284
|
+
def address_resolve(hostname)
|
|
285
|
+
if hostname.include?("%")
|
|
286
|
+
hostname = hostname.split("%", 2).first
|
|
287
|
+
end
|
|
288
|
+
Resolv.getaddresses(hostname)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Run an arbitrary callable on a background thread.
|
|
292
|
+
# @param work [#call]
|
|
293
|
+
def blocking_operation_wait(work)
|
|
294
|
+
await_background_operation do
|
|
295
|
+
work.call
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Deliver an exception to a fiber from another fiber.
|
|
300
|
+
# @param fiber [Fiber]
|
|
301
|
+
# @param exception [Exception]
|
|
302
|
+
def fiber_interrupt(fiber, exception)
|
|
303
|
+
@selector.raise(fiber, exception)
|
|
304
|
+
@selector.wakeup
|
|
305
|
+
true
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Run a block with a timeout, raising an exception if it expires.
|
|
309
|
+
# @param duration [Float] seconds
|
|
310
|
+
# @param klass [Class, Exception] exception class or instance
|
|
311
|
+
# @param message [String]
|
|
312
|
+
def timeout_after(duration, klass = Timeout::Error, message = "execution expired", &block)
|
|
313
|
+
exc = klass.is_a?(Class) ? klass.new(message) : klass
|
|
314
|
+
token = @selector.raise_after(Fiber.current, exc, duration)
|
|
315
|
+
block.call(duration)
|
|
316
|
+
ensure
|
|
317
|
+
@selector.cancel_timer(token) if token
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Run one event loop iteration. Alias for {#select}.
|
|
321
|
+
def run_once(timeout = nil)
|
|
322
|
+
@selector.select(timeout)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Run the event loop until all fibers and background operations complete.
|
|
326
|
+
def run
|
|
327
|
+
Kernel.raise RuntimeError, "Scheduler has been closed" if closed?
|
|
328
|
+
|
|
329
|
+
run_once until idle?
|
|
330
|
+
true
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
private
|
|
334
|
+
|
|
335
|
+
def idle?
|
|
336
|
+
@active_fibers.zero? && @background_count.zero? && !@selector.pending?
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def fiber_done
|
|
340
|
+
@selector.cancel_block_timer(Fiber.current)
|
|
341
|
+
@active_fibers -= 1 if @active_fibers.positive?
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def await_background_operation(&block)
|
|
345
|
+
fiber = Fiber.current
|
|
346
|
+
box = {}
|
|
347
|
+
|
|
348
|
+
Thread.new do
|
|
349
|
+
Thread.current.report_on_exception = false
|
|
350
|
+
|
|
351
|
+
begin
|
|
352
|
+
box[:result] = block.call
|
|
353
|
+
@selector.resume(fiber, true)
|
|
354
|
+
rescue => e
|
|
355
|
+
box[:error] = e
|
|
356
|
+
@selector.resume(fiber, true)
|
|
357
|
+
ensure
|
|
358
|
+
@selector.wakeup
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
@background_count += 1
|
|
363
|
+
@selector.block(fiber, nil)
|
|
364
|
+
|
|
365
|
+
Kernel.raise box[:error] if box[:error]
|
|
366
|
+
|
|
367
|
+
box[:result]
|
|
368
|
+
ensure
|
|
369
|
+
@background_count -= 1 if @background_count.positive?
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def poll_io_now(io, events)
|
|
373
|
+
# Net::HTTP#begin_transport calls `wait_readable(0)` before every
|
|
374
|
+
# keep-alive request to probe for a closed connection. On a healthy
|
|
375
|
+
# connection this is always "not readable", so returning false
|
|
376
|
+
# directly saves one MSG_PEEK recvfrom per request. On a genuinely
|
|
377
|
+
# closed connection Net::HTTP will detect EOF on the next real read
|
|
378
|
+
# and reconnect — one extra request's worth of latency, at most.
|
|
379
|
+
return false if events == IO::READABLE && io.is_a?(BasicSocket)
|
|
380
|
+
|
|
381
|
+
Fiber.blocking { io_select_readiness(io, events, 0) }
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def io_select_readiness(io, events, timeout)
|
|
385
|
+
readers = (events & IO::READABLE).zero? ? nil : [io]
|
|
386
|
+
writers = (events & IO::WRITABLE).zero? ? nil : [io]
|
|
387
|
+
ready = IO.select(readers, writers, nil, timeout)
|
|
388
|
+
return false unless ready
|
|
389
|
+
|
|
390
|
+
readiness = 0
|
|
391
|
+
readiness |= IO::READABLE if ready[0]&.include?(io)
|
|
392
|
+
readiness |= IO::WRITABLE if ready[1]&.include?(io)
|
|
393
|
+
readiness.zero? ? false : readiness
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
end
|
data/lib/carbon_fiber.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: carbon_fiber
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: aarch64-linux
|
|
6
|
+
authors:
|
|
7
|
+
- Yaroslav Markin
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
dependencies:
|
|
11
|
+
- !ruby/object:Gem::Dependency
|
|
12
|
+
name: async
|
|
13
|
+
requirement: !ruby/object:Gem::Requirement
|
|
14
|
+
requirements:
|
|
15
|
+
- - "~>"
|
|
16
|
+
- !ruby/object:Gem::Version
|
|
17
|
+
version: '2.0'
|
|
18
|
+
type: :development
|
|
19
|
+
prerelease: false
|
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
21
|
+
requirements:
|
|
22
|
+
- - "~>"
|
|
23
|
+
- !ruby/object:Gem::Version
|
|
24
|
+
version: '2.0'
|
|
25
|
+
- !ruby/object:Gem::Dependency
|
|
26
|
+
name: rake
|
|
27
|
+
requirement: !ruby/object:Gem::Requirement
|
|
28
|
+
requirements:
|
|
29
|
+
- - "~>"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '13.0'
|
|
32
|
+
type: :development
|
|
33
|
+
prerelease: false
|
|
34
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - "~>"
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '13.0'
|
|
39
|
+
- !ruby/object:Gem::Dependency
|
|
40
|
+
name: rake-compiler-dock
|
|
41
|
+
requirement: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - "~>"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '1.11'
|
|
46
|
+
type: :development
|
|
47
|
+
prerelease: false
|
|
48
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - "~>"
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '1.11'
|
|
53
|
+
- !ruby/object:Gem::Dependency
|
|
54
|
+
name: rspec
|
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - "~>"
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '3.13'
|
|
60
|
+
type: :development
|
|
61
|
+
prerelease: false
|
|
62
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - "~>"
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '3.13'
|
|
67
|
+
- !ruby/object:Gem::Dependency
|
|
68
|
+
name: standard
|
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - "~>"
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '1.0'
|
|
74
|
+
type: :development
|
|
75
|
+
prerelease: false
|
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - "~>"
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '1.0'
|
|
81
|
+
- !ruby/object:Gem::Dependency
|
|
82
|
+
name: yard
|
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - "~>"
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '0.9'
|
|
88
|
+
type: :development
|
|
89
|
+
prerelease: false
|
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - "~>"
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '0.9'
|
|
95
|
+
- !ruby/object:Gem::Dependency
|
|
96
|
+
name: lefthook
|
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
|
98
|
+
requirements:
|
|
99
|
+
- - "~>"
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: 2.1.5
|
|
102
|
+
type: :development
|
|
103
|
+
prerelease: false
|
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
105
|
+
requirements:
|
|
106
|
+
- - "~>"
|
|
107
|
+
- !ruby/object:Gem::Version
|
|
108
|
+
version: 2.1.5
|
|
109
|
+
description: A high-performance Ruby Fiber Scheduler using a Zig native extension
|
|
110
|
+
with libxev (io_uring on Linux, kqueue on macOS). Works as a pure Ruby Fiber Scheduler,
|
|
111
|
+
as well as with the async gem.
|
|
112
|
+
email:
|
|
113
|
+
- yaroslav@markin.net
|
|
114
|
+
executables: []
|
|
115
|
+
extensions: []
|
|
116
|
+
extra_rdoc_files: []
|
|
117
|
+
files:
|
|
118
|
+
- lib/carbon_fiber/async.rb
|
|
119
|
+
- lib/carbon_fiber/native/fallback.rb
|
|
120
|
+
- lib/carbon_fiber/native.rb
|
|
121
|
+
- lib/carbon_fiber/scheduler.rb
|
|
122
|
+
- lib/carbon_fiber/version.rb
|
|
123
|
+
- lib/carbon_fiber.rb
|
|
124
|
+
- lib/carbon_fiber/3.4.0/carbon_fiber_native.so
|
|
125
|
+
- lib/carbon_fiber/4.0.0/carbon_fiber_native.so
|
|
126
|
+
- README.md
|
|
127
|
+
- LICENSE
|
|
128
|
+
homepage: https://github.com/yaroslav/carbon_fiber
|
|
129
|
+
licenses:
|
|
130
|
+
- MIT
|
|
131
|
+
metadata:
|
|
132
|
+
source_code_uri: https://github.com/yaroslav/carbon_fiber
|
|
133
|
+
homepage_uri: https://github.com/yaroslav/carbon_fiber
|
|
134
|
+
changelog_uri: https://github.com/yaroslav/carbon_fiber/blob/main/CHANGELOG.md
|
|
135
|
+
bug_tracker_uri: https://github.com/yaroslav/carbon_fiber/issues
|
|
136
|
+
documentation_uri: https://rubydoc.info/gems/carbon_fiber
|
|
137
|
+
rdoc_options: []
|
|
138
|
+
require_paths:
|
|
139
|
+
- lib
|
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - ">="
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: '3.4'
|
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
146
|
+
requirements:
|
|
147
|
+
- - ">="
|
|
148
|
+
- !ruby/object:Gem::Version
|
|
149
|
+
version: '0'
|
|
150
|
+
requirements: []
|
|
151
|
+
rubygems_version: 4.0.6
|
|
152
|
+
specification_version: 4
|
|
153
|
+
summary: High-performance Ruby Fiber Scheduler backed by Zig with libxev. Pure Ruby
|
|
154
|
+
and gem async.
|
|
155
|
+
test_files: []
|