polyphony 0.55.0 → 0.56.0
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/CHANGELOG.md +5 -0
- data/Gemfile.lock +1 -1
- data/examples/core/idle_gc.rb +21 -0
- data/examples/io/pipe.rb +11 -0
- data/ext/polyphony/backend_common.c +186 -0
- data/ext/polyphony/backend_common.h +25 -130
- data/ext/polyphony/backend_io_uring.c +32 -36
- data/ext/polyphony/backend_libev.c +204 -61
- data/ext/polyphony/polyphony.c +17 -15
- data/ext/polyphony/polyphony.h +2 -0
- data/ext/polyphony/thread.c +5 -0
- data/lib/polyphony/version.rb +1 -1
- data/test/test_backend.rb +27 -0
- data/test/test_timer.rb +7 -7
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6c9247de0e5049128fffb31879fd0a79ba6fe304e3802a5b7fa69baa4445ecf
|
4
|
+
data.tar.gz: f2e432fb0f9d6362b7d38834a0e37c7907328c6a27d6544e1dad5363ba4f715c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d69bdb3f12cb65cf580cc243ea0a4f479b5306cf087bcfc5e9d8def1fee49b998528f881844649bc01b501ea22371632fd1f4f08d1f8118692bdf2331f698e6
|
7
|
+
data.tar.gz: d49152bd9c9be94835a020f829c7ddc7f3766fcac9fca8e652bd8df43daab7a6eda90c94b40e57b3cef064d1ff2a957f42030aedefae1b2d1ad6a6587cf6fa86
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -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,186 @@
|
|
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
|
+
#ifdef POLYPHONY_USE_PIDFD_OPEN
|
9
|
+
#ifndef __NR_pidfd_open
|
10
|
+
#define __NR_pidfd_open 434 /* System call # on most architectures */
|
11
|
+
#endif
|
12
|
+
|
13
|
+
inline int pidfd_open(pid_t pid, unsigned int flags) {
|
14
|
+
return syscall(__NR_pidfd_open, pid, flags);
|
15
|
+
}
|
16
|
+
#endif
|
17
|
+
|
18
|
+
//////////////////////////////////////////////////////////////////////
|
19
|
+
//////////////////////////////////////////////////////////////////////
|
20
|
+
// the following is copied verbatim from the Ruby source code (io.c)
|
21
|
+
|
22
|
+
inline int io_setstrbuf(VALUE *str, long len) {
|
23
|
+
#ifdef _WIN32
|
24
|
+
len = (len + 1) & ~1L; /* round up for wide char */
|
25
|
+
#endif
|
26
|
+
if (*str == Qnil) {
|
27
|
+
*str = rb_str_new(0, len);
|
28
|
+
return 1;
|
29
|
+
}
|
30
|
+
else {
|
31
|
+
VALUE s = StringValue(*str);
|
32
|
+
long clen = RSTRING_LEN(s);
|
33
|
+
if (clen >= len) {
|
34
|
+
rb_str_modify(s);
|
35
|
+
return 0;
|
36
|
+
}
|
37
|
+
len -= clen;
|
38
|
+
}
|
39
|
+
rb_str_modify_expand(*str, len);
|
40
|
+
return 0;
|
41
|
+
}
|
42
|
+
|
43
|
+
#define MAX_REALLOC_GAP 4096
|
44
|
+
|
45
|
+
inline void io_shrink_read_string(VALUE str, long n) {
|
46
|
+
if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
|
47
|
+
rb_str_resize(str, n);
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
inline void io_set_read_length(VALUE str, long n, int shrinkable) {
|
52
|
+
if (RSTRING_LEN(str) != n) {
|
53
|
+
rb_str_modify(str);
|
54
|
+
rb_str_set_len(str, n);
|
55
|
+
if (shrinkable) io_shrink_read_string(str, n);
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
inline rb_encoding* io_read_encoding(rb_io_t *fptr) {
|
60
|
+
if (fptr->encs.enc) {
|
61
|
+
return fptr->encs.enc;
|
62
|
+
}
|
63
|
+
return rb_default_external_encoding();
|
64
|
+
}
|
65
|
+
|
66
|
+
inline VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
|
67
|
+
OBJ_TAINT(str);
|
68
|
+
rb_enc_associate(str, io_read_encoding(fptr));
|
69
|
+
return str;
|
70
|
+
}
|
71
|
+
|
72
|
+
//////////////////////////////////////////////////////////////////////
|
73
|
+
//////////////////////////////////////////////////////////////////////
|
74
|
+
|
75
|
+
VALUE backend_await(struct Backend_base *backend) {
|
76
|
+
VALUE ret;
|
77
|
+
backend->pending_count++;
|
78
|
+
ret = Thread_switch_fiber(rb_thread_current());
|
79
|
+
backend->pending_count--;
|
80
|
+
RB_GC_GUARD(ret);
|
81
|
+
return ret;
|
82
|
+
}
|
83
|
+
|
84
|
+
VALUE backend_snooze() {
|
85
|
+
Fiber_make_runnable(rb_fiber_current(), Qnil);
|
86
|
+
VALUE ret = Thread_switch_fiber(rb_thread_current());
|
87
|
+
return ret;
|
88
|
+
}
|
89
|
+
|
90
|
+
inline void rectify_io_file_pos(rb_io_t *fptr) {
|
91
|
+
// Apparently after reopening a closed file, the file position is not reset,
|
92
|
+
// which causes the read to fail. Fortunately we can use fptr->rbuf.len to
|
93
|
+
// find out if that's the case.
|
94
|
+
// See: https://github.com/digital-fabric/polyphony/issues/30
|
95
|
+
if (fptr->rbuf.len > 0) {
|
96
|
+
lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
|
97
|
+
fptr->rbuf.len = 0;
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
inline double current_time() {
|
102
|
+
struct timespec ts;
|
103
|
+
clock_gettime(CLOCK_MONOTONIC, &ts);
|
104
|
+
long long ns = ts.tv_sec;
|
105
|
+
ns = ns * 1e9 + ts.tv_nsec;
|
106
|
+
double t = ns;
|
107
|
+
return t / 1e9;
|
108
|
+
}
|
109
|
+
|
110
|
+
inline VALUE backend_timeout_exception(VALUE exception) {
|
111
|
+
if (rb_obj_is_kind_of(exception, rb_cArray) == Qtrue)
|
112
|
+
return rb_funcall(rb_ary_entry(exception, 0), ID_new, 1, rb_ary_entry(exception, 1));
|
113
|
+
else if (rb_obj_is_kind_of(exception, rb_cClass) == Qtrue)
|
114
|
+
return rb_funcall(exception, ID_new, 0);
|
115
|
+
else
|
116
|
+
return rb_funcall(rb_eRuntimeError, ID_new, 1, exception);
|
117
|
+
}
|
118
|
+
|
119
|
+
VALUE Backend_timeout_safe(VALUE arg) {
|
120
|
+
return rb_yield(arg);
|
121
|
+
}
|
122
|
+
|
123
|
+
VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
|
124
|
+
return exception;
|
125
|
+
}
|
126
|
+
|
127
|
+
VALUE Backend_timeout_ensure_safe(VALUE arg) {
|
128
|
+
return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
|
129
|
+
}
|
130
|
+
|
131
|
+
static VALUE empty_string = Qnil;
|
132
|
+
|
133
|
+
VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
|
134
|
+
switch (RARRAY_LEN(ary)) {
|
135
|
+
case 0:
|
136
|
+
return Qnil;
|
137
|
+
case 1:
|
138
|
+
return Backend_send(self, io, RARRAY_AREF(ary, 0), flags);
|
139
|
+
default:
|
140
|
+
if (empty_string == Qnil) {
|
141
|
+
empty_string = rb_str_new_literal("");
|
142
|
+
rb_global_variable(&empty_string);
|
143
|
+
}
|
144
|
+
VALUE joined = rb_ary_join(ary, empty_string);
|
145
|
+
VALUE result = Backend_send(self, io, joined, flags);
|
146
|
+
RB_GC_GUARD(joined);
|
147
|
+
return result;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
|
152
|
+
VALUE blocking_mode = rb_ivar_get(io, ID_ivar_blocking_mode);
|
153
|
+
if (blocking == blocking_mode) return;
|
154
|
+
|
155
|
+
rb_ivar_set(io, ID_ivar_blocking_mode, blocking);
|
156
|
+
|
157
|
+
#ifdef _WIN32
|
158
|
+
if (blocking != Qtrue)
|
159
|
+
rb_w32_set_nonblock(fptr->fd);
|
160
|
+
#elif defined(F_GETFL)
|
161
|
+
int flags = fcntl(fptr->fd, F_GETFL);
|
162
|
+
if (flags == -1) return;
|
163
|
+
int is_nonblocking = flags & O_NONBLOCK;
|
164
|
+
|
165
|
+
if (blocking == Qtrue) {
|
166
|
+
if (!is_nonblocking) return;
|
167
|
+
flags &= ~O_NONBLOCK;
|
168
|
+
} else {
|
169
|
+
if (is_nonblocking) return;
|
170
|
+
flags |= O_NONBLOCK;
|
171
|
+
}
|
172
|
+
fcntl(fptr->fd, F_SETFL, flags);
|
173
|
+
#endif
|
174
|
+
}
|
175
|
+
|
176
|
+
inline void backend_run_idle_tasks(struct Backend_base *base) {
|
177
|
+
if (base->idle_gc_period == 0) return;
|
178
|
+
|
179
|
+
double now = current_time();
|
180
|
+
if (now - base->idle_gc_last_time < base->idle_gc_period) return;
|
181
|
+
|
182
|
+
base->idle_gc_last_time = now;
|
183
|
+
rb_gc_enable();
|
184
|
+
rb_gc_start();
|
185
|
+
rb_gc_disable();
|
186
|
+
}
|
@@ -1,17 +1,18 @@
|
|
1
|
-
#
|
1
|
+
#ifndef BACKEND_COMMON_H
|
2
|
+
#define BACKEND_COMMON_H
|
2
3
|
|
3
4
|
#include "ruby.h"
|
4
5
|
#include "ruby/io.h"
|
5
6
|
|
7
|
+
struct Backend_base {
|
8
|
+
unsigned int currently_polling;
|
9
|
+
unsigned int pending_count;
|
10
|
+
double idle_gc_period;
|
11
|
+
double idle_gc_last_time;
|
12
|
+
};
|
6
13
|
|
7
14
|
#ifdef POLYPHONY_USE_PIDFD_OPEN
|
8
|
-
|
9
|
-
#define __NR_pidfd_open 434 /* System call # on most architectures */
|
10
|
-
#endif
|
11
|
-
|
12
|
-
static int pidfd_open(pid_t pid, unsigned int flags) {
|
13
|
-
return syscall(__NR_pidfd_open, pid, flags);
|
14
|
-
}
|
15
|
+
int pidfd_open(pid_t pid, unsigned int flags);
|
15
16
|
#endif
|
16
17
|
|
17
18
|
//////////////////////////////////////////////////////////////////////
|
@@ -26,75 +27,19 @@ struct io_internal_read_struct {
|
|
26
27
|
|
27
28
|
#define StringValue(v) rb_string_value(&(v))
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
*str = rb_str_new(0, len);
|
35
|
-
return 1;
|
36
|
-
}
|
37
|
-
else {
|
38
|
-
VALUE s = StringValue(*str);
|
39
|
-
long clen = RSTRING_LEN(s);
|
40
|
-
if (clen >= len) {
|
41
|
-
rb_str_modify(s);
|
42
|
-
return 0;
|
43
|
-
}
|
44
|
-
len -= clen;
|
45
|
-
}
|
46
|
-
rb_str_modify_expand(*str, len);
|
47
|
-
return 0;
|
48
|
-
}
|
49
|
-
|
50
|
-
#define MAX_REALLOC_GAP 4096
|
51
|
-
|
52
|
-
inline void io_shrink_read_string(VALUE str, long n) {
|
53
|
-
if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
|
54
|
-
rb_str_resize(str, n);
|
55
|
-
}
|
56
|
-
}
|
57
|
-
|
58
|
-
inline void io_set_read_length(VALUE str, long n, int shrinkable) {
|
59
|
-
if (RSTRING_LEN(str) != n) {
|
60
|
-
rb_str_modify(str);
|
61
|
-
rb_str_set_len(str, n);
|
62
|
-
if (shrinkable) io_shrink_read_string(str, n);
|
63
|
-
}
|
64
|
-
}
|
65
|
-
|
66
|
-
inline rb_encoding* io_read_encoding(rb_io_t *fptr) {
|
67
|
-
if (fptr->encs.enc) {
|
68
|
-
return fptr->encs.enc;
|
69
|
-
}
|
70
|
-
return rb_default_external_encoding();
|
71
|
-
}
|
72
|
-
|
73
|
-
inline VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
|
74
|
-
OBJ_TAINT(str);
|
75
|
-
rb_enc_associate(str, io_read_encoding(fptr));
|
76
|
-
return str;
|
77
|
-
}
|
30
|
+
int io_setstrbuf(VALUE *str, long len);
|
31
|
+
void io_shrink_read_string(VALUE str, long n);
|
32
|
+
void io_set_read_length(VALUE str, long n, int shrinkable);
|
33
|
+
rb_encoding* io_read_encoding(rb_io_t *fptr);
|
34
|
+
VALUE io_enc_str(VALUE str, rb_io_t *fptr);
|
78
35
|
|
79
36
|
//////////////////////////////////////////////////////////////////////
|
80
37
|
//////////////////////////////////////////////////////////////////////
|
81
38
|
|
82
|
-
|
83
|
-
|
84
|
-
backend->pending_count++;
|
85
|
-
ret = Thread_switch_fiber(rb_thread_current());
|
86
|
-
backend->pending_count--;
|
87
|
-
RB_GC_GUARD(ret);
|
88
|
-
return ret;
|
89
|
-
}
|
90
|
-
|
91
|
-
inline VALUE backend_snooze() {
|
92
|
-
Fiber_make_runnable(rb_fiber_current(), Qnil);
|
93
|
-
return Thread_switch_fiber(rb_thread_current());
|
94
|
-
}
|
39
|
+
VALUE backend_await(struct Backend_base *backend);
|
40
|
+
VALUE backend_snooze();
|
95
41
|
|
96
42
|
// macros for doing read loops
|
97
|
-
|
98
43
|
#define READ_LOOP_PREPARE_STR() { \
|
99
44
|
str = Qnil; \
|
100
45
|
shrinkable = io_setstrbuf(&str, len); \
|
@@ -117,63 +62,13 @@ inline VALUE backend_snooze() {
|
|
117
62
|
READ_LOOP_PREPARE_STR(); \
|
118
63
|
}
|
119
64
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
}
|
129
|
-
}
|
130
|
-
|
131
|
-
inline double current_time() {
|
132
|
-
struct timespec ts;
|
133
|
-
clock_gettime(CLOCK_MONOTONIC, &ts);
|
134
|
-
long long ns = ts.tv_sec;
|
135
|
-
ns = ns * 1e9 + ts.tv_nsec;
|
136
|
-
double t = ns;
|
137
|
-
return t / 1e9;
|
138
|
-
}
|
139
|
-
|
140
|
-
inline VALUE backend_timeout_exception(VALUE exception) {
|
141
|
-
if (rb_obj_is_kind_of(exception, rb_cArray) == Qtrue)
|
142
|
-
return rb_funcall(rb_ary_entry(exception, 0), ID_new, 1, rb_ary_entry(exception, 1));
|
143
|
-
else if (rb_obj_is_kind_of(exception, rb_cClass) == Qtrue)
|
144
|
-
return rb_funcall(exception, ID_new, 0);
|
145
|
-
else
|
146
|
-
return rb_funcall(rb_eRuntimeError, ID_new, 1, exception);
|
147
|
-
}
|
65
|
+
void rectify_io_file_pos(rb_io_t *fptr);
|
66
|
+
double current_time();
|
67
|
+
VALUE backend_timeout_exception(VALUE exception);
|
68
|
+
VALUE Backend_timeout_ensure_safe(VALUE arg);
|
69
|
+
VALUE Backend_timeout_ensure_safe(VALUE arg);
|
70
|
+
VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
|
71
|
+
void backend_run_idle_tasks(struct Backend_base *base);
|
72
|
+
void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking);
|
148
73
|
|
149
|
-
|
150
|
-
return rb_yield(arg);
|
151
|
-
}
|
152
|
-
|
153
|
-
VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
|
154
|
-
return exception;
|
155
|
-
}
|
156
|
-
|
157
|
-
VALUE Backend_timeout_ensure_safe(VALUE arg) {
|
158
|
-
return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
|
159
|
-
}
|
160
|
-
|
161
|
-
static VALUE empty_string = Qnil;
|
162
|
-
|
163
|
-
VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
|
164
|
-
switch (RARRAY_LEN(ary)) {
|
165
|
-
case 0:
|
166
|
-
return Qnil;
|
167
|
-
case 1:
|
168
|
-
return Backend_send(self, io, RARRAY_AREF(ary, 0), flags);
|
169
|
-
default:
|
170
|
-
if (empty_string == Qnil) {
|
171
|
-
empty_string = rb_str_new_literal("");
|
172
|
-
rb_global_variable(&empty_string);
|
173
|
-
}
|
174
|
-
VALUE joined = rb_ary_join(ary, empty_string);
|
175
|
-
VALUE result = Backend_send(self, io, joined, flags);
|
176
|
-
RB_GC_GUARD(joined);
|
177
|
-
return result;
|
178
|
-
}
|
179
|
-
}
|
74
|
+
#endif /* BACKEND_COMMON_H */
|
@@ -4,7 +4,6 @@
|
|
4
4
|
#include <sys/socket.h>
|
5
5
|
#include <sys/uio.h>
|
6
6
|
#include <unistd.h>
|
7
|
-
#include <fcntl.h>
|
8
7
|
#include <netinet/in.h>
|
9
8
|
#include <arpa/inet.h>
|
10
9
|
#include <stdnoreturn.h>
|
@@ -19,6 +18,7 @@
|
|
19
18
|
#include "backend_io_uring_context.h"
|
20
19
|
#include "ruby/thread.h"
|
21
20
|
#include "ruby/io.h"
|
21
|
+
#include "backend_common.h"
|
22
22
|
|
23
23
|
VALUE SYM_io_uring;
|
24
24
|
VALUE SYM_send;
|
@@ -26,34 +26,13 @@ VALUE SYM_splice;
|
|
26
26
|
VALUE SYM_write;
|
27
27
|
|
28
28
|
#ifdef POLYPHONY_UNSET_NONBLOCK
|
29
|
-
|
30
|
-
|
31
|
-
// One of the changes introduced in Ruby 3.0 as part of the work on the
|
32
|
-
// FiberScheduler interface is that all created sockets are marked as
|
33
|
-
// non-blocking. This prevents the io_uring backend from working correctly,
|
34
|
-
// since it will return an EAGAIN error just like a normal syscall. So here
|
35
|
-
// instead of setting O_NONBLOCK (which is required for the libev backend), we
|
36
|
-
// unset it.
|
37
|
-
inline void io_unset_nonblock(rb_io_t *fptr, VALUE io) {
|
38
|
-
VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
|
39
|
-
if (is_nonblocking == Qfalse) return;
|
40
|
-
|
41
|
-
rb_ivar_set(io, ID_ivar_is_nonblocking, Qfalse);
|
42
|
-
|
43
|
-
int oflags = fcntl(fptr->fd, F_GETFL);
|
44
|
-
if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
|
45
|
-
oflags &= !O_NONBLOCK;
|
46
|
-
fcntl(fptr->fd, F_SETFL, oflags);
|
47
|
-
}
|
29
|
+
#define io_unset_nonblock(fptr, io) io_verify_blocking_mode(fptr, io, Qtrue)
|
48
30
|
#else
|
49
|
-
// NOP
|
50
31
|
#define io_unset_nonblock(fptr, io)
|
51
32
|
#endif
|
52
33
|
|
53
34
|
typedef struct Backend_t {
|
54
|
-
|
55
|
-
unsigned int currently_polling;
|
56
|
-
unsigned int pending_count;
|
35
|
+
struct Backend_base base;
|
57
36
|
|
58
37
|
// implementation-specific fields
|
59
38
|
struct io_uring ring;
|
@@ -63,8 +42,6 @@ typedef struct Backend_t {
|
|
63
42
|
int event_fd;
|
64
43
|
} Backend_t;
|
65
44
|
|
66
|
-
#include "backend_common.h"
|
67
|
-
|
68
45
|
static size_t Backend_size(const void *ptr) {
|
69
46
|
return sizeof(Backend_t);
|
70
47
|
}
|
@@ -88,8 +65,11 @@ static VALUE Backend_initialize(VALUE self) {
|
|
88
65
|
Backend_t *backend;
|
89
66
|
GetBackend(self, backend);
|
90
67
|
|
91
|
-
backend->currently_polling = 0;
|
92
|
-
backend->pending_count = 0;
|
68
|
+
backend->base.currently_polling = 0;
|
69
|
+
backend->base.pending_count = 0;
|
70
|
+
backend->base.idle_gc_period = 0;
|
71
|
+
backend->base.idle_gc_last_time = 0;
|
72
|
+
|
93
73
|
backend->pending_sqes = 0;
|
94
74
|
backend->prepared_limit = 2048;
|
95
75
|
|
@@ -117,8 +97,8 @@ VALUE Backend_post_fork(VALUE self) {
|
|
117
97
|
io_uring_queue_exit(&backend->ring);
|
118
98
|
io_uring_queue_init(backend->prepared_limit, &backend->ring, 0);
|
119
99
|
context_store_free(&backend->store);
|
120
|
-
backend->currently_polling = 0;
|
121
|
-
backend->pending_count = 0;
|
100
|
+
backend->base.currently_polling = 0;
|
101
|
+
backend->base.pending_count = 0;
|
122
102
|
backend->pending_sqes = 0;
|
123
103
|
|
124
104
|
return self;
|
@@ -128,7 +108,7 @@ unsigned int Backend_pending_count(VALUE self) {
|
|
128
108
|
Backend_t *backend;
|
129
109
|
GetBackend(self, backend);
|
130
110
|
|
131
|
-
return backend->pending_count;
|
111
|
+
return backend->base.pending_count;
|
132
112
|
}
|
133
113
|
|
134
114
|
typedef struct poll_context {
|
@@ -197,9 +177,9 @@ void io_uring_backend_poll(Backend_t *backend) {
|
|
197
177
|
io_uring_submit(&backend->ring);
|
198
178
|
}
|
199
179
|
|
200
|
-
backend->currently_polling = 1;
|
180
|
+
backend->base.currently_polling = 1;
|
201
181
|
rb_thread_call_without_gvl(io_uring_backend_poll_without_gvl, (void *)&poll_ctx, RUBY_UBF_IO, 0);
|
202
|
-
backend->currently_polling = 0;
|
182
|
+
backend->base.currently_polling = 0;
|
203
183
|
if (poll_ctx.result < 0) return;
|
204
184
|
|
205
185
|
io_uring_backend_handle_completion(poll_ctx.cqe, backend);
|
@@ -228,7 +208,7 @@ VALUE Backend_wakeup(VALUE self) {
|
|
228
208
|
Backend_t *backend;
|
229
209
|
GetBackend(self, backend);
|
230
210
|
|
231
|
-
if (backend->currently_polling) {
|
211
|
+
if (backend->base.currently_polling) {
|
232
212
|
// Since we're currently blocking while waiting for a completion, we add a
|
233
213
|
// NOP which would cause the io_uring_enter syscall to return
|
234
214
|
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
@@ -263,7 +243,7 @@ int io_uring_backend_defer_submit_and_await(
|
|
263
243
|
io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
|
264
244
|
io_uring_backend_defer_submit(backend);
|
265
245
|
|
266
|
-
switchpoint_result = backend_await(backend);
|
246
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
267
247
|
|
268
248
|
if (ctx->ref_count > 1) {
|
269
249
|
// op was not completed (an exception was raised), so we need to cancel it
|
@@ -1177,7 +1157,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
|
1177
1157
|
|
1178
1158
|
ctx->ref_count = sqe_count + 1;
|
1179
1159
|
io_uring_backend_defer_submit(backend);
|
1180
|
-
resume_value = backend_await(backend);
|
1160
|
+
resume_value = backend_await((struct Backend_base *)backend);
|
1181
1161
|
int result = ctx->result;
|
1182
1162
|
int completed = context_store_release(&backend->store, ctx);
|
1183
1163
|
if (!completed) {
|
@@ -1195,6 +1175,21 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
|
1195
1175
|
return INT2NUM(result);
|
1196
1176
|
}
|
1197
1177
|
|
1178
|
+
VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
|
1179
|
+
Backend_t *backend;
|
1180
|
+
GetBackend(self, backend);
|
1181
|
+
backend->base.idle_gc_period = NUM2DBL(period);
|
1182
|
+
backend->base.idle_gc_last_time = current_time();
|
1183
|
+
return self;
|
1184
|
+
}
|
1185
|
+
|
1186
|
+
inline VALUE Backend_run_idle_tasks(VALUE self) {
|
1187
|
+
Backend_t *backend;
|
1188
|
+
GetBackend(self, backend);
|
1189
|
+
backend_run_idle_tasks(&backend->base);
|
1190
|
+
return self;
|
1191
|
+
}
|
1192
|
+
|
1198
1193
|
void Init_Backend() {
|
1199
1194
|
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
|
1200
1195
|
rb_define_alloc_func(cBackend, Backend_allocate);
|
@@ -1207,6 +1202,7 @@ void Init_Backend() {
|
|
1207
1202
|
rb_define_method(cBackend, "break", Backend_wakeup, 0);
|
1208
1203
|
rb_define_method(cBackend, "kind", Backend_kind, 0);
|
1209
1204
|
rb_define_method(cBackend, "chain", Backend_chain, -1);
|
1205
|
+
rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
|
1210
1206
|
|
1211
1207
|
rb_define_method(cBackend, "accept", Backend_accept, 2);
|
1212
1208
|
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
@@ -42,7 +42,6 @@ thread.
|
|
42
42
|
#define _GNU_SOURCE 1
|
43
43
|
#endif
|
44
44
|
|
45
|
-
#include <fcntl.h>
|
46
45
|
#include <netdb.h>
|
47
46
|
#include <sys/socket.h>
|
48
47
|
#include <sys/uio.h>
|
@@ -52,44 +51,22 @@ thread.
|
|
52
51
|
#include <stdnoreturn.h>
|
53
52
|
#include <sys/types.h>
|
54
53
|
#include <sys/wait.h>
|
54
|
+
#include <fcntl.h>
|
55
55
|
|
56
56
|
#include "polyphony.h"
|
57
57
|
#include "../libev/ev.h"
|
58
58
|
#include "ruby/io.h"
|
59
59
|
|
60
|
+
#include "../libev/ev.h"
|
61
|
+
#include "backend_common.h"
|
62
|
+
|
60
63
|
VALUE SYM_libev;
|
61
64
|
VALUE SYM_send;
|
62
65
|
VALUE SYM_splice;
|
63
66
|
VALUE SYM_write;
|
64
67
|
|
65
|
-
ID ID_ivar_is_nonblocking;
|
66
|
-
|
67
|
-
// Since we need to ensure that fd's are non-blocking before every I/O
|
68
|
-
// operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
|
69
|
-
// "nonblock" state in an instance variable. Calling rb_ivar_get on every read
|
70
|
-
// is still much cheaper than doing a fcntl syscall on every read! Preliminary
|
71
|
-
// benchmarks (with a "hello world" HTTP server) show throughput is improved
|
72
|
-
// by 10-13%.
|
73
|
-
inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
|
74
|
-
VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
|
75
|
-
if (is_nonblocking == Qtrue) return;
|
76
|
-
|
77
|
-
rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
|
78
|
-
|
79
|
-
#ifdef _WIN32
|
80
|
-
rb_w32_set_nonblock(fptr->fd);
|
81
|
-
#elif defined(F_GETFL)
|
82
|
-
int oflags = fcntl(fptr->fd, F_GETFL);
|
83
|
-
if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
|
84
|
-
oflags |= O_NONBLOCK;
|
85
|
-
fcntl(fptr->fd, F_SETFL, oflags);
|
86
|
-
#endif
|
87
|
-
}
|
88
|
-
|
89
68
|
typedef struct Backend_t {
|
90
|
-
|
91
|
-
unsigned int currently_polling;
|
92
|
-
unsigned int pending_count;
|
69
|
+
struct Backend_base base;
|
93
70
|
|
94
71
|
// implementation-specific fields
|
95
72
|
struct ev_loop *ev_loop;
|
@@ -142,8 +119,10 @@ static VALUE Backend_initialize(VALUE self) {
|
|
142
119
|
// block when no other watcher is active
|
143
120
|
ev_unref(backend->ev_loop);
|
144
121
|
|
145
|
-
backend->currently_polling = 0;
|
146
|
-
backend->pending_count = 0;
|
122
|
+
backend->base.currently_polling = 0;
|
123
|
+
backend->base.pending_count = 0;
|
124
|
+
backend->base.idle_gc_period = 0;
|
125
|
+
backend->base.idle_gc_last_time = 0;
|
147
126
|
|
148
127
|
return Qnil;
|
149
128
|
}
|
@@ -174,11 +153,11 @@ VALUE Backend_post_fork(VALUE self) {
|
|
174
153
|
return self;
|
175
154
|
}
|
176
155
|
|
177
|
-
unsigned int Backend_pending_count(VALUE self) {
|
156
|
+
inline unsigned int Backend_pending_count(VALUE self) {
|
178
157
|
Backend_t *backend;
|
179
158
|
GetBackend(self, backend);
|
180
159
|
|
181
|
-
return backend->pending_count;
|
160
|
+
return backend->base.pending_count;
|
182
161
|
}
|
183
162
|
|
184
163
|
VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
|
@@ -186,9 +165,9 @@ VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue
|
|
186
165
|
GetBackend(self, backend);
|
187
166
|
|
188
167
|
COND_TRACE(2, SYM_fiber_event_poll_enter, current_fiber);
|
189
|
-
backend->currently_polling = 1;
|
168
|
+
backend->base.currently_polling = 1;
|
190
169
|
ev_run(backend->ev_loop, nowait == Qtrue ? EVRUN_NOWAIT : EVRUN_ONCE);
|
191
|
-
backend->currently_polling = 0;
|
170
|
+
backend->base.currently_polling = 0;
|
192
171
|
COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
|
193
172
|
|
194
173
|
return self;
|
@@ -198,7 +177,7 @@ VALUE Backend_wakeup(VALUE self) {
|
|
198
177
|
Backend_t *backend;
|
199
178
|
GetBackend(self, backend);
|
200
179
|
|
201
|
-
if (backend->currently_polling) {
|
180
|
+
if (backend->base.currently_polling) {
|
202
181
|
// Since the loop will run until at least one event has occurred, we signal
|
203
182
|
// the selector's associated async watcher, which will cause the ev loop to
|
204
183
|
// return. In contrast to using `ev_break` to break out of the loop, which
|
@@ -211,10 +190,6 @@ VALUE Backend_wakeup(VALUE self) {
|
|
211
190
|
return Qnil;
|
212
191
|
}
|
213
192
|
|
214
|
-
#include "../libev/ev.h"
|
215
|
-
|
216
|
-
#include "backend_common.h"
|
217
|
-
|
218
193
|
struct libev_io {
|
219
194
|
struct ev_io io;
|
220
195
|
VALUE fiber;
|
@@ -235,7 +210,7 @@ VALUE libev_wait_fd_with_watcher(Backend_t *backend, int fd, struct libev_io *wa
|
|
235
210
|
}
|
236
211
|
ev_io_start(backend->ev_loop, &watcher->io);
|
237
212
|
|
238
|
-
switchpoint_result = backend_await(backend);
|
213
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
239
214
|
|
240
215
|
ev_io_stop(backend->ev_loop, &watcher->io);
|
241
216
|
RB_GC_GUARD(switchpoint_result);
|
@@ -271,7 +246,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
271
246
|
if (underlying_io != Qnil) io = underlying_io;
|
272
247
|
GetOpenFile(io, fptr);
|
273
248
|
rb_io_check_byte_readable(fptr);
|
274
|
-
|
249
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
275
250
|
rectify_io_file_pos(fptr);
|
276
251
|
watcher.fiber = Qnil;
|
277
252
|
OBJ_TAINT(str);
|
@@ -288,7 +263,6 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
288
263
|
}
|
289
264
|
else {
|
290
265
|
switchpoint_result = backend_snooze();
|
291
|
-
|
292
266
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
293
267
|
|
294
268
|
if (n == 0) break; // EOF
|
@@ -343,7 +317,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
|
|
343
317
|
if (underlying_io != Qnil) io = underlying_io;
|
344
318
|
GetOpenFile(io, fptr);
|
345
319
|
rb_io_check_byte_readable(fptr);
|
346
|
-
|
320
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
347
321
|
rectify_io_file_pos(fptr);
|
348
322
|
watcher.fiber = Qnil;
|
349
323
|
|
@@ -395,7 +369,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
395
369
|
if (underlying_io != Qnil) io = underlying_io;
|
396
370
|
GetOpenFile(io, fptr);
|
397
371
|
rb_io_check_byte_readable(fptr);
|
398
|
-
|
372
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
399
373
|
rectify_io_file_pos(fptr);
|
400
374
|
watcher.fiber = Qnil;
|
401
375
|
|
@@ -443,7 +417,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
443
417
|
GetBackend(self, backend);
|
444
418
|
io = rb_io_get_write_io(io);
|
445
419
|
GetOpenFile(io, fptr);
|
446
|
-
|
420
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
447
421
|
watcher.fiber = Qnil;
|
448
422
|
|
449
423
|
while (left > 0) {
|
@@ -493,7 +467,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
493
467
|
GetBackend(self, backend);
|
494
468
|
io = rb_io_get_write_io(io);
|
495
469
|
GetOpenFile(io, fptr);
|
496
|
-
|
470
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
497
471
|
watcher.fiber = Qnil;
|
498
472
|
|
499
473
|
iov = malloc(iov_count * sizeof(struct iovec));
|
@@ -574,7 +548,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
574
548
|
|
575
549
|
GetBackend(self, backend);
|
576
550
|
GetOpenFile(server_socket, fptr);
|
577
|
-
|
551
|
+
io_verify_blocking_mode(fptr, server_socket, Qfalse);
|
578
552
|
watcher.fiber = Qnil;
|
579
553
|
while (1) {
|
580
554
|
fd = accept(fptr->fd, &addr, &len);
|
@@ -602,7 +576,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
602
576
|
fp->fd = fd;
|
603
577
|
fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
|
604
578
|
rb_io_ascii8bit_binmode(socket);
|
605
|
-
|
579
|
+
io_verify_blocking_mode(fp, socket, Qfalse);
|
606
580
|
rb_io_synchronized(fp);
|
607
581
|
|
608
582
|
// if (rsock_do_not_reverse_lookup) {
|
@@ -631,7 +605,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
631
605
|
|
632
606
|
GetBackend(self, backend);
|
633
607
|
GetOpenFile(server_socket, fptr);
|
634
|
-
|
608
|
+
io_verify_blocking_mode(fptr, server_socket, Qfalse);
|
635
609
|
watcher.fiber = Qnil;
|
636
610
|
|
637
611
|
while (1) {
|
@@ -659,7 +633,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
659
633
|
fp->fd = fd;
|
660
634
|
fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
|
661
635
|
rb_io_ascii8bit_binmode(socket);
|
662
|
-
|
636
|
+
io_verify_blocking_mode(fp, socket, Qfalse);
|
663
637
|
rb_io_synchronized(fp);
|
664
638
|
|
665
639
|
rb_yield(socket);
|
@@ -687,7 +661,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
687
661
|
|
688
662
|
GetBackend(self, backend);
|
689
663
|
GetOpenFile(sock, fptr);
|
690
|
-
|
664
|
+
io_verify_blocking_mode(fptr, sock, Qfalse);
|
691
665
|
watcher.fiber = Qnil;
|
692
666
|
|
693
667
|
addr.sin_family = AF_INET;
|
@@ -730,7 +704,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
|
730
704
|
GetBackend(self, backend);
|
731
705
|
io = rb_io_get_write_io(io);
|
732
706
|
GetOpenFile(io, fptr);
|
733
|
-
|
707
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
734
708
|
watcher.fiber = Qnil;
|
735
709
|
|
736
710
|
while (left > 0) {
|
@@ -778,13 +752,13 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
778
752
|
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
779
753
|
if (underlying_io != Qnil) src = underlying_io;
|
780
754
|
GetOpenFile(src, src_fptr);
|
781
|
-
|
755
|
+
io_verify_blocking_mode(src_fptr, src, Qfalse);
|
782
756
|
|
783
757
|
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
784
758
|
if (underlying_io != Qnil) dest = underlying_io;
|
785
759
|
dest = rb_io_get_write_io(dest);
|
786
760
|
GetOpenFile(dest, dest_fptr);
|
787
|
-
|
761
|
+
io_verify_blocking_mode(dest_fptr, dest, Qfalse);
|
788
762
|
|
789
763
|
watcher.fiber = Qnil;
|
790
764
|
while (1) {
|
@@ -832,13 +806,13 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
832
806
|
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
833
807
|
if (underlying_io != Qnil) src = underlying_io;
|
834
808
|
GetOpenFile(src, src_fptr);
|
835
|
-
|
809
|
+
io_verify_blocking_mode(src_fptr, src, Qfalse);
|
836
810
|
|
837
811
|
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
838
812
|
if (underlying_io != Qnil) dest = underlying_io;
|
839
813
|
dest = rb_io_get_write_io(dest);
|
840
814
|
GetOpenFile(dest, dest_fptr);
|
841
|
-
|
815
|
+
io_verify_blocking_mode(dest_fptr, dest, Qfalse);
|
842
816
|
|
843
817
|
watcher.fiber = Qnil;
|
844
818
|
while (1) {
|
@@ -875,6 +849,157 @@ error:
|
|
875
849
|
}
|
876
850
|
#endif
|
877
851
|
|
852
|
+
VALUE Backend_fake_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
853
|
+
Backend_t *backend;
|
854
|
+
struct libev_io watcher;
|
855
|
+
VALUE switchpoint_result = Qnil;
|
856
|
+
VALUE underlying_io;
|
857
|
+
rb_io_t *src_fptr;
|
858
|
+
rb_io_t *dest_fptr;
|
859
|
+
int len = NUM2INT(maxlen);
|
860
|
+
VALUE str = rb_str_new(0, len);
|
861
|
+
char *buf = RSTRING_PTR(str);
|
862
|
+
int left = 0;
|
863
|
+
int total = 0;
|
864
|
+
|
865
|
+
GetBackend(self, backend);
|
866
|
+
|
867
|
+
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
868
|
+
if (underlying_io != Qnil) src = underlying_io;
|
869
|
+
GetOpenFile(src, src_fptr);
|
870
|
+
io_verify_blocking_mode(src_fptr, src, Qfalse);
|
871
|
+
|
872
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
873
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
874
|
+
dest = rb_io_get_write_io(dest);
|
875
|
+
GetOpenFile(dest, dest_fptr);
|
876
|
+
io_verify_blocking_mode(dest_fptr, dest, Qfalse);
|
877
|
+
|
878
|
+
watcher.fiber = Qnil;
|
879
|
+
|
880
|
+
while (1) {
|
881
|
+
ssize_t n = read(src_fptr->fd, buf, len);
|
882
|
+
if (n < 0) {
|
883
|
+
int e = errno;
|
884
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
885
|
+
|
886
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
|
887
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
888
|
+
}
|
889
|
+
else {
|
890
|
+
total = left = n;
|
891
|
+
break;
|
892
|
+
}
|
893
|
+
}
|
894
|
+
|
895
|
+
while (left > 0) {
|
896
|
+
ssize_t n = write(dest_fptr->fd, buf, left);
|
897
|
+
if (n < 0) {
|
898
|
+
int e = errno;
|
899
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
900
|
+
|
901
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
|
902
|
+
|
903
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
904
|
+
}
|
905
|
+
else {
|
906
|
+
buf += n;
|
907
|
+
left -= n;
|
908
|
+
}
|
909
|
+
}
|
910
|
+
|
911
|
+
if (watcher.fiber == Qnil) {
|
912
|
+
switchpoint_result = backend_snooze();
|
913
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
914
|
+
}
|
915
|
+
|
916
|
+
RB_GC_GUARD(watcher.fiber);
|
917
|
+
RB_GC_GUARD(switchpoint_result);
|
918
|
+
RB_GC_GUARD(str);
|
919
|
+
|
920
|
+
return INT2NUM(total);
|
921
|
+
error:
|
922
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
923
|
+
}
|
924
|
+
|
925
|
+
VALUE Backend_fake_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
926
|
+
Backend_t *backend;
|
927
|
+
struct libev_io watcher;
|
928
|
+
VALUE switchpoint_result = Qnil;
|
929
|
+
VALUE underlying_io;
|
930
|
+
rb_io_t *src_fptr;
|
931
|
+
rb_io_t *dest_fptr;
|
932
|
+
int len = NUM2INT(maxlen);
|
933
|
+
VALUE str = rb_str_new(0, len);
|
934
|
+
char *buf = RSTRING_PTR(str);
|
935
|
+
int left = 0;
|
936
|
+
int total = 0;
|
937
|
+
|
938
|
+
GetBackend(self, backend);
|
939
|
+
|
940
|
+
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
941
|
+
if (underlying_io != Qnil) src = underlying_io;
|
942
|
+
GetOpenFile(src, src_fptr);
|
943
|
+
io_verify_blocking_mode(src_fptr, src, Qfalse);
|
944
|
+
|
945
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
946
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
947
|
+
dest = rb_io_get_write_io(dest);
|
948
|
+
GetOpenFile(dest, dest_fptr);
|
949
|
+
io_verify_blocking_mode(dest_fptr, dest, Qfalse);
|
950
|
+
|
951
|
+
watcher.fiber = Qnil;
|
952
|
+
|
953
|
+
while (1) {
|
954
|
+
char *ptr = buf;
|
955
|
+
while (1) {
|
956
|
+
ssize_t n = read(src_fptr->fd, ptr, len);
|
957
|
+
if (n < 0) {
|
958
|
+
int e = errno;
|
959
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
960
|
+
|
961
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
|
962
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
963
|
+
}
|
964
|
+
else if (n == 0) goto done;
|
965
|
+
else {
|
966
|
+
total += n;
|
967
|
+
left = n;
|
968
|
+
break;
|
969
|
+
}
|
970
|
+
}
|
971
|
+
|
972
|
+
while (left > 0) {
|
973
|
+
ssize_t n = write(dest_fptr->fd, ptr, left);
|
974
|
+
if (n < 0) {
|
975
|
+
int e = errno;
|
976
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
977
|
+
|
978
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
|
979
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
980
|
+
}
|
981
|
+
else {
|
982
|
+
ptr += n;
|
983
|
+
left -= n;
|
984
|
+
}
|
985
|
+
}
|
986
|
+
}
|
987
|
+
|
988
|
+
done:
|
989
|
+
if (watcher.fiber == Qnil) {
|
990
|
+
switchpoint_result = backend_snooze();
|
991
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
992
|
+
}
|
993
|
+
|
994
|
+
RB_GC_GUARD(watcher.fiber);
|
995
|
+
RB_GC_GUARD(switchpoint_result);
|
996
|
+
RB_GC_GUARD(str);
|
997
|
+
|
998
|
+
return INT2NUM(total);
|
999
|
+
error:
|
1000
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
1001
|
+
}
|
1002
|
+
|
878
1003
|
VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
|
879
1004
|
Backend_t *backend;
|
880
1005
|
rb_io_t *fptr;
|
@@ -908,7 +1033,7 @@ VALUE Backend_sleep(VALUE self, VALUE duration) {
|
|
908
1033
|
ev_timer_init(&watcher.timer, Backend_timer_callback, NUM2DBL(duration), 0.);
|
909
1034
|
ev_timer_start(backend->ev_loop, &watcher.timer);
|
910
1035
|
|
911
|
-
switchpoint_result = backend_await(backend);
|
1036
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
912
1037
|
|
913
1038
|
ev_timer_stop(backend->ev_loop, &watcher.timer);
|
914
1039
|
RAISE_IF_EXCEPTION(switchpoint_result);
|
@@ -936,7 +1061,7 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
|
|
936
1061
|
VALUE switchpoint_result = Qnil;
|
937
1062
|
ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
|
938
1063
|
ev_timer_start(backend->ev_loop, &watcher.timer);
|
939
|
-
switchpoint_result = backend_await(backend);
|
1064
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
940
1065
|
ev_timer_stop(backend->ev_loop, &watcher.timer);
|
941
1066
|
RAISE_IF_EXCEPTION(switchpoint_result);
|
942
1067
|
RB_GC_GUARD(switchpoint_result);
|
@@ -1053,7 +1178,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
|
|
1053
1178
|
ev_child_init(&watcher.child, Backend_child_callback, NUM2INT(pid), 0);
|
1054
1179
|
ev_child_start(backend->ev_loop, &watcher.child);
|
1055
1180
|
|
1056
|
-
switchpoint_result = backend_await(backend);
|
1181
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
1057
1182
|
|
1058
1183
|
ev_child_stop(backend->ev_loop, &watcher.child);
|
1059
1184
|
RAISE_IF_EXCEPTION(switchpoint_result);
|
@@ -1075,7 +1200,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
|
|
1075
1200
|
ev_async_init(&async, Backend_async_callback);
|
1076
1201
|
ev_async_start(backend->ev_loop, &async);
|
1077
1202
|
|
1078
|
-
switchpoint_result = backend_await(backend);
|
1203
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
1079
1204
|
|
1080
1205
|
ev_async_stop(backend->ev_loop, &async);
|
1081
1206
|
if (RTEST(raise)) RAISE_IF_EXCEPTION(switchpoint_result);
|
@@ -1112,6 +1237,21 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
|
1112
1237
|
return result;
|
1113
1238
|
}
|
1114
1239
|
|
1240
|
+
VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
|
1241
|
+
Backend_t *backend;
|
1242
|
+
GetBackend(self, backend);
|
1243
|
+
backend->base.idle_gc_period = NUM2DBL(period);
|
1244
|
+
backend->base.idle_gc_last_time = current_time();
|
1245
|
+
return self;
|
1246
|
+
}
|
1247
|
+
|
1248
|
+
inline VALUE Backend_run_idle_tasks(VALUE self) {
|
1249
|
+
Backend_t *backend;
|
1250
|
+
GetBackend(self, backend);
|
1251
|
+
backend_run_idle_tasks(&backend->base);
|
1252
|
+
return self;
|
1253
|
+
}
|
1254
|
+
|
1115
1255
|
void Init_Backend() {
|
1116
1256
|
ev_set_allocator(xrealloc);
|
1117
1257
|
|
@@ -1126,6 +1266,7 @@ void Init_Backend() {
|
|
1126
1266
|
rb_define_method(cBackend, "break", Backend_wakeup, 0);
|
1127
1267
|
rb_define_method(cBackend, "kind", Backend_kind, 0);
|
1128
1268
|
rb_define_method(cBackend, "chain", Backend_chain, -1);
|
1269
|
+
rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
|
1129
1270
|
|
1130
1271
|
rb_define_method(cBackend, "accept", Backend_accept, 2);
|
1131
1272
|
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
@@ -1143,6 +1284,9 @@ void Init_Backend() {
|
|
1143
1284
|
#ifdef POLYPHONY_LINUX
|
1144
1285
|
rb_define_method(cBackend, "splice", Backend_splice, 3);
|
1145
1286
|
rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
|
1287
|
+
#else
|
1288
|
+
rb_define_method(cBackend, "splice", Backend_fake_splice, 3);
|
1289
|
+
rb_define_method(cBackend, "splice_to_eof", Backend_fake_splice_to_eof, 3);
|
1146
1290
|
#endif
|
1147
1291
|
|
1148
1292
|
rb_define_method(cBackend, "timeout", Backend_timeout, -1);
|
@@ -1152,7 +1296,6 @@ void Init_Backend() {
|
|
1152
1296
|
rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
|
1153
1297
|
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
1154
1298
|
|
1155
|
-
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
1156
1299
|
SYM_libev = ID2SYM(rb_intern("libev"));
|
1157
1300
|
|
1158
1301
|
SYM_send = ID2SYM(rb_intern("send"));
|
data/ext/polyphony/polyphony.c
CHANGED
@@ -10,6 +10,7 @@ ID ID_each;
|
|
10
10
|
ID ID_inspect;
|
11
11
|
ID ID_invoke;
|
12
12
|
ID ID_new;
|
13
|
+
ID ID_ivar_blocking_mode;
|
13
14
|
ID ID_ivar_io;
|
14
15
|
ID ID_ivar_runnable;
|
15
16
|
ID ID_ivar_running;
|
@@ -158,19 +159,20 @@ void Init_Polyphony() {
|
|
158
159
|
|
159
160
|
cTimeoutException = rb_define_class_under(mPolyphony, "TimeoutException", rb_eException);
|
160
161
|
|
161
|
-
ID_call
|
162
|
-
ID_caller
|
163
|
-
ID_clear
|
164
|
-
ID_each
|
165
|
-
ID_inspect
|
166
|
-
ID_invoke
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
162
|
+
ID_call = rb_intern("call");
|
163
|
+
ID_caller = rb_intern("caller");
|
164
|
+
ID_clear = rb_intern("clear");
|
165
|
+
ID_each = rb_intern("each");
|
166
|
+
ID_inspect = rb_intern("inspect");
|
167
|
+
ID_invoke = rb_intern("invoke");
|
168
|
+
ID_ivar_blocking_mode = rb_intern("@blocking_mode");
|
169
|
+
ID_ivar_io = rb_intern("@io");
|
170
|
+
ID_ivar_runnable = rb_intern("@runnable");
|
171
|
+
ID_ivar_running = rb_intern("@running");
|
172
|
+
ID_ivar_thread = rb_intern("@thread");
|
173
|
+
ID_new = rb_intern("new");
|
174
|
+
ID_signal = rb_intern("signal");
|
175
|
+
ID_size = rb_intern("size");
|
176
|
+
ID_switch_fiber = rb_intern("switch_fiber");
|
177
|
+
ID_transfer = rb_intern("transfer");
|
176
178
|
}
|
data/ext/polyphony/polyphony.h
CHANGED
@@ -47,6 +47,7 @@ extern ID ID_fiber_trace;
|
|
47
47
|
extern ID ID_inspect;
|
48
48
|
extern ID ID_invoke;
|
49
49
|
extern ID ID_ivar_backend;
|
50
|
+
extern ID ID_ivar_blocking_mode;
|
50
51
|
extern ID ID_ivar_io;
|
51
52
|
extern ID ID_ivar_runnable;
|
52
53
|
extern ID ID_ivar_running;
|
@@ -124,6 +125,7 @@ unsigned int Backend_pending_count(VALUE self);
|
|
124
125
|
VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue);
|
125
126
|
VALUE Backend_wait_event(VALUE self, VALUE raise_on_exception);
|
126
127
|
VALUE Backend_wakeup(VALUE self);
|
128
|
+
VALUE Backend_run_idle_tasks(VALUE self);
|
127
129
|
|
128
130
|
VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
|
129
131
|
VALUE Thread_schedule_fiber_with_priority(VALUE thread, VALUE fiber, VALUE value);
|
data/ext/polyphony/thread.c
CHANGED
@@ -88,6 +88,7 @@ VALUE Thread_switch_fiber(VALUE self) {
|
|
88
88
|
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
89
89
|
unsigned int pending_ops_count = Backend_pending_count(backend);
|
90
90
|
unsigned int backend_was_polled = 0;
|
91
|
+
unsigned int idle_tasks_run_count = 0;
|
91
92
|
|
92
93
|
if (__tracing_enabled__ && (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse))
|
93
94
|
TRACE(2, SYM_fiber_switchpoint, current_fiber);
|
@@ -112,6 +113,10 @@ VALUE Thread_switch_fiber(VALUE self) {
|
|
112
113
|
break;
|
113
114
|
}
|
114
115
|
|
116
|
+
if (!idle_tasks_run_count) {
|
117
|
+
idle_tasks_run_count++;
|
118
|
+
Backend_run_idle_tasks(backend);
|
119
|
+
}
|
115
120
|
if (pending_ops_count == 0) break;
|
116
121
|
Backend_poll(backend, Qnil, current_fiber, runqueue);
|
117
122
|
backend_was_polled = 1;
|
data/lib/polyphony/version.rb
CHANGED
data/test/test_backend.rb
CHANGED
@@ -281,6 +281,33 @@ class BackendTest < MiniTest::Test
|
|
281
281
|
f.await
|
282
282
|
end
|
283
283
|
end
|
284
|
+
|
285
|
+
def test_idle_gc
|
286
|
+
GC.disable
|
287
|
+
|
288
|
+
count = GC.count
|
289
|
+
snooze
|
290
|
+
assert_equal count, GC.count
|
291
|
+
sleep 0.01
|
292
|
+
assert_equal count, GC.count
|
293
|
+
|
294
|
+
@backend.idle_gc_period = 0.1
|
295
|
+
snooze
|
296
|
+
assert_equal count, GC.count
|
297
|
+
sleep 0.05
|
298
|
+
assert_equal count, GC.count
|
299
|
+
# The idle tasks are ran at most once per fiber switch, before the backend
|
300
|
+
# is polled. Therefore, the second sleep will not have triggered a GC, since
|
301
|
+
# only 0.05s have passed since the gc period was set.
|
302
|
+
sleep 0.07
|
303
|
+
assert_equal count, GC.count
|
304
|
+
# Upon the third sleep the GC should be triggered, at 0.12s post setting the
|
305
|
+
# GC period.
|
306
|
+
sleep 0.05
|
307
|
+
assert_equal count + 1, GC.count
|
308
|
+
ensure
|
309
|
+
GC.enable
|
310
|
+
end
|
284
311
|
end
|
285
312
|
|
286
313
|
class BackendChainTest < MiniTest::Test
|
data/test/test_timer.rb
CHANGED
@@ -48,7 +48,7 @@ class TimerMoveOnAfterTest < MiniTest::Test
|
|
48
48
|
t1 = Time.now
|
49
49
|
|
50
50
|
assert_nil v
|
51
|
-
assert_in_range 0.015..0.
|
51
|
+
assert_in_range 0.015..0.04, t1 - t0
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -76,17 +76,17 @@ class TimerCancelAfterTest < MiniTest::Test
|
|
76
76
|
|
77
77
|
def test_timer_cancel_after_with_reset
|
78
78
|
buf = []
|
79
|
-
@timer.cancel_after(0.
|
80
|
-
sleep 0.
|
79
|
+
@timer.cancel_after(0.13) do
|
80
|
+
sleep 0.05
|
81
81
|
buf << 1
|
82
82
|
@timer.reset
|
83
|
-
sleep 0.
|
83
|
+
sleep 0.05
|
84
84
|
buf << 2
|
85
85
|
@timer.reset
|
86
|
-
sleep 0.
|
86
|
+
sleep 0.05
|
87
87
|
buf << 3
|
88
88
|
@timer.reset
|
89
|
-
sleep 0.
|
89
|
+
sleep 0.05
|
90
90
|
buf << 4
|
91
91
|
end
|
92
92
|
assert_equal [1, 2, 3, 4], buf
|
@@ -158,6 +158,6 @@ class TimerMiscTest < MiniTest::Test
|
|
158
158
|
end
|
159
159
|
sleep 0.05
|
160
160
|
f.stop
|
161
|
-
assert_in_range
|
161
|
+
assert_in_range 3..7, buffer.size
|
162
162
|
end
|
163
163
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: polyphony
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.56.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-06-
|
11
|
+
date: 2021-06-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -324,6 +324,7 @@ files:
|
|
324
324
|
- examples/core/erlang-style-genserver.rb
|
325
325
|
- examples/core/forking.rb
|
326
326
|
- examples/core/handling-signals.rb
|
327
|
+
- examples/core/idle_gc.rb
|
327
328
|
- examples/core/interrupt.rb
|
328
329
|
- examples/core/nested.rb
|
329
330
|
- examples/core/pingpong.rb
|
@@ -355,6 +356,7 @@ files:
|
|
355
356
|
- examples/io/irb.rb
|
356
357
|
- examples/io/net-http.rb
|
357
358
|
- examples/io/open.rb
|
359
|
+
- examples/io/pipe.rb
|
358
360
|
- examples/io/pry.rb
|
359
361
|
- examples/io/rack_server.rb
|
360
362
|
- examples/io/raw.rb
|
@@ -414,6 +416,7 @@ files:
|
|
414
416
|
- ext/liburing/setup.c
|
415
417
|
- ext/liburing/syscall.c
|
416
418
|
- ext/liburing/syscall.h
|
419
|
+
- ext/polyphony/backend_common.c
|
417
420
|
- ext/polyphony/backend_common.h
|
418
421
|
- ext/polyphony/backend_io_uring.c
|
419
422
|
- ext/polyphony/backend_io_uring_context.c
|