polyphony 0.54.0 → 0.59
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/CHANGELOG.md +54 -25
- data/Gemfile.lock +1 -1
- data/TODO.md +0 -3
- data/examples/core/idle_gc.rb +21 -0
- data/examples/io/pipe.rb +11 -0
- data/examples/io/splice_chunks.rb +29 -0
- data/examples/io/stdio.rb +8 -0
- data/ext/polyphony/backend_common.c +288 -0
- data/ext/polyphony/backend_common.h +49 -130
- data/ext/polyphony/backend_io_uring.c +439 -122
- data/ext/polyphony/backend_io_uring_context.c +14 -3
- data/ext/polyphony/backend_io_uring_context.h +11 -11
- data/ext/polyphony/backend_libev.c +463 -94
- data/ext/polyphony/fiber.c +0 -2
- data/ext/polyphony/polyphony.c +17 -22
- data/ext/polyphony/polyphony.h +9 -16
- data/ext/polyphony/polyphony_ext.c +0 -4
- data/ext/polyphony/runqueue.c +35 -72
- data/ext/polyphony/runqueue.h +27 -0
- data/ext/polyphony/thread.c +10 -84
- data/lib/polyphony/extensions/fiber.rb +2 -2
- data/lib/polyphony/extensions/socket.rb +6 -20
- data/lib/polyphony/extensions/thread.rb +8 -0
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +3 -3
- data/test/test_backend.rb +137 -2
- data/test/test_fiber.rb +0 -1
- data/test/test_io.rb +6 -3
- data/test/test_signal.rb +1 -1
- data/test/test_thread.rb +57 -11
- data/test/test_thread_pool.rb +1 -1
- data/test/test_timer.rb +16 -10
- data/test/test_trace.rb +27 -49
- metadata +8 -4
- data/ext/polyphony/tracing.c +0 -11
- data/lib/polyphony/adapters/trace.rb +0 -138
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 313e33321df0e375d128a79fccfab98e06183adbdbde7916347f6795f56ca369
|
4
|
+
data.tar.gz: 6428c0516544bdb955fa465bf36c40b9b2f162306f3e5689848ff487f24c4440
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 40c88f5d1c2aa4307dea78dcf1a2ba8d347ef252e7e56e67d466c291066d39544d051864ccd0138dd94ca0beef7af71f5257052b3556ab0c7294b1b94b517d47
|
7
|
+
data.tar.gz: 109410d6b222846b0c2b3f54061358ef922b57c9b6559c37aec36f27116e7ca389ff51762e988e2e9293a7a1358d97b80ada74daa4e44d7d25c1e5335351d8c6
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,33 @@
|
|
1
|
-
## 0.
|
1
|
+
## 0.59 2021-06-28
|
2
|
+
|
3
|
+
- Redesign tracing mechanism and API - now completely separated from Ruby core
|
4
|
+
trace API
|
5
|
+
- Refactor C code - move run queue into backend
|
6
|
+
|
7
|
+
## 0.58 2021-06-25
|
8
|
+
|
9
|
+
- Implement `Thread#idle_gc_period`, `#on_idle` (#56)
|
10
|
+
- Implement `Backend#idle_block=` (#56)
|
11
|
+
|
12
|
+
## 0.57.0 2021-06-23
|
13
|
+
|
14
|
+
- Implement `Backend#splice_chunks` method for both libev and io_uring backends
|
15
|
+
- Improve waiting for readiness in libev `Backend#splice`, `#splice_to_eof`
|
16
|
+
- Enable splice op in libev `Backend#chain` for non-Linux OS
|
17
|
+
|
18
|
+
## 0.56.0 2021-06-22
|
19
|
+
|
20
|
+
- Implement fake `Backend#splice`, `Backend#splice_to_eof` methods for non-Linux
|
21
|
+
OS
|
22
|
+
|
23
|
+
## 0.55.0 2021-06-17
|
24
|
+
|
25
|
+
- Finish io_uring implementation of Backend#chain
|
26
|
+
- Reimplement io_uring op_context acquire/release algorithm (using ref count)
|
27
|
+
- Fix #gets on sockets
|
28
|
+
- Redesign event anti-starvation mechanism
|
29
|
+
|
30
|
+
## 0.54.0 2021-06-14
|
2
31
|
|
3
32
|
- Implement Mutex#owned?, #locked? (#50)
|
4
33
|
- Fix arity for SSLSocket#peeraddr (#55)
|
@@ -6,15 +35,15 @@
|
|
6
35
|
- Fix SSLSocket buffering behaviour
|
7
36
|
- Add recv_loop alias for SSLSocket (#54)
|
8
37
|
|
9
|
-
## 0.53.2
|
38
|
+
## 0.53.2 2021-05-10
|
10
39
|
|
11
40
|
- Remove `splice` methods on libev backend on non-Linux OS (#43)
|
12
41
|
|
13
|
-
## 0.53.0
|
42
|
+
## 0.53.0 2021-04-23
|
14
43
|
|
15
44
|
- Implement `Backend#splice`, `Backend#splice_to_eof`, along with `IO#splice`, `IO#splice_to_eof`
|
16
45
|
|
17
|
-
## 0.52.0
|
46
|
+
## 0.52.0 2021-02-28
|
18
47
|
|
19
48
|
- Polyphony is now compatible with Ruby 3.0
|
20
49
|
- Add `Backend#sendv` method for sending multiple strings
|
@@ -24,19 +53,19 @@
|
|
24
53
|
- libev backend: Use` pidfd_open` for Linux 5.3+, otherwise use a libev child watcher
|
25
54
|
- Use `:call` as default method in `#feed_loop`
|
26
55
|
|
27
|
-
## 0.51.0
|
56
|
+
## 0.51.0 2021-02-02
|
28
57
|
|
29
58
|
- Implement `IO#feed_loop`, `Socket#feed_loop`
|
30
59
|
- Fix error handling in `Process.kill_and_await`
|
31
60
|
|
32
|
-
## 0.50.1
|
61
|
+
## 0.50.1 2021-01-31
|
33
62
|
|
34
63
|
- Set `IOSQE_ASYNC` flag in io_uring backend
|
35
64
|
- Fix error handling in `Backend#waitpid`
|
36
65
|
- Reimplement libev backend's `#waitpid` by using pidfd_open (in similar manner
|
37
66
|
to the io_uring backend)
|
38
67
|
|
39
|
-
## 0.50.0
|
68
|
+
## 0.50.0 2021-01-28
|
40
69
|
|
41
70
|
- Use `Process::CLOCK_MONOTONIC` in Timer
|
42
71
|
- Add `Timer#sleep`, `Timer#after`, `Timer#every`
|
@@ -44,50 +73,50 @@
|
|
44
73
|
- Add `Thread#fiber_index_of` method
|
45
74
|
- Use `Backend#wait_event` in `Fiber#await`
|
46
75
|
|
47
|
-
## 0.49.2
|
76
|
+
## 0.49.2 2021-01-19
|
48
77
|
|
49
78
|
- Fix hang with 100s or more child fibers when terminating
|
50
79
|
- Fix double pending_count increment in io_uring backend
|
51
80
|
|
52
|
-
## 0.49.1
|
81
|
+
## 0.49.1 2021-01-13
|
53
82
|
|
54
83
|
- Use `TCPSocket` instead of `Socket` in `Net.tcp_connect`
|
55
84
|
- Catch `Errno::ERSCH` in `Process.kill_and_await`
|
56
85
|
- Set io_uring queue size to 2048
|
57
86
|
|
58
|
-
## 0.49.0
|
87
|
+
## 0.49.0 2021-01-11
|
59
88
|
|
60
89
|
- Implement `Polyphony::Timer` for performant timeouts
|
61
90
|
|
62
|
-
## 0.48.0
|
91
|
+
## 0.48.0 2021-01-05
|
63
92
|
|
64
93
|
- Implement graceful shutdown
|
65
94
|
- Add support for `break` / `StopIteration` in `spin_loop`
|
66
95
|
- Fix `IO#gets`, `IO#readpartial`
|
67
96
|
|
68
|
-
## 0.47.5.1
|
97
|
+
## 0.47.5.1 2020-11-20
|
69
98
|
|
70
99
|
- Add missing `Socket#accept_loop` method
|
71
100
|
|
72
|
-
## 0.47.5
|
101
|
+
## 0.47.5 2020-11-20
|
73
102
|
|
74
103
|
- Add `socket_class` argument to `Backend#accept`, `Backend#accept_loop`
|
75
104
|
- Fix `#supervise` to stop when all children fibers are done
|
76
105
|
|
77
|
-
## 0.47.4
|
106
|
+
## 0.47.4 2020-11-14
|
78
107
|
|
79
108
|
- Add support for Unix sockets
|
80
109
|
|
81
|
-
## 0.47.3
|
110
|
+
## 0.47.3 2020-11-12
|
82
111
|
|
83
112
|
- Enable I/O in signal handlers (#45)
|
84
113
|
- Accept `:interval` argument in `#spin_loop`
|
85
114
|
|
86
|
-
## 0.47.2
|
115
|
+
## 0.47.2 2020-11-10
|
87
116
|
|
88
117
|
- Fix API compatibility between TCPSocket and IO
|
89
118
|
|
90
|
-
## 0.47.0
|
119
|
+
## 0.47.0 2020-11-10
|
91
120
|
|
92
121
|
- Implement `#spin_scope` used for creating blocking fiber scopes
|
93
122
|
- Reimplement `move_on_after`, `cancel_after`, `Timeout.timeout` using
|
@@ -95,18 +124,18 @@
|
|
95
124
|
- Implement `Backend#timeout` API
|
96
125
|
- Implemented capped queues
|
97
126
|
|
98
|
-
## 0.46.1
|
127
|
+
## 0.46.1 2020-11-04
|
99
128
|
|
100
129
|
- Add `TCPServer#accept_loop`, `OpenSSL::SSL::SSLSocket#accept_loop` method
|
101
130
|
- Fix compilation error on MacOS (#43)
|
102
131
|
- Fix backtrace for `Timeout.timeout`
|
103
132
|
- Add `Backend#timer_loop`
|
104
133
|
|
105
|
-
## 0.46.0
|
134
|
+
## 0.46.0 2020-10-08
|
106
135
|
|
107
136
|
- Implement [io_uring backend](https://github.com/digital-fabric/polyphony/pull/44)
|
108
137
|
|
109
|
-
## 0.45.5
|
138
|
+
## 0.45.5 2020-10-04
|
110
139
|
|
111
140
|
- Fix compilation error (#43)
|
112
141
|
- Add support for resetting move_on_after, cancel_after timeouts
|
@@ -115,22 +144,22 @@
|
|
115
144
|
- Schedule parent with priority on uncaught exception
|
116
145
|
- Fix race condition in `Mutex#synchronize` (#41)
|
117
146
|
|
118
|
-
## 0.45.4
|
147
|
+
## 0.45.4 2020-09-06
|
119
148
|
|
120
149
|
- Improve signal trapping mechanism
|
121
150
|
|
122
|
-
## 0.45.3
|
151
|
+
## 0.45.3 2020-09-02
|
123
152
|
|
124
153
|
- Don't swallow error in `Process#kill_and_await`
|
125
154
|
- Add `Fiber#mailbox` attribute reader
|
126
155
|
- Fix bug in `Fiber.await`
|
127
156
|
- Implement `IO#getc`, `IO#getbyte`
|
128
157
|
|
129
|
-
## 0.45.2
|
158
|
+
## 0.45.2 2020-08-03
|
130
159
|
|
131
160
|
- Rewrite `Fiber#<<`, `Fiber#await`, `Fiber#receive` in C
|
132
161
|
|
133
|
-
## 0.45.1
|
162
|
+
## 0.45.1 2020-08-01
|
134
163
|
|
135
164
|
- Fix Net::HTTP compatibility
|
136
165
|
- Fix fs adapter
|
@@ -140,7 +169,7 @@
|
|
140
169
|
- Cleanup code
|
141
170
|
- Improve support for Ruby 3 keyword args
|
142
171
|
|
143
|
-
## 0.45.0
|
172
|
+
## 0.45.0 2020-07-29
|
144
173
|
|
145
174
|
- Cleanup code
|
146
175
|
- Rename `Agent` to `Backend`
|
data/Gemfile.lock
CHANGED
data/TODO.md
CHANGED
@@ -10,9 +10,6 @@
|
|
10
10
|
|
11
11
|
- Add support for `break` and `StopIteration` in all loops (with tests)
|
12
12
|
|
13
|
-
- Change `IO#gets` to use `String#split` to cut into lines, much faster (see
|
14
|
-
examples/performance/line_splitting.rb)
|
15
|
-
|
16
13
|
- More tight loops
|
17
14
|
- `IO#gets_loop`, `Socket#gets_loop`, `OpenSSL::Socket#gets_loop` (medium effort)
|
18
15
|
- `Fiber#receive_loop` (very little effort, should be implemented in C)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
|
5
|
+
require 'polyphony'
|
6
|
+
|
7
|
+
GC.disable
|
8
|
+
|
9
|
+
p count: GC.count
|
10
|
+
snooze
|
11
|
+
p count_after_snooze: GC.count
|
12
|
+
sleep 0.1
|
13
|
+
p count_after_sleep: GC.count
|
14
|
+
|
15
|
+
Thread.current.backend.idle_gc_period = 60
|
16
|
+
|
17
|
+
p count: GC.count
|
18
|
+
snooze
|
19
|
+
p count_after_snooze: GC.count
|
20
|
+
sleep 0.1
|
21
|
+
p count_after_sleep: GC.count
|
data/examples/io/pipe.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
i, o = IO.pipe
|
7
|
+
|
8
|
+
f = spin do
|
9
|
+
i.read_loop { |data| STDOUT << data }
|
10
|
+
end
|
11
|
+
|
12
|
+
result = nil
|
13
|
+
# File.open(__FILE__, 'r') do |f|
|
14
|
+
File.open('../tipi/log', 'r') do |f|
|
15
|
+
result = Thread.current.backend.splice_chunks(
|
16
|
+
f,
|
17
|
+
o,
|
18
|
+
"Content-Type: ruby\n\n",
|
19
|
+
"0\r\n\r\n",
|
20
|
+
->(len) { "#{len.to_s(16)}\r\n" },
|
21
|
+
"\r\n",
|
22
|
+
16384
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
o.close
|
28
|
+
f.await
|
29
|
+
p result: result
|
@@ -0,0 +1,288 @@
|
|
1
|
+
#include <time.h>
|
2
|
+
#include <fcntl.h>
|
3
|
+
#include "ruby.h"
|
4
|
+
#include "ruby/io.h"
|
5
|
+
#include "polyphony.h"
|
6
|
+
#include "backend_common.h"
|
7
|
+
|
8
|
+
inline void backend_base_initialize(struct Backend_base *base) {
|
9
|
+
runqueue_initialize(&base->runqueue);
|
10
|
+
base->currently_polling = 0;
|
11
|
+
base->pending_count = 0;
|
12
|
+
base->idle_gc_period = 0;
|
13
|
+
base->idle_gc_last_time = 0;
|
14
|
+
base->idle_proc = Qnil;
|
15
|
+
base->trace_proc = Qnil;
|
16
|
+
}
|
17
|
+
|
18
|
+
inline void backend_base_finalize(struct Backend_base *base) {
|
19
|
+
runqueue_finalize(&base->runqueue);
|
20
|
+
}
|
21
|
+
|
22
|
+
inline void backend_base_mark(struct Backend_base *base) {
|
23
|
+
if (base->idle_proc != Qnil) rb_gc_mark(base->idle_proc);
|
24
|
+
if (base->trace_proc != Qnil) rb_gc_mark(base->trace_proc);
|
25
|
+
runqueue_mark(&base->runqueue);
|
26
|
+
}
|
27
|
+
|
28
|
+
VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base) {
|
29
|
+
VALUE current_fiber = rb_fiber_current();
|
30
|
+
runqueue_entry next;
|
31
|
+
unsigned int pending_ops_count = base->pending_count;
|
32
|
+
unsigned int backend_was_polled = 0;
|
33
|
+
unsigned int idle_tasks_run_count = 0;
|
34
|
+
|
35
|
+
if (SHOULD_TRACE(base) && (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse))
|
36
|
+
TRACE(base, 2, SYM_fiber_switchpoint, current_fiber);
|
37
|
+
|
38
|
+
while (1) {
|
39
|
+
next = runqueue_shift(&base->runqueue);
|
40
|
+
if (next.fiber != Qnil) {
|
41
|
+
// Polling for I/O op completion is normally done when the run queue is
|
42
|
+
// empty, but if the runqueue never empties, we'll never get to process
|
43
|
+
// any event completions. In order to prevent this, an anti-starve
|
44
|
+
// mechanism is employed, under the following conditions:
|
45
|
+
// - a blocking poll was not yet performed
|
46
|
+
// - there are pending blocking operations
|
47
|
+
// - the runqueue has signalled that a non-blocking poll should be
|
48
|
+
// performed
|
49
|
+
// - the run queue length high watermark has reached its threshold (currently 128)
|
50
|
+
// - the run queue switch counter has reached its threshold (currently 64)
|
51
|
+
if (!backend_was_polled && pending_ops_count && runqueue_should_poll_nonblocking(&base->runqueue)) {
|
52
|
+
// this prevents event starvation in case the run queue never empties
|
53
|
+
Backend_poll(backend, Qnil);
|
54
|
+
}
|
55
|
+
break;
|
56
|
+
}
|
57
|
+
|
58
|
+
if (!idle_tasks_run_count) {
|
59
|
+
idle_tasks_run_count++;
|
60
|
+
backend_run_idle_tasks(base);
|
61
|
+
}
|
62
|
+
if (pending_ops_count == 0) break;
|
63
|
+
Backend_poll(backend, Qtrue);
|
64
|
+
backend_was_polled = 1;
|
65
|
+
}
|
66
|
+
|
67
|
+
if (next.fiber == Qnil) return Qnil;
|
68
|
+
|
69
|
+
// run next fiber
|
70
|
+
COND_TRACE(base, 3, SYM_fiber_run, next.fiber, next.value);
|
71
|
+
|
72
|
+
rb_ivar_set(next.fiber, ID_ivar_runnable, Qnil);
|
73
|
+
RB_GC_GUARD(next.fiber);
|
74
|
+
RB_GC_GUARD(next.value);
|
75
|
+
return (next.fiber == current_fiber) ?
|
76
|
+
next.value : FIBER_TRANSFER(next.fiber, next.value);
|
77
|
+
}
|
78
|
+
|
79
|
+
void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_base *base, VALUE fiber, VALUE value, int prioritize) {
|
80
|
+
int already_runnable;
|
81
|
+
|
82
|
+
if (rb_fiber_alive_p(fiber) != Qtrue) return;
|
83
|
+
already_runnable = rb_ivar_get(fiber, ID_ivar_runnable) != Qnil;
|
84
|
+
|
85
|
+
COND_TRACE(base, 3, SYM_fiber_schedule, fiber, value);
|
86
|
+
(prioritize ? runqueue_unshift : runqueue_push)(&base->runqueue, fiber, value, already_runnable);
|
87
|
+
if (!already_runnable) {
|
88
|
+
rb_ivar_set(fiber, ID_ivar_runnable, Qtrue);
|
89
|
+
if (rb_thread_current() != thread) {
|
90
|
+
// If the fiber scheduling is done across threads, we need to make sure the
|
91
|
+
// target thread is woken up in case it is in the middle of running its
|
92
|
+
// event selector. Otherwise it's gonna be stuck waiting for an event to
|
93
|
+
// happen, not knowing that it there's already a fiber ready to run in its
|
94
|
+
// run queue.
|
95
|
+
Backend_wakeup(backend);
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
|
101
|
+
inline void backend_trace(struct Backend_base *base, int argc, VALUE *argv) {
|
102
|
+
if (base->trace_proc == Qnil) return;
|
103
|
+
|
104
|
+
rb_funcallv(base->trace_proc, ID_call, argc, argv);
|
105
|
+
}
|
106
|
+
|
107
|
+
#ifdef POLYPHONY_USE_PIDFD_OPEN
|
108
|
+
#ifndef __NR_pidfd_open
|
109
|
+
#define __NR_pidfd_open 434 /* System call # on most architectures */
|
110
|
+
#endif
|
111
|
+
|
112
|
+
inline int pidfd_open(pid_t pid, unsigned int flags) {
|
113
|
+
return syscall(__NR_pidfd_open, pid, flags);
|
114
|
+
}
|
115
|
+
#endif
|
116
|
+
|
117
|
+
//////////////////////////////////////////////////////////////////////
|
118
|
+
//////////////////////////////////////////////////////////////////////
|
119
|
+
// the following is copied verbatim from the Ruby source code (io.c)
|
120
|
+
|
121
|
+
inline int io_setstrbuf(VALUE *str, long len) {
|
122
|
+
#ifdef _WIN32
|
123
|
+
len = (len + 1) & ~1L; /* round up for wide char */
|
124
|
+
#endif
|
125
|
+
if (*str == Qnil) {
|
126
|
+
*str = rb_str_new(0, len);
|
127
|
+
return 1;
|
128
|
+
}
|
129
|
+
else {
|
130
|
+
VALUE s = StringValue(*str);
|
131
|
+
long clen = RSTRING_LEN(s);
|
132
|
+
if (clen >= len) {
|
133
|
+
rb_str_modify(s);
|
134
|
+
return 0;
|
135
|
+
}
|
136
|
+
len -= clen;
|
137
|
+
}
|
138
|
+
rb_str_modify_expand(*str, len);
|
139
|
+
return 0;
|
140
|
+
}
|
141
|
+
|
142
|
+
#define MAX_REALLOC_GAP 4096
|
143
|
+
|
144
|
+
inline void io_shrink_read_string(VALUE str, long n) {
|
145
|
+
if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
|
146
|
+
rb_str_resize(str, n);
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
inline void io_set_read_length(VALUE str, long n, int shrinkable) {
|
151
|
+
if (RSTRING_LEN(str) != n) {
|
152
|
+
rb_str_modify(str);
|
153
|
+
rb_str_set_len(str, n);
|
154
|
+
if (shrinkable) io_shrink_read_string(str, n);
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
inline rb_encoding* io_read_encoding(rb_io_t *fptr) {
|
159
|
+
if (fptr->encs.enc) {
|
160
|
+
return fptr->encs.enc;
|
161
|
+
}
|
162
|
+
return rb_default_external_encoding();
|
163
|
+
}
|
164
|
+
|
165
|
+
inline VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
|
166
|
+
OBJ_TAINT(str);
|
167
|
+
rb_enc_associate(str, io_read_encoding(fptr));
|
168
|
+
return str;
|
169
|
+
}
|
170
|
+
|
171
|
+
//////////////////////////////////////////////////////////////////////
|
172
|
+
//////////////////////////////////////////////////////////////////////
|
173
|
+
|
174
|
+
VALUE backend_await(struct Backend_base *backend) {
|
175
|
+
VALUE ret;
|
176
|
+
backend->pending_count++;
|
177
|
+
ret = Thread_switch_fiber(rb_thread_current());
|
178
|
+
backend->pending_count--;
|
179
|
+
RB_GC_GUARD(ret);
|
180
|
+
return ret;
|
181
|
+
}
|
182
|
+
|
183
|
+
VALUE backend_snooze() {
|
184
|
+
Fiber_make_runnable(rb_fiber_current(), Qnil);
|
185
|
+
VALUE ret = Thread_switch_fiber(rb_thread_current());
|
186
|
+
return ret;
|
187
|
+
}
|
188
|
+
|
189
|
+
inline void rectify_io_file_pos(rb_io_t *fptr) {
|
190
|
+
// Apparently after reopening a closed file, the file position is not reset,
|
191
|
+
// which causes the read to fail. Fortunately we can use fptr->rbuf.len to
|
192
|
+
// find out if that's the case.
|
193
|
+
// See: https://github.com/digital-fabric/polyphony/issues/30
|
194
|
+
if (fptr->rbuf.len > 0) {
|
195
|
+
lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
|
196
|
+
fptr->rbuf.len = 0;
|
197
|
+
}
|
198
|
+
}
|
199
|
+
|
200
|
+
inline double current_time() {
|
201
|
+
struct timespec ts;
|
202
|
+
clock_gettime(CLOCK_MONOTONIC, &ts);
|
203
|
+
long long ns = ts.tv_sec;
|
204
|
+
ns = ns * 1e9 + ts.tv_nsec;
|
205
|
+
double t = ns;
|
206
|
+
return t / 1e9;
|
207
|
+
}
|
208
|
+
|
209
|
+
inline VALUE backend_timeout_exception(VALUE exception) {
|
210
|
+
if (rb_obj_is_kind_of(exception, rb_cArray) == Qtrue)
|
211
|
+
return rb_funcall(rb_ary_entry(exception, 0), ID_new, 1, rb_ary_entry(exception, 1));
|
212
|
+
else if (rb_obj_is_kind_of(exception, rb_cClass) == Qtrue)
|
213
|
+
return rb_funcall(exception, ID_new, 0);
|
214
|
+
else
|
215
|
+
return rb_funcall(rb_eRuntimeError, ID_new, 1, exception);
|
216
|
+
}
|
217
|
+
|
218
|
+
VALUE Backend_timeout_safe(VALUE arg) {
|
219
|
+
return rb_yield(arg);
|
220
|
+
}
|
221
|
+
|
222
|
+
VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
|
223
|
+
return exception;
|
224
|
+
}
|
225
|
+
|
226
|
+
VALUE Backend_timeout_ensure_safe(VALUE arg) {
|
227
|
+
return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
|
228
|
+
}
|
229
|
+
|
230
|
+
static VALUE empty_string = Qnil;
|
231
|
+
|
232
|
+
VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
|
233
|
+
switch (RARRAY_LEN(ary)) {
|
234
|
+
case 0:
|
235
|
+
return Qnil;
|
236
|
+
case 1:
|
237
|
+
return Backend_send(self, io, RARRAY_AREF(ary, 0), flags);
|
238
|
+
default:
|
239
|
+
if (empty_string == Qnil) {
|
240
|
+
empty_string = rb_str_new_literal("");
|
241
|
+
rb_global_variable(&empty_string);
|
242
|
+
}
|
243
|
+
VALUE joined = rb_ary_join(ary, empty_string);
|
244
|
+
VALUE result = Backend_send(self, io, joined, flags);
|
245
|
+
RB_GC_GUARD(joined);
|
246
|
+
return result;
|
247
|
+
}
|
248
|
+
}
|
249
|
+
|
250
|
+
inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
|
251
|
+
VALUE blocking_mode = rb_ivar_get(io, ID_ivar_blocking_mode);
|
252
|
+
if (blocking == blocking_mode) return;
|
253
|
+
|
254
|
+
rb_ivar_set(io, ID_ivar_blocking_mode, blocking);
|
255
|
+
|
256
|
+
#ifdef _WIN32
|
257
|
+
if (blocking != Qtrue)
|
258
|
+
rb_w32_set_nonblock(fptr->fd);
|
259
|
+
#elif defined(F_GETFL)
|
260
|
+
int flags = fcntl(fptr->fd, F_GETFL);
|
261
|
+
if (flags == -1) return;
|
262
|
+
int is_nonblocking = flags & O_NONBLOCK;
|
263
|
+
|
264
|
+
if (blocking == Qtrue) {
|
265
|
+
if (!is_nonblocking) return;
|
266
|
+
flags &= ~O_NONBLOCK;
|
267
|
+
} else {
|
268
|
+
if (is_nonblocking) return;
|
269
|
+
flags |= O_NONBLOCK;
|
270
|
+
}
|
271
|
+
fcntl(fptr->fd, F_SETFL, flags);
|
272
|
+
#endif
|
273
|
+
}
|
274
|
+
|
275
|
+
inline void backend_run_idle_tasks(struct Backend_base *base) {
|
276
|
+
if (base->idle_proc != Qnil)
|
277
|
+
rb_funcall(base->idle_proc, ID_call, 0);
|
278
|
+
|
279
|
+
if (base->idle_gc_period == 0) return;
|
280
|
+
|
281
|
+
double now = current_time();
|
282
|
+
if (now - base->idle_gc_last_time < base->idle_gc_period) return;
|
283
|
+
|
284
|
+
base->idle_gc_last_time = now;
|
285
|
+
rb_gc_enable();
|
286
|
+
rb_gc_start();
|
287
|
+
rb_gc_disable();
|
288
|
+
}
|