polyphony 0.66 → 0.70
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -1
- data/Gemfile.lock +1 -1
- data/TODO.md +10 -63
- data/bin/pdbg +112 -0
- data/examples/core/await.rb +9 -1
- data/ext/polyphony/backend_common.c +32 -1
- data/ext/polyphony/backend_common.h +4 -1
- data/ext/polyphony/backend_io_uring.c +17 -5
- data/ext/polyphony/backend_libev.c +16 -0
- data/ext/polyphony/fiber.c +20 -0
- data/ext/polyphony/polyphony.c +2 -0
- data/ext/polyphony/polyphony.h +5 -2
- data/ext/polyphony/runqueue.c +4 -0
- data/ext/polyphony/runqueue.h +1 -0
- data/ext/polyphony/runqueue_ring_buffer.c +25 -14
- data/ext/polyphony/runqueue_ring_buffer.h +2 -0
- data/ext/polyphony/thread.c +2 -8
- data/lib/polyphony.rb +6 -0
- data/lib/polyphony/debugger.rb +225 -0
- data/lib/polyphony/extensions/debug.rb +1 -1
- data/lib/polyphony/extensions/fiber.rb +66 -69
- data/lib/polyphony/extensions/io.rb +1 -3
- data/lib/polyphony/extensions/openssl.rb +63 -1
- data/lib/polyphony/extensions/socket.rb +3 -3
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +4 -5
- data/test/stress.rb +6 -2
- data/test/test_fiber.rb +30 -11
- data/test/test_process_supervision.rb +38 -9
- data/test/test_supervise.rb +183 -100
- data/test/test_thread_pool.rb +1 -1
- data/test/test_throttler.rb +2 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65be64df5933c24a4afb38b24c139213dcc71a7fbde7689c46c862d7bb04eef3
|
4
|
+
data.tar.gz: 3feec6f48ea17b15468790c91a790438d163b1d905d5ef6c7a2ffd6d3e47bac8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3417d863dcd7e99c3123fbc81923627a77c6a5b45ed20d6e84148eabb945ecb38f429873be01fc53d5c35f36a225045c42e51b2c36f543fb3539ebc7d91f8bcf
|
7
|
+
data.tar.gz: be39ff1ec9616998fc2a5df0bd9af368687e3424cf4b97776466e803624953440bbf75fc166cd273f8f3d4502eefee4a13283c4129c3ed02113cc2015d314428
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
## 0.70 2021-08-19
|
2
|
+
|
3
|
+
- New implementation for `#supervise`
|
4
|
+
- Reset backend state and runqueue on fork
|
5
|
+
|
6
|
+
## 0.69 2021-08-16
|
7
|
+
|
8
|
+
- Rename `#__polyphony_read_method__` to `#__parser_read_method__`
|
9
|
+
|
10
|
+
## 0.68 2021-08-13
|
11
|
+
|
12
|
+
- Fix missing default value in socket classes' `#readpartial`
|
13
|
+
- Fix linking of operations in `Backend#chain` (io_uring version)
|
14
|
+
- Rename `Fiber#attach` to `Fiber#attach_to`
|
15
|
+
- Expose original `SSLServer#accept`
|
16
|
+
|
17
|
+
## 0.67 2021-08-06
|
18
|
+
|
19
|
+
- Improve fiber monitoring
|
20
|
+
- Add fiber parking (a parked fiber is prevented from running). This is in
|
21
|
+
preparation for the upcoming work on an integrated debugger.
|
22
|
+
|
1
23
|
## 0.66 2021-08-01
|
2
24
|
|
3
25
|
- Fix all splicing APIs on non-linux OSes (#63)
|
@@ -5,7 +27,7 @@
|
|
5
27
|
|
6
28
|
## 0.65 2021-07-29
|
7
29
|
|
8
|
-
- Add `#
|
30
|
+
- Add `#__parser_read_method__` method for read method detection
|
9
31
|
|
10
32
|
## 0.64 2021-07-26
|
11
33
|
|
data/Gemfile.lock
CHANGED
data/TODO.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
- io_uring backend:
|
2
|
+
- if `io_uring_get_sqe` returns null, call `io_uring_submit`, (snooze fiber)?
|
3
|
+
and try again
|
4
|
+
|
5
|
+
- Tracing:
|
6
|
+
- Emit events on I/O ops, e.g.:
|
7
|
+
- [:op_read_submit, id, io, len]
|
8
|
+
- [:op_read_complete, id, io, len, buffer]
|
9
|
+
- Prevent tracing while an event is being emitted (to allow the trace proc to perform I/O)
|
10
|
+
|
1
11
|
- Add support for IPv6:
|
2
12
|
https://www.reddit.com/r/ruby/comments/lyen23/understanding_ipv6_and_why_its_important_to_you/
|
3
13
|
|
@@ -14,72 +24,10 @@
|
|
14
24
|
- `IO#gets_loop`, `Socket#gets_loop`, `OpenSSL::Socket#gets_loop` (medium effort)
|
15
25
|
- `Fiber#receive_loop` (very little effort, should be implemented in C)
|
16
26
|
|
17
|
-
|
18
|
-
- Add `Backend#splice`, `Backend#splice_to_eof` for implementing stuff like proxying:
|
19
|
-
|
20
|
-
```ruby
|
21
|
-
def two_way_proxy(socket1, socket2)
|
22
|
-
backend = Thread.current.backend
|
23
|
-
f1 = spin { backend.splice_to_eof(socket1, socket2) }
|
24
|
-
f2 = spin { backend.splice_to_eof(socket2, socket1) }
|
25
|
-
Fiber.await(f1, f2)
|
26
|
-
end
|
27
|
-
```
|
28
|
-
|
29
27
|
- Add support for `close` to io_uring backend
|
30
28
|
|
31
|
-
- Add support for submission of multiple requests to io_uring backend:
|
32
|
-
|
33
|
-
```ruby
|
34
|
-
Thread.current.backend.submit(
|
35
|
-
[:send, sock, chunk_header(len)],
|
36
|
-
[:splice, file, sock, len]
|
37
|
-
)
|
38
|
-
```
|
39
|
-
|
40
|
-
Full example (for writing chunks from a file to an HTTP response):
|
41
|
-
|
42
|
-
```ruby
|
43
|
-
def serve_io(io)
|
44
|
-
i, o = IO.pipe
|
45
|
-
backend = Thread.current.backend
|
46
|
-
while true
|
47
|
-
len = o.splice(io, 8192)
|
48
|
-
break if len == 0
|
49
|
-
|
50
|
-
backend.submit(
|
51
|
-
[:write, sock, chunk_header(len)],
|
52
|
-
[:splice, i, sock, len]
|
53
|
-
)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
```
|
57
|
-
|
58
|
-
- Graceful shutdown again:
|
59
|
-
- What happens to children when doing a graceful shutdown?
|
60
|
-
- What are the implications of passing graceful shutdown flag to children?
|
61
|
-
- What about errors while doing a graceful shutdown?
|
62
|
-
- What about graceful restarts?
|
63
|
-
- Some interesting discussions:
|
64
|
-
- https://trio.discourse.group/search?q=graceful%20shutdown
|
65
|
-
- https://github.com/python-trio/trio/issues/147
|
66
|
-
- https://github.com/python-trio/trio/issues/143
|
67
|
-
- https://trio.discourse.group/t/graceful-shutdown/93/2
|
68
|
-
- https://250bpm.com/blog:146/
|
69
|
-
- https://www.rodrigoaraujo.me/posts/golang-pattern-graceful-shutdown-of-concurrent-events/
|
70
|
-
- https://github.com/tj/go-gracefully
|
71
|
-
- `Fiber#finalize_children` should pass graceful shutdown flag to children
|
72
|
-
- A good use case is an HTTP server that on graceful shutdown:
|
73
|
-
- stops listening
|
74
|
-
- waits for all ongoing requests to finish, optionally with a timeout
|
75
|
-
|
76
29
|
## Roadmap for Polyphony 1.0
|
77
30
|
|
78
|
-
- check integration with rb-inotify
|
79
|
-
|
80
|
-
- Improve `#supervise`. It does not work as advertised, and seems to exhibit an
|
81
|
-
inconsistent behaviour (see supervisor example).
|
82
|
-
|
83
31
|
- Add test that mimics the original design for Monocrono:
|
84
32
|
- 256 fibers each waiting for a message
|
85
33
|
- When message received do some blocking work using a `ThreadPool`
|
@@ -92,7 +40,6 @@
|
|
92
40
|
|
93
41
|
-----------------------------------------------------
|
94
42
|
|
95
|
-
- Add `Backend#splice(in, out, nbytes)` API
|
96
43
|
- Adapter for io/console (what does `IO#raw` do?)
|
97
44
|
- Adapter for Pry and IRB (Which fixes #5 and #6)
|
98
45
|
- allow backend selection at runtime
|
data/bin/pdbg
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'polyphony'
|
6
|
+
|
7
|
+
UNIX_SOCKET_PATH = '/tmp/pdbg.sock'
|
8
|
+
|
9
|
+
cmd = ARGV.join(' ')
|
10
|
+
injected_lib_path = File.expand_path('../lib/polyphony/debugger/server_inject.rb', __dir__)
|
11
|
+
pid = fork { exec("env POLYPHONY_DEBUG_SOCKET_PATH=#{UNIX_SOCKET_PATH} ruby #{cmd}") }
|
12
|
+
puts "Started debugged process (#{pid})"
|
13
|
+
|
14
|
+
socket = nil
|
15
|
+
while !socket
|
16
|
+
socket = UNIXSocket.new(UNIX_SOCKET_PATH) rescue nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_command(cmd)
|
20
|
+
case cmd
|
21
|
+
when /^(step|s)$/
|
22
|
+
{ cmd: :step }
|
23
|
+
when /^(state|st)$/
|
24
|
+
{ cmd: :state }
|
25
|
+
when /^(help|h)$/
|
26
|
+
{ cmd: :help }
|
27
|
+
when /^(list|l)$/
|
28
|
+
{ cmd: :list }
|
29
|
+
else
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def display_info(info)
|
35
|
+
info = eval(info)
|
36
|
+
case (info && info[:kind])
|
37
|
+
when :listing
|
38
|
+
print_listing(info)
|
39
|
+
when :state
|
40
|
+
print_state(info)
|
41
|
+
else
|
42
|
+
p info
|
43
|
+
end
|
44
|
+
rescue SyntaxError
|
45
|
+
puts "Failed to eval:"
|
46
|
+
p info
|
47
|
+
end
|
48
|
+
|
49
|
+
FILE_LINES_CACHE = {}
|
50
|
+
|
51
|
+
def self.get_snippet(path, lineno)
|
52
|
+
lines = FILE_LINES_CACHE[path] ||= IO.read(path).lines
|
53
|
+
start_idx = lineno - 5
|
54
|
+
stop_idx = lineno + 3
|
55
|
+
stop_idx = lines.size - 1 if stop_idx >= lines.size
|
56
|
+
start_idx = 0 if start_idx < 0
|
57
|
+
(start_idx..stop_idx).map { |idx| [idx + 1, lines[idx]]}
|
58
|
+
end
|
59
|
+
|
60
|
+
def print_snippet(info, snippet, cur_line)
|
61
|
+
places = FILE_LINES_CACHE[info[:path]].size.to_s.size
|
62
|
+
snippet.each do |(lineno, line)|
|
63
|
+
is_cur = lineno == cur_line
|
64
|
+
formatted = format("%s% #{places}d %s", is_cur ? '=> ' : ' ', lineno, line)
|
65
|
+
puts formatted
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def print_listing(info)
|
70
|
+
snippet = get_snippet(info[:path], info[:lineno])
|
71
|
+
puts "Fiber: #{info[:fiber]} Location: #{info[:path]}:#{info[:lineno]}"
|
72
|
+
puts
|
73
|
+
print_snippet(info, snippet, info[:lineno])
|
74
|
+
puts
|
75
|
+
end
|
76
|
+
|
77
|
+
def print_help
|
78
|
+
puts
|
79
|
+
puts "Here's some help..."
|
80
|
+
puts
|
81
|
+
end
|
82
|
+
|
83
|
+
def print_state(info)
|
84
|
+
p info
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_user_cmd
|
88
|
+
while true
|
89
|
+
STDOUT << "(pdbg) "
|
90
|
+
cmd = parse_command(STDIN.gets)
|
91
|
+
next unless cmd
|
92
|
+
|
93
|
+
if cmd[:cmd] == :help
|
94
|
+
print_help
|
95
|
+
else
|
96
|
+
return cmd if cmd
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
socket.puts 'pdbg'
|
102
|
+
response = socket.gets
|
103
|
+
if response.chomp == 'pdbg'
|
104
|
+
puts 'Connected to process'
|
105
|
+
end
|
106
|
+
loop do
|
107
|
+
info = socket.gets.chomp
|
108
|
+
display_info(info)
|
109
|
+
|
110
|
+
cmd = get_user_cmd
|
111
|
+
socket.puts cmd.inspect
|
112
|
+
end
|
data/examples/core/await.rb
CHANGED
@@ -2,7 +2,11 @@
|
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
4
|
require 'polyphony'
|
5
|
+
require 'polyphony/extensions/debug'
|
5
6
|
|
7
|
+
Exception.__disable_sanitized_backtrace__ = true
|
8
|
+
|
9
|
+
puts '----- start await example ------'
|
6
10
|
sleeper = spin do
|
7
11
|
puts 'going to sleep'
|
8
12
|
sleep 1
|
@@ -17,4 +21,8 @@ waiter = spin do
|
|
17
21
|
puts 'done waiting'
|
18
22
|
end
|
19
23
|
|
20
|
-
|
24
|
+
trace :before_await
|
25
|
+
|
26
|
+
sleep 2
|
27
|
+
waiter.await
|
28
|
+
trace :after_await
|
@@ -7,6 +7,7 @@
|
|
7
7
|
|
8
8
|
inline void backend_base_initialize(struct Backend_base *base) {
|
9
9
|
runqueue_initialize(&base->runqueue);
|
10
|
+
runqueue_initialize(&base->parked_runqueue);
|
10
11
|
base->currently_polling = 0;
|
11
12
|
base->op_count = 0;
|
12
13
|
base->switch_count = 0;
|
@@ -20,12 +21,32 @@ inline void backend_base_initialize(struct Backend_base *base) {
|
|
20
21
|
|
21
22
|
inline void backend_base_finalize(struct Backend_base *base) {
|
22
23
|
runqueue_finalize(&base->runqueue);
|
24
|
+
runqueue_finalize(&base->parked_runqueue);
|
23
25
|
}
|
24
26
|
|
25
27
|
inline void backend_base_mark(struct Backend_base *base) {
|
26
28
|
if (base->idle_proc != Qnil) rb_gc_mark(base->idle_proc);
|
27
29
|
if (base->trace_proc != Qnil) rb_gc_mark(base->trace_proc);
|
28
30
|
runqueue_mark(&base->runqueue);
|
31
|
+
runqueue_mark(&base->parked_runqueue);
|
32
|
+
}
|
33
|
+
|
34
|
+
void backend_base_reset(struct Backend_base *base) {
|
35
|
+
runqueue_finalize(&base->runqueue);
|
36
|
+
runqueue_finalize(&base->parked_runqueue);
|
37
|
+
|
38
|
+
runqueue_initialize(&base->runqueue);
|
39
|
+
runqueue_initialize(&base->parked_runqueue);
|
40
|
+
|
41
|
+
base->currently_polling = 0;
|
42
|
+
base->op_count = 0;
|
43
|
+
base->switch_count = 0;
|
44
|
+
base->poll_count = 0;
|
45
|
+
base->pending_count = 0;
|
46
|
+
base->idle_gc_period = 0;
|
47
|
+
base->idle_gc_last_time = 0;
|
48
|
+
base->idle_proc = Qnil;
|
49
|
+
base->trace_proc = Qnil;
|
29
50
|
}
|
30
51
|
|
31
52
|
const unsigned int ANTI_STARVE_SWITCH_COUNT_THRESHOLD = 64;
|
@@ -91,7 +112,10 @@ void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_bas
|
|
91
112
|
|
92
113
|
COND_TRACE(base, 4, SYM_fiber_schedule, fiber, value, prioritize ? Qtrue : Qfalse);
|
93
114
|
|
94
|
-
|
115
|
+
runqueue_t *runqueue = rb_ivar_get(fiber, ID_ivar_parked) == Qtrue ?
|
116
|
+
&base->parked_runqueue : &base->runqueue;
|
117
|
+
|
118
|
+
(prioritize ? runqueue_unshift : runqueue_push)(runqueue, fiber, value, already_runnable);
|
95
119
|
if (!already_runnable) {
|
96
120
|
rb_ivar_set(fiber, ID_ivar_runnable, Qtrue);
|
97
121
|
if (rb_thread_current() != thread) {
|
@@ -105,6 +129,13 @@ void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_bas
|
|
105
129
|
}
|
106
130
|
}
|
107
131
|
|
132
|
+
inline void backend_base_park_fiber(struct Backend_base *base, VALUE fiber) {
|
133
|
+
runqueue_migrate(&base->runqueue, &base->parked_runqueue, fiber);
|
134
|
+
}
|
135
|
+
|
136
|
+
inline void backend_base_unpark_fiber(struct Backend_base *base, VALUE fiber) {
|
137
|
+
runqueue_migrate(&base->parked_runqueue, &base->runqueue, fiber);
|
138
|
+
}
|
108
139
|
|
109
140
|
inline void backend_trace(struct Backend_base *base, int argc, VALUE *argv) {
|
110
141
|
if (base->trace_proc == Qnil) return;
|
@@ -17,6 +17,7 @@ struct backend_stats {
|
|
17
17
|
|
18
18
|
struct Backend_base {
|
19
19
|
runqueue_t runqueue;
|
20
|
+
runqueue_t parked_runqueue;
|
20
21
|
unsigned int currently_polling;
|
21
22
|
unsigned int op_count;
|
22
23
|
unsigned int switch_count;
|
@@ -31,8 +32,11 @@ struct Backend_base {
|
|
31
32
|
void backend_base_initialize(struct Backend_base *base);
|
32
33
|
void backend_base_finalize(struct Backend_base *base);
|
33
34
|
void backend_base_mark(struct Backend_base *base);
|
35
|
+
void backend_base_reset(struct Backend_base *base);
|
34
36
|
VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base);
|
35
37
|
void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_base *base, VALUE fiber, VALUE value, int prioritize);
|
38
|
+
void backend_base_park_fiber(struct Backend_base *base, VALUE fiber);
|
39
|
+
void backend_base_unpark_fiber(struct Backend_base *base, VALUE fiber);
|
36
40
|
void backend_trace(struct Backend_base *base, int argc, VALUE *argv);
|
37
41
|
struct backend_stats backend_base_stats(struct Backend_base *base);
|
38
42
|
|
@@ -104,7 +108,6 @@ VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
|
|
104
108
|
VALUE Backend_stats(VALUE self);
|
105
109
|
void backend_run_idle_tasks(struct Backend_base *base);
|
106
110
|
void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking);
|
107
|
-
|
108
111
|
void backend_setup_stats_symbols();
|
109
112
|
|
110
113
|
#endif /* BACKEND_COMMON_H */
|
@@ -106,9 +106,7 @@ VALUE Backend_post_fork(VALUE self) {
|
|
106
106
|
io_uring_queue_exit(&backend->ring);
|
107
107
|
io_uring_queue_init(backend->prepared_limit, &backend->ring, 0);
|
108
108
|
context_store_free(&backend->store);
|
109
|
-
backend->base
|
110
|
-
backend->base.pending_count = 0;
|
111
|
-
backend->pending_sqes = 0;
|
109
|
+
backend_base_reset(&backend->base);
|
112
110
|
|
113
111
|
return self;
|
114
112
|
}
|
@@ -229,7 +227,7 @@ inline void Backend_unschedule_fiber(VALUE self, VALUE fiber) {
|
|
229
227
|
Backend_t *backend;
|
230
228
|
GetBackend(self, backend);
|
231
229
|
|
232
|
-
runqueue_delete(&backend->base.runqueue, fiber);
|
230
|
+
runqueue_delete(&backend->base.runqueue, fiber);
|
233
231
|
}
|
234
232
|
|
235
233
|
inline VALUE Backend_switch_fiber(VALUE self) {
|
@@ -1248,7 +1246,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
|
1248
1246
|
}
|
1249
1247
|
|
1250
1248
|
io_uring_sqe_set_data(last_sqe, ctx);
|
1251
|
-
unsigned int flags = (i == argc - 1) ? IOSQE_ASYNC : IOSQE_ASYNC
|
1249
|
+
unsigned int flags = (i == (argc - 1)) ? IOSQE_ASYNC : IOSQE_ASYNC | IOSQE_IO_LINK;
|
1252
1250
|
io_uring_sqe_set_flags(last_sqe, flags);
|
1253
1251
|
sqe_count++;
|
1254
1252
|
}
|
@@ -1474,6 +1472,20 @@ VALUE Backend_trace_proc_set(VALUE self, VALUE block) {
|
|
1474
1472
|
return self;
|
1475
1473
|
}
|
1476
1474
|
|
1475
|
+
void Backend_park_fiber(VALUE self, VALUE fiber) {
|
1476
|
+
Backend_t *backend;
|
1477
|
+
GetBackend(self, backend);
|
1478
|
+
|
1479
|
+
backend_base_park_fiber(&backend->base, fiber);
|
1480
|
+
}
|
1481
|
+
|
1482
|
+
void Backend_unpark_fiber(VALUE self, VALUE fiber) {
|
1483
|
+
Backend_t *backend;
|
1484
|
+
GetBackend(self, backend);
|
1485
|
+
|
1486
|
+
backend_base_unpark_fiber(&backend->base, fiber);
|
1487
|
+
}
|
1488
|
+
|
1477
1489
|
void Init_Backend() {
|
1478
1490
|
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
|
1479
1491
|
rb_define_alloc_func(cBackend, Backend_allocate);
|
@@ -157,6 +157,8 @@ VALUE Backend_post_fork(VALUE self) {
|
|
157
157
|
ev_loop_destroy(backend->ev_loop);
|
158
158
|
backend->ev_loop = EV_DEFAULT;
|
159
159
|
|
160
|
+
backend_base_reset(&backend->base);
|
161
|
+
|
160
162
|
return self;
|
161
163
|
}
|
162
164
|
|
@@ -1553,6 +1555,20 @@ VALUE Backend_trace_proc_set(VALUE self, VALUE block) {
|
|
1553
1555
|
return self;
|
1554
1556
|
}
|
1555
1557
|
|
1558
|
+
void Backend_park_fiber(VALUE self, VALUE fiber) {
|
1559
|
+
Backend_t *backend;
|
1560
|
+
GetBackend(self, backend);
|
1561
|
+
|
1562
|
+
backend_base_park_fiber(&backend->base, fiber);
|
1563
|
+
}
|
1564
|
+
|
1565
|
+
void Backend_unpark_fiber(VALUE self, VALUE fiber) {
|
1566
|
+
Backend_t *backend;
|
1567
|
+
GetBackend(self, backend);
|
1568
|
+
|
1569
|
+
backend_base_unpark_fiber(&backend->base, fiber);
|
1570
|
+
}
|
1571
|
+
|
1556
1572
|
void Init_Backend() {
|
1557
1573
|
ev_set_allocator(xrealloc);
|
1558
1574
|
|