polyphony 0.34 → 0.41
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/.github/workflows/test.yml +11 -2
- data/.gitignore +2 -2
- data/.rubocop.yml +30 -0
- data/CHANGELOG.md +34 -0
- data/Gemfile +0 -11
- data/Gemfile.lock +11 -10
- data/README.md +2 -1
- data/Rakefile +6 -2
- data/TODO.md +18 -95
- data/docs/_includes/head.html +40 -0
- data/docs/_includes/nav.html +5 -5
- data/docs/api-reference.md +1 -1
- data/docs/api-reference/fiber.md +18 -0
- data/docs/api-reference/gyro-async.md +57 -0
- data/docs/api-reference/gyro-child.md +29 -0
- data/docs/api-reference/gyro-queue.md +44 -0
- data/docs/api-reference/gyro-timer.md +51 -0
- data/docs/api-reference/gyro.md +25 -0
- data/docs/index.md +10 -7
- data/docs/main-concepts/design-principles.md +67 -9
- data/docs/main-concepts/extending.md +1 -1
- data/docs/main-concepts/fiber-scheduling.md +55 -72
- data/examples/core/xx-agent.rb +102 -0
- data/examples/core/xx-fork-cleanup.rb +22 -0
- data/examples/core/xx-sleeping.rb +14 -6
- data/examples/core/xx-timer-gc.rb +17 -0
- data/examples/io/tunnel.rb +48 -0
- data/examples/io/xx-irb.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +14 -25
- data/ext/{gyro → polyphony}/extconf.rb +2 -2
- data/ext/polyphony/fiber.c +112 -0
- data/ext/{gyro → polyphony}/libev.c +0 -0
- data/ext/{gyro → polyphony}/libev.h +0 -0
- data/ext/polyphony/libev_agent.c +503 -0
- data/ext/polyphony/libev_queue.c +214 -0
- data/ext/polyphony/polyphony.c +89 -0
- data/ext/{gyro/gyro.h → polyphony/polyphony.h} +49 -59
- data/ext/polyphony/polyphony_ext.c +23 -0
- data/ext/{gyro → polyphony}/socket.c +21 -19
- data/ext/{gyro → polyphony}/thread.c +55 -119
- data/ext/{gyro → polyphony}/tracing.c +1 -1
- data/lib/polyphony.rb +37 -44
- data/lib/polyphony/adapters/fs.rb +1 -4
- data/lib/polyphony/adapters/irb.rb +2 -2
- data/lib/polyphony/adapters/postgres.rb +6 -5
- data/lib/polyphony/adapters/process.rb +27 -23
- data/lib/polyphony/adapters/trace.rb +110 -105
- data/lib/polyphony/core/channel.rb +35 -35
- data/lib/polyphony/core/exceptions.rb +29 -29
- data/lib/polyphony/core/global_api.rb +94 -91
- data/lib/polyphony/core/resource_pool.rb +83 -83
- data/lib/polyphony/core/sync.rb +16 -16
- data/lib/polyphony/core/thread_pool.rb +49 -37
- data/lib/polyphony/core/throttler.rb +30 -23
- data/lib/polyphony/event.rb +27 -0
- data/lib/polyphony/extensions/core.rb +23 -14
- data/lib/polyphony/extensions/fiber.rb +269 -267
- data/lib/polyphony/extensions/io.rb +56 -26
- data/lib/polyphony/extensions/openssl.rb +5 -9
- data/lib/polyphony/extensions/socket.rb +29 -10
- data/lib/polyphony/extensions/thread.rb +19 -12
- data/lib/polyphony/net.rb +64 -60
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +3 -6
- data/test/helper.rb +14 -1
- data/test/stress.rb +17 -12
- data/test/test_agent.rb +77 -0
- data/test/{test_async.rb → test_event.rb} +17 -9
- data/test/test_ext.rb +25 -4
- data/test/test_fiber.rb +23 -14
- data/test/test_global_api.rb +5 -5
- data/test/test_io.rb +46 -24
- data/test/test_queue.rb +74 -0
- data/test/test_signal.rb +3 -40
- data/test/test_socket.rb +33 -0
- data/test/test_thread.rb +38 -16
- data/test/test_thread_pool.rb +3 -3
- data/test/test_throttler.rb +0 -1
- data/test/test_trace.rb +6 -5
- metadata +34 -39
- data/ext/gyro/async.c +0 -158
- data/ext/gyro/child.c +0 -117
- data/ext/gyro/gyro.c +0 -203
- data/ext/gyro/gyro_ext.c +0 -31
- data/ext/gyro/io.c +0 -447
- data/ext/gyro/queue.c +0 -142
- data/ext/gyro/selector.c +0 -183
- data/ext/gyro/signal.c +0 -108
- data/ext/gyro/timer.c +0 -154
- data/test/test_timer.rb +0 -56
data/ext/gyro/gyro.c
DELETED
@@ -1,203 +0,0 @@
|
|
1
|
-
#include "gyro.h"
|
2
|
-
|
3
|
-
VALUE mGyro;
|
4
|
-
|
5
|
-
ID ID_call;
|
6
|
-
ID ID_caller;
|
7
|
-
ID ID_clear;
|
8
|
-
ID ID_each;
|
9
|
-
ID ID_fiber_trace;
|
10
|
-
ID ID_inspect;
|
11
|
-
ID ID_new;
|
12
|
-
ID ID_raise;
|
13
|
-
ID ID_ivar_running;
|
14
|
-
ID ID_ivar_thread;
|
15
|
-
ID ID_runnable;
|
16
|
-
ID ID_runnable_value;
|
17
|
-
ID ID_size;
|
18
|
-
ID ID_signal_bang;
|
19
|
-
ID ID_switch_fiber;
|
20
|
-
ID ID_transfer;
|
21
|
-
ID ID_R;
|
22
|
-
ID ID_W;
|
23
|
-
ID ID_RW;
|
24
|
-
|
25
|
-
ID ID_trace_ev_loop_enter;
|
26
|
-
ID ID_trace_ev_loop_leave;
|
27
|
-
ID ID_trace_run;
|
28
|
-
ID ID_trace_runnable;
|
29
|
-
ID ID_trace_terminate;
|
30
|
-
ID ID_trace_wait;
|
31
|
-
|
32
|
-
ID ID_empty;
|
33
|
-
ID ID_pop;
|
34
|
-
ID ID_push;
|
35
|
-
|
36
|
-
VALUE SYM_dead;
|
37
|
-
VALUE SYM_running;
|
38
|
-
VALUE SYM_runnable;
|
39
|
-
VALUE SYM_waiting;
|
40
|
-
|
41
|
-
VALUE SYM_fiber_create;
|
42
|
-
VALUE SYM_fiber_ev_loop_enter;
|
43
|
-
VALUE SYM_fiber_ev_loop_leave;
|
44
|
-
VALUE SYM_fiber_run;
|
45
|
-
VALUE SYM_fiber_schedule;
|
46
|
-
VALUE SYM_fiber_switchpoint;
|
47
|
-
VALUE SYM_fiber_terminate;
|
48
|
-
|
49
|
-
|
50
|
-
static VALUE Gyro_break_set(VALUE self) {
|
51
|
-
// break_flag = 1;
|
52
|
-
ev_break(Gyro_Selector_current_thread_ev_loop(), EVBREAK_ALL);
|
53
|
-
return Qnil;
|
54
|
-
}
|
55
|
-
|
56
|
-
// static VALUE Gyro_break_get(VALUE self) {
|
57
|
-
// return (break_flag == 0) ? Qfalse : Qtrue;
|
58
|
-
// }
|
59
|
-
|
60
|
-
VALUE Gyro_snooze(VALUE self) {
|
61
|
-
VALUE fiber = rb_fiber_current();
|
62
|
-
Gyro_schedule_fiber(fiber, Qnil);
|
63
|
-
|
64
|
-
VALUE ret = Thread_switch_fiber(rb_thread_current());
|
65
|
-
if (RTEST(rb_obj_is_kind_of(ret, rb_eException)))
|
66
|
-
return rb_funcall(rb_mKernel, ID_raise, 1, ret);
|
67
|
-
else
|
68
|
-
return ret;
|
69
|
-
}
|
70
|
-
|
71
|
-
static VALUE Gyro_post_fork(VALUE self) {
|
72
|
-
Thread_post_fork(rb_thread_current());
|
73
|
-
return Qnil;
|
74
|
-
}
|
75
|
-
|
76
|
-
static VALUE Gyro_ref(VALUE self) {
|
77
|
-
return Thread_ref(rb_thread_current());
|
78
|
-
}
|
79
|
-
|
80
|
-
static VALUE Gyro_unref(VALUE self) {
|
81
|
-
return Thread_unref(rb_thread_current());
|
82
|
-
}
|
83
|
-
|
84
|
-
static VALUE Gyro_suspend(VALUE self) {
|
85
|
-
VALUE ret = Thread_switch_fiber(rb_thread_current());
|
86
|
-
|
87
|
-
if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
|
88
|
-
return rb_funcall(rb_mKernel, ID_raise, 1, ret);
|
89
|
-
}
|
90
|
-
else {
|
91
|
-
return ret;
|
92
|
-
}
|
93
|
-
}
|
94
|
-
|
95
|
-
static VALUE Fiber_safe_transfer(int argc, VALUE *argv, VALUE self) {
|
96
|
-
VALUE arg = (argc == 0) ? Qnil : argv[0];
|
97
|
-
VALUE ret = rb_funcall(self, ID_transfer, 1, arg);
|
98
|
-
|
99
|
-
// fiber is resumed, check if resumed value is an exception
|
100
|
-
return RTEST(rb_obj_is_kind_of(ret, rb_eException)) ?
|
101
|
-
rb_funcall(rb_mKernel, ID_raise, 1, ret) : ret;
|
102
|
-
}
|
103
|
-
|
104
|
-
static VALUE Fiber_schedule(int argc, VALUE *argv, VALUE self) {
|
105
|
-
VALUE value = (argc == 0) ? Qnil : argv[0];
|
106
|
-
Gyro_schedule_fiber(self, value);
|
107
|
-
return self;
|
108
|
-
}
|
109
|
-
|
110
|
-
static VALUE Fiber_state(VALUE self) {
|
111
|
-
if (!rb_fiber_alive_p(self) || (rb_ivar_get(self, ID_ivar_running) == Qfalse))
|
112
|
-
return SYM_dead;
|
113
|
-
if (rb_fiber_current() == self) return SYM_running;
|
114
|
-
if (rb_ivar_get(self, ID_runnable) != Qnil) return SYM_runnable;
|
115
|
-
|
116
|
-
return SYM_waiting;
|
117
|
-
}
|
118
|
-
|
119
|
-
void Gyro_schedule_fiber(VALUE fiber, VALUE value) {
|
120
|
-
VALUE thread = rb_ivar_get(fiber, ID_ivar_thread);
|
121
|
-
if (thread != Qnil) {
|
122
|
-
Thread_schedule_fiber(thread, fiber, value);
|
123
|
-
}
|
124
|
-
else {
|
125
|
-
rb_warn("No thread set for fiber");
|
126
|
-
}
|
127
|
-
}
|
128
|
-
|
129
|
-
VALUE Gyro_trace(VALUE self, VALUE enabled) {
|
130
|
-
__tracing_enabled__ = RTEST(enabled) ? 1 : 0;
|
131
|
-
return Qnil;
|
132
|
-
}
|
133
|
-
|
134
|
-
void Init_Gyro() {
|
135
|
-
mGyro = rb_define_module("Gyro");
|
136
|
-
|
137
|
-
rb_define_singleton_method(mGyro, "post_fork", Gyro_post_fork, 0);
|
138
|
-
rb_define_singleton_method(mGyro, "ref", Gyro_ref, 0);
|
139
|
-
rb_define_singleton_method(mGyro, "unref", Gyro_unref, 0);
|
140
|
-
rb_define_singleton_method(mGyro, "trace", Gyro_trace, 1);
|
141
|
-
|
142
|
-
rb_define_singleton_method(mGyro, "break!", Gyro_break_set, 0);
|
143
|
-
// rb_define_singleton_method(mGyro, "break?", Gyro_break_get, 0);
|
144
|
-
|
145
|
-
rb_define_global_function("snooze", Gyro_snooze, 0);
|
146
|
-
rb_define_global_function("suspend", Gyro_suspend, 0);
|
147
|
-
|
148
|
-
VALUE cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
|
149
|
-
rb_define_method(cFiber, "safe_transfer", Fiber_safe_transfer, -1);
|
150
|
-
rb_define_method(cFiber, "schedule", Fiber_schedule, -1);
|
151
|
-
rb_define_method(cFiber, "state", Fiber_state, 0);
|
152
|
-
|
153
|
-
ID_call = rb_intern("call");
|
154
|
-
ID_caller = rb_intern("caller");
|
155
|
-
ID_clear = rb_intern("clear");
|
156
|
-
ID_each = rb_intern("each");
|
157
|
-
ID_empty = rb_intern("empty?");
|
158
|
-
ID_inspect = rb_intern("inspect");
|
159
|
-
ID_ivar_running = rb_intern("@running");
|
160
|
-
ID_ivar_thread = rb_intern("@thread");
|
161
|
-
ID_new = rb_intern("new");
|
162
|
-
ID_pop = rb_intern("pop");
|
163
|
-
ID_push = rb_intern("push");
|
164
|
-
ID_raise = rb_intern("raise");
|
165
|
-
ID_runnable = rb_intern("runnable");
|
166
|
-
ID_runnable_value = rb_intern("runnable_value");
|
167
|
-
ID_signal_bang = rb_intern("signal!");
|
168
|
-
ID_size = rb_intern("size");
|
169
|
-
ID_switch_fiber = rb_intern("switch_fiber");
|
170
|
-
ID_transfer = rb_intern("transfer");
|
171
|
-
|
172
|
-
ID_R = rb_intern("r");
|
173
|
-
ID_RW = rb_intern("rw");
|
174
|
-
ID_W = rb_intern("w");
|
175
|
-
|
176
|
-
ID_fiber_trace = rb_intern("__fiber_trace__");
|
177
|
-
|
178
|
-
#define GLOBAL_SYM(sym) var = ID2SYM(rb_intern(sym)); rb_global_variable(sym)
|
179
|
-
|
180
|
-
SYM_dead = ID2SYM(rb_intern("dead"));
|
181
|
-
SYM_running = ID2SYM(rb_intern("running"));
|
182
|
-
SYM_runnable = ID2SYM(rb_intern("runnable"));
|
183
|
-
SYM_waiting = ID2SYM(rb_intern("waiting"));
|
184
|
-
rb_global_variable(&SYM_dead);
|
185
|
-
rb_global_variable(&SYM_running);
|
186
|
-
rb_global_variable(&SYM_runnable);
|
187
|
-
rb_global_variable(&SYM_waiting);
|
188
|
-
|
189
|
-
SYM_fiber_create = ID2SYM(rb_intern("fiber_create"));
|
190
|
-
SYM_fiber_ev_loop_enter = ID2SYM(rb_intern("fiber_ev_loop_enter"));
|
191
|
-
SYM_fiber_ev_loop_leave = ID2SYM(rb_intern("fiber_ev_loop_leave"));
|
192
|
-
SYM_fiber_run = ID2SYM(rb_intern("fiber_run"));
|
193
|
-
SYM_fiber_schedule = ID2SYM(rb_intern("fiber_schedule"));
|
194
|
-
SYM_fiber_switchpoint = ID2SYM(rb_intern("fiber_switchpoint"));
|
195
|
-
SYM_fiber_terminate = ID2SYM(rb_intern("fiber_terminate"));
|
196
|
-
rb_global_variable(&SYM_fiber_create);
|
197
|
-
rb_global_variable(&SYM_fiber_ev_loop_enter);
|
198
|
-
rb_global_variable(&SYM_fiber_ev_loop_leave);
|
199
|
-
rb_global_variable(&SYM_fiber_run);
|
200
|
-
rb_global_variable(&SYM_fiber_schedule);
|
201
|
-
rb_global_variable(&SYM_fiber_switchpoint);
|
202
|
-
rb_global_variable(&SYM_fiber_terminate);
|
203
|
-
}
|
data/ext/gyro/gyro_ext.c
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
#include "gyro.h"
|
2
|
-
|
3
|
-
void Init_Gyro();
|
4
|
-
void Init_Gyro_Async();
|
5
|
-
void Init_Gyro_Child();
|
6
|
-
void Init_Gyro_IO();
|
7
|
-
void Init_Gyro_Queue();
|
8
|
-
void Init_Gyro_Selector();
|
9
|
-
void Init_Gyro_Signal();
|
10
|
-
void Init_Gyro_Timer();
|
11
|
-
void Init_Socket();
|
12
|
-
void Init_Thread();
|
13
|
-
void Init_Tracing();
|
14
|
-
|
15
|
-
void Init_gyro_ext() {
|
16
|
-
ev_set_allocator(xrealloc);
|
17
|
-
|
18
|
-
Init_Gyro();
|
19
|
-
Init_Gyro_Async();
|
20
|
-
Init_Gyro_Child();
|
21
|
-
Init_Gyro_IO();
|
22
|
-
Init_Gyro_Queue();
|
23
|
-
Init_Gyro_Selector();
|
24
|
-
Init_Gyro_Signal();
|
25
|
-
Init_Gyro_Timer();
|
26
|
-
|
27
|
-
Init_Socket();
|
28
|
-
Init_Thread();
|
29
|
-
|
30
|
-
Init_Tracing();
|
31
|
-
}
|
data/ext/gyro/io.c
DELETED
@@ -1,447 +0,0 @@
|
|
1
|
-
#include "gyro.h"
|
2
|
-
|
3
|
-
#ifdef GetReadFile
|
4
|
-
# define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
|
5
|
-
#else
|
6
|
-
# define FPTR_TO_FD(fptr) fptr->fd
|
7
|
-
#endif /* GetReadFile */
|
8
|
-
|
9
|
-
struct Gyro_IO {
|
10
|
-
struct ev_io ev_io;
|
11
|
-
struct ev_loop *ev_loop;
|
12
|
-
int active;
|
13
|
-
int event_mask;
|
14
|
-
VALUE fiber;
|
15
|
-
};
|
16
|
-
|
17
|
-
VALUE cGyro_IO = Qnil;
|
18
|
-
|
19
|
-
ID ID_ivar_read_watcher;
|
20
|
-
ID ID_ivar_write_watcher;
|
21
|
-
VALUE SYM_r;
|
22
|
-
VALUE SYM_w;
|
23
|
-
|
24
|
-
static void Gyro_IO_mark(void *ptr) {
|
25
|
-
struct Gyro_IO *io = ptr;
|
26
|
-
if (io->fiber != Qnil) {
|
27
|
-
rb_gc_mark(io->fiber);
|
28
|
-
}
|
29
|
-
}
|
30
|
-
|
31
|
-
static void Gyro_IO_free(void *ptr) {
|
32
|
-
struct Gyro_IO *io = ptr;
|
33
|
-
if (io->active) {
|
34
|
-
printf("IO watcher garbage collected while still active!\n");
|
35
|
-
}
|
36
|
-
xfree(io);
|
37
|
-
}
|
38
|
-
|
39
|
-
static size_t Gyro_IO_size(const void *ptr) {
|
40
|
-
return sizeof(struct Gyro_IO);
|
41
|
-
}
|
42
|
-
|
43
|
-
static const rb_data_type_t Gyro_IO_type = {
|
44
|
-
"Gyro_IO",
|
45
|
-
{Gyro_IO_mark, Gyro_IO_free, Gyro_IO_size,},
|
46
|
-
0, 0,
|
47
|
-
RUBY_TYPED_FREE_IMMEDIATELY,
|
48
|
-
};
|
49
|
-
|
50
|
-
static VALUE Gyro_IO_allocate(VALUE klass) {
|
51
|
-
struct Gyro_IO *io = (struct Gyro_IO *)xmalloc(sizeof(struct Gyro_IO));
|
52
|
-
|
53
|
-
return TypedData_Wrap_Struct(klass, &Gyro_IO_type, io);
|
54
|
-
}
|
55
|
-
|
56
|
-
static const char * S_IO = "IO";
|
57
|
-
static const char * S_to_io = "to_io";
|
58
|
-
|
59
|
-
void Gyro_IO_callback(struct ev_loop *ev_loop, struct ev_io *ev_io, int revents) {
|
60
|
-
struct Gyro_IO *io = (struct Gyro_IO*)ev_io;
|
61
|
-
|
62
|
-
ev_io_stop(io->ev_loop, ev_io);
|
63
|
-
io->active = 0;
|
64
|
-
|
65
|
-
if (io->fiber != Qnil) {
|
66
|
-
VALUE fiber = io->fiber;
|
67
|
-
io->fiber = Qnil;
|
68
|
-
Gyro_schedule_fiber(fiber, Qnil);
|
69
|
-
}
|
70
|
-
}
|
71
|
-
|
72
|
-
static int Gyro_IO_symbol2event_mask(VALUE sym) {
|
73
|
-
ID sym_id;
|
74
|
-
|
75
|
-
if (NIL_P(sym)) {
|
76
|
-
return 0;
|
77
|
-
}
|
78
|
-
|
79
|
-
sym_id = SYM2ID(sym);
|
80
|
-
|
81
|
-
if(sym_id == ID_R) {
|
82
|
-
return EV_READ;
|
83
|
-
} else if(sym_id == ID_W) {
|
84
|
-
return EV_WRITE;
|
85
|
-
} else if(sym_id == ID_RW) {
|
86
|
-
return EV_READ | EV_WRITE;
|
87
|
-
} else {
|
88
|
-
rb_raise(rb_eArgError, "invalid interest type %s (must be :r, :w, or :rw)",
|
89
|
-
RSTRING_PTR(rb_funcall(sym, ID_inspect, 0)));
|
90
|
-
}
|
91
|
-
}
|
92
|
-
|
93
|
-
#define GetGyro_IO(obj, io) TypedData_Get_Struct((obj), struct Gyro_IO, &Gyro_IO_type, (io))
|
94
|
-
|
95
|
-
static VALUE Gyro_IO_initialize(VALUE self, VALUE io_obj, VALUE event_mask) {
|
96
|
-
struct Gyro_IO *io;
|
97
|
-
rb_io_t *fptr;
|
98
|
-
|
99
|
-
GetGyro_IO(self, io);
|
100
|
-
|
101
|
-
io->event_mask = Gyro_IO_symbol2event_mask(event_mask);
|
102
|
-
io->fiber = Qnil;
|
103
|
-
io->active = 0;
|
104
|
-
|
105
|
-
GetOpenFile(rb_convert_type(io_obj, T_FILE, S_IO, S_to_io), fptr);
|
106
|
-
ev_io_init(&io->ev_io, Gyro_IO_callback, FPTR_TO_FD(fptr), io->event_mask);
|
107
|
-
|
108
|
-
return Qnil;
|
109
|
-
}
|
110
|
-
|
111
|
-
VALUE Gyro_IO_await(VALUE self) {
|
112
|
-
struct Gyro_IO *io;
|
113
|
-
VALUE ret;
|
114
|
-
|
115
|
-
GetGyro_IO(self, io);
|
116
|
-
|
117
|
-
io->fiber = rb_fiber_current();
|
118
|
-
io->active = 1;
|
119
|
-
io->ev_loop = Gyro_Selector_current_thread_ev_loop();
|
120
|
-
ev_io_start(io->ev_loop, &io->ev_io);
|
121
|
-
|
122
|
-
ret = Fiber_await();
|
123
|
-
RB_GC_GUARD(ret);
|
124
|
-
|
125
|
-
if (io->active) {
|
126
|
-
io->active = 0;
|
127
|
-
io->fiber = Qnil;
|
128
|
-
ev_io_stop(io->ev_loop, &io->ev_io);
|
129
|
-
}
|
130
|
-
|
131
|
-
// fiber is resumed, check if resumed value is an exception
|
132
|
-
if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
|
133
|
-
return rb_funcall(rb_mKernel, ID_raise, 1, ret);
|
134
|
-
}
|
135
|
-
else {
|
136
|
-
return Qnil;
|
137
|
-
}
|
138
|
-
}
|
139
|
-
|
140
|
-
//////////////////////////////////////////////////////////////////////
|
141
|
-
//////////////////////////////////////////////////////////////////////
|
142
|
-
// the following is copied verbatim from the Ruby source code (io.c)
|
143
|
-
struct io_internal_read_struct {
|
144
|
-
int fd;
|
145
|
-
int nonblock;
|
146
|
-
void *buf;
|
147
|
-
size_t capa;
|
148
|
-
};
|
149
|
-
|
150
|
-
int io_setstrbuf(VALUE *str, long len) {
|
151
|
-
#ifdef _WIN32
|
152
|
-
len = (len + 1) & ~1L; /* round up for wide char */
|
153
|
-
#endif
|
154
|
-
if (NIL_P(*str)) {
|
155
|
-
*str = rb_str_new(0, len);
|
156
|
-
return 1;
|
157
|
-
}
|
158
|
-
else {
|
159
|
-
VALUE s = StringValue(*str);
|
160
|
-
long clen = RSTRING_LEN(s);
|
161
|
-
if (clen >= len) {
|
162
|
-
rb_str_modify(s);
|
163
|
-
return 0;
|
164
|
-
}
|
165
|
-
len -= clen;
|
166
|
-
}
|
167
|
-
rb_str_modify_expand(*str, len);
|
168
|
-
return 0;
|
169
|
-
}
|
170
|
-
|
171
|
-
#define MAX_REALLOC_GAP 4096
|
172
|
-
static void io_shrink_read_string(VALUE str, long n) {
|
173
|
-
if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
|
174
|
-
rb_str_resize(str, n);
|
175
|
-
}
|
176
|
-
}
|
177
|
-
|
178
|
-
void io_set_read_length(VALUE str, long n, int shrinkable) {
|
179
|
-
if (RSTRING_LEN(str) != n) {
|
180
|
-
rb_str_modify(str);
|
181
|
-
rb_str_set_len(str, n);
|
182
|
-
if (shrinkable) io_shrink_read_string(str, n);
|
183
|
-
}
|
184
|
-
}
|
185
|
-
|
186
|
-
static rb_encoding* io_read_encoding(rb_io_t *fptr) {
|
187
|
-
if (fptr->encs.enc) {
|
188
|
-
return fptr->encs.enc;
|
189
|
-
}
|
190
|
-
return rb_default_external_encoding();
|
191
|
-
}
|
192
|
-
|
193
|
-
VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
|
194
|
-
OBJ_TAINT(str);
|
195
|
-
rb_enc_associate(str, io_read_encoding(fptr));
|
196
|
-
return str;
|
197
|
-
}
|
198
|
-
|
199
|
-
//////////////////////////////////////////////////////////////////////
|
200
|
-
//////////////////////////////////////////////////////////////////////
|
201
|
-
|
202
|
-
static VALUE IO_read(int argc, VALUE *argv, VALUE io) {
|
203
|
-
VALUE underlying_io = rb_iv_get(io, "@io");
|
204
|
-
if (!NIL_P(underlying_io)) io = underlying_io;
|
205
|
-
|
206
|
-
long len = argc == 1 ? NUM2LONG(argv[0]) : (1 << 30);
|
207
|
-
|
208
|
-
rb_io_t *fptr;
|
209
|
-
long n;
|
210
|
-
int shrinkable;
|
211
|
-
VALUE read_watcher = Qnil;
|
212
|
-
|
213
|
-
if (len < 0) {
|
214
|
-
rb_raise(rb_eArgError, "negative length %ld given", len);
|
215
|
-
}
|
216
|
-
|
217
|
-
VALUE str = argc >= 2 ? argv[1] : Qnil;
|
218
|
-
|
219
|
-
shrinkable = io_setstrbuf(&str, len);
|
220
|
-
OBJ_TAINT(str);
|
221
|
-
GetOpenFile(io, fptr);
|
222
|
-
rb_io_check_byte_readable(fptr);
|
223
|
-
rb_io_set_nonblock(fptr);
|
224
|
-
|
225
|
-
if (len == 0)
|
226
|
-
return str;
|
227
|
-
|
228
|
-
char *buf = RSTRING_PTR(str);
|
229
|
-
long total = 0;
|
230
|
-
|
231
|
-
while (1) {
|
232
|
-
n = read(fptr->fd, buf, len);
|
233
|
-
if (n < 0) {
|
234
|
-
int e = errno;
|
235
|
-
if ((e == EWOULDBLOCK || e == EAGAIN)) {
|
236
|
-
if (read_watcher == Qnil)
|
237
|
-
read_watcher = IO_read_watcher(io);
|
238
|
-
Gyro_IO_await(read_watcher);
|
239
|
-
}
|
240
|
-
else
|
241
|
-
rb_syserr_fail(e, strerror(e));
|
242
|
-
// rb_syserr_fail_path(e, fptr->pathv);
|
243
|
-
}
|
244
|
-
else if (n == 0)
|
245
|
-
break;
|
246
|
-
else {
|
247
|
-
total = total + n;
|
248
|
-
buf += n;
|
249
|
-
len -= n;
|
250
|
-
if (len == 0)
|
251
|
-
break;
|
252
|
-
}
|
253
|
-
}
|
254
|
-
|
255
|
-
if (total == 0)
|
256
|
-
return Qnil;
|
257
|
-
|
258
|
-
io_set_read_length(str, total, shrinkable);
|
259
|
-
io_enc_str(str, fptr);
|
260
|
-
|
261
|
-
return str;
|
262
|
-
}
|
263
|
-
|
264
|
-
#define READ_DATA_PENDING_COUNT(fptr) ((fptr)->rbuf.len)
|
265
|
-
#define MEMMOVE(p1,p2,type,n) memmove((p1), (p2), sizeof(type)*(size_t)(n))
|
266
|
-
|
267
|
-
static long
|
268
|
-
read_buffered_data(char *ptr, long len, rb_io_t *fptr)
|
269
|
-
{
|
270
|
-
int n;
|
271
|
-
|
272
|
-
n = READ_DATA_PENDING_COUNT(fptr);
|
273
|
-
if (n <= 0) return 0;
|
274
|
-
if (n > len) n = (int)len;
|
275
|
-
MEMMOVE(ptr, fptr->rbuf.ptr+fptr->rbuf.off, char, n);
|
276
|
-
fptr->rbuf.off += n;
|
277
|
-
fptr->rbuf.len -= n;
|
278
|
-
return n;
|
279
|
-
}
|
280
|
-
|
281
|
-
static VALUE IO_readpartial(int argc, VALUE *argv, VALUE io) {
|
282
|
-
VALUE underlying_io = rb_iv_get(io, "@io");
|
283
|
-
if (!NIL_P(underlying_io)) io = underlying_io;
|
284
|
-
|
285
|
-
long len = argc >= 1 ? NUM2LONG(argv[0]) : 8192;
|
286
|
-
|
287
|
-
rb_io_t *fptr;
|
288
|
-
long n;
|
289
|
-
int shrinkable;
|
290
|
-
VALUE read_watcher = Qnil;
|
291
|
-
|
292
|
-
if (len < 0) {
|
293
|
-
rb_raise(rb_eArgError, "negative length %ld given", len);
|
294
|
-
}
|
295
|
-
|
296
|
-
VALUE str = argc >= 2 ? argv[1] : Qnil;
|
297
|
-
|
298
|
-
shrinkable = io_setstrbuf(&str, len);
|
299
|
-
OBJ_TAINT(str);
|
300
|
-
GetOpenFile(io, fptr);
|
301
|
-
rb_io_set_nonblock(fptr);
|
302
|
-
rb_io_check_byte_readable(fptr);
|
303
|
-
|
304
|
-
if (len == 0)
|
305
|
-
return str;
|
306
|
-
|
307
|
-
n = read_buffered_data(RSTRING_PTR(str), len, fptr);
|
308
|
-
if (n <= 0) {
|
309
|
-
while (1) {
|
310
|
-
n = read(fptr->fd, RSTRING_PTR(str), len);
|
311
|
-
if (n < 0) {
|
312
|
-
int e = errno;
|
313
|
-
if (e == EWOULDBLOCK || e == EAGAIN) {
|
314
|
-
if (read_watcher == Qnil)
|
315
|
-
read_watcher = IO_read_watcher(io);
|
316
|
-
Gyro_IO_await(read_watcher);
|
317
|
-
}
|
318
|
-
else
|
319
|
-
rb_syserr_fail(e, strerror(e));
|
320
|
-
// rb_syserr_fail_path(e, fptr->pathv);
|
321
|
-
}
|
322
|
-
else
|
323
|
-
break;
|
324
|
-
}
|
325
|
-
}
|
326
|
-
|
327
|
-
io_set_read_length(str, n, shrinkable);
|
328
|
-
io_enc_str(str, fptr);
|
329
|
-
|
330
|
-
// ensure yielding to reactor if haven't yielded while reading
|
331
|
-
// if (read_watcher == Qnil) {
|
332
|
-
// Gyro_snooze(Qnil);
|
333
|
-
// }
|
334
|
-
|
335
|
-
if (n == 0)
|
336
|
-
return Qnil;
|
337
|
-
|
338
|
-
return str;
|
339
|
-
}
|
340
|
-
|
341
|
-
static VALUE IO_write(int argc, VALUE *argv, VALUE io) {
|
342
|
-
VALUE underlying_io = rb_iv_get(io, "@io");
|
343
|
-
if (!NIL_P(underlying_io)) io = underlying_io;
|
344
|
-
|
345
|
-
long i;
|
346
|
-
long n;
|
347
|
-
long total = 0;
|
348
|
-
rb_io_t *fptr;
|
349
|
-
|
350
|
-
io = rb_io_get_write_io(io);
|
351
|
-
VALUE write_watcher = Qnil;
|
352
|
-
|
353
|
-
GetOpenFile(io, fptr);
|
354
|
-
rb_io_check_writable(fptr);
|
355
|
-
rb_io_set_nonblock(fptr);
|
356
|
-
|
357
|
-
for (i = 0; i < argc; i++) {
|
358
|
-
VALUE str = argv[i];
|
359
|
-
if (!RB_TYPE_P(str, T_STRING))
|
360
|
-
str = rb_obj_as_string(str);
|
361
|
-
char *buf = RSTRING_PTR(str);
|
362
|
-
long len = RSTRING_LEN(str);
|
363
|
-
RB_GC_GUARD(str);
|
364
|
-
while (1) {
|
365
|
-
n = write(fptr->fd, buf, len);
|
366
|
-
|
367
|
-
if (n < 0) {
|
368
|
-
int e = errno;
|
369
|
-
if (e == EWOULDBLOCK || e == EAGAIN) {
|
370
|
-
if (write_watcher == Qnil)
|
371
|
-
write_watcher = IO_write_watcher(io);
|
372
|
-
Gyro_IO_await(write_watcher);
|
373
|
-
}
|
374
|
-
else {
|
375
|
-
rb_syserr_fail(e, strerror(e));
|
376
|
-
// rb_syserr_fail_path(e, fptr->pathv);
|
377
|
-
}
|
378
|
-
}
|
379
|
-
else {
|
380
|
-
total += n;
|
381
|
-
if (n < len) {
|
382
|
-
buf += n;
|
383
|
-
len -= n;
|
384
|
-
}
|
385
|
-
else break;
|
386
|
-
}
|
387
|
-
}
|
388
|
-
}
|
389
|
-
|
390
|
-
// ensure yielding to reactor if haven't yielded while writing
|
391
|
-
// if (write_watcher == Qnil) {
|
392
|
-
// Gyro_snooze(Qnil);
|
393
|
-
// }
|
394
|
-
|
395
|
-
return LONG2FIX(total);
|
396
|
-
}
|
397
|
-
|
398
|
-
static VALUE IO_write_chevron(VALUE io, VALUE str) {
|
399
|
-
IO_write(1, &str, io);
|
400
|
-
return io;
|
401
|
-
}
|
402
|
-
|
403
|
-
VALUE IO_read_watcher(VALUE self) {
|
404
|
-
VALUE watcher = rb_ivar_get(self, ID_ivar_read_watcher);
|
405
|
-
if (watcher == Qnil) {
|
406
|
-
VALUE args[] = {self, SYM_r};
|
407
|
-
watcher = rb_class_new_instance(2, args, cGyro_IO);
|
408
|
-
rb_ivar_set(self, ID_ivar_read_watcher, watcher);
|
409
|
-
}
|
410
|
-
return watcher;
|
411
|
-
}
|
412
|
-
|
413
|
-
VALUE IO_write_watcher(VALUE self) {
|
414
|
-
VALUE watcher = rb_ivar_get(self, ID_ivar_write_watcher);
|
415
|
-
if (watcher == Qnil) {
|
416
|
-
VALUE args[] = {self, SYM_w};
|
417
|
-
watcher = rb_class_new_instance(2, args, cGyro_IO);
|
418
|
-
rb_ivar_set(self, ID_ivar_write_watcher, watcher);
|
419
|
-
}
|
420
|
-
return watcher;
|
421
|
-
}
|
422
|
-
|
423
|
-
void Init_Gyro_IO() {
|
424
|
-
cGyro_IO = rb_define_class_under(mGyro, "IO", rb_cData);
|
425
|
-
rb_define_alloc_func(cGyro_IO, Gyro_IO_allocate);
|
426
|
-
|
427
|
-
rb_define_method(cGyro_IO, "initialize", Gyro_IO_initialize, 2);
|
428
|
-
rb_define_method(cGyro_IO, "await", Gyro_IO_await, 0);
|
429
|
-
|
430
|
-
VALUE cIO = rb_const_get(rb_cObject, rb_intern("IO"));
|
431
|
-
// rb_define_method(cIO, "gets", IO_gets, -1);
|
432
|
-
rb_define_method(cIO, "read", IO_read, -1);
|
433
|
-
rb_define_method(cIO, "readpartial", IO_readpartial, -1);
|
434
|
-
rb_define_method(cIO, "write", IO_write, -1);
|
435
|
-
rb_define_method(cIO, "write_nonblock", IO_write, -1);
|
436
|
-
rb_define_method(cIO, "<<", IO_write_chevron, 1);
|
437
|
-
rb_define_method(cIO, "read_watcher", IO_read_watcher, 0);
|
438
|
-
rb_define_method(cIO, "write_watcher", IO_write_watcher, 0);
|
439
|
-
|
440
|
-
ID_ivar_read_watcher = rb_intern("@read_watcher");
|
441
|
-
ID_ivar_write_watcher = rb_intern("@write_watcher");
|
442
|
-
SYM_r = ID2SYM(rb_intern("r"));
|
443
|
-
SYM_w = ID2SYM(rb_intern("w"));
|
444
|
-
|
445
|
-
rb_global_variable(&SYM_r);
|
446
|
-
rb_global_variable(&SYM_w);
|
447
|
-
}
|