polyphony 0.34 → 0.36

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.
data/ext/gyro/gyro.c CHANGED
@@ -6,47 +6,24 @@ ID ID_call;
6
6
  ID ID_caller;
7
7
  ID ID_clear;
8
8
  ID ID_each;
9
- ID ID_fiber_trace;
9
+ ID ID_empty;
10
10
  ID ID_inspect;
11
11
  ID ID_new;
12
+ ID ID_pop;
13
+ ID ID_push;
12
14
  ID ID_raise;
13
15
  ID ID_ivar_running;
14
16
  ID ID_ivar_thread;
15
17
  ID ID_runnable;
16
18
  ID ID_runnable_value;
17
19
  ID ID_size;
18
- ID ID_signal_bang;
20
+ ID ID_signal;
19
21
  ID ID_switch_fiber;
20
22
  ID ID_transfer;
21
23
  ID ID_R;
22
24
  ID ID_W;
23
25
  ID ID_RW;
24
26
 
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
27
  static VALUE Gyro_break_set(VALUE self) {
51
28
  // break_flag = 1;
52
29
  ev_break(Gyro_Selector_current_thread_ev_loop(), EVBREAK_ALL);
@@ -59,13 +36,12 @@ static VALUE Gyro_break_set(VALUE self) {
59
36
 
60
37
  VALUE Gyro_snooze(VALUE self) {
61
38
  VALUE fiber = rb_fiber_current();
62
- Gyro_schedule_fiber(fiber, Qnil);
39
+ Fiber_make_runnable(fiber, Qnil);
63
40
 
64
41
  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;
42
+ TEST_RESUME_EXCEPTION(ret);
43
+ RB_GC_GUARD(ret);
44
+ return ret;
69
45
  }
70
46
 
71
47
  static VALUE Gyro_post_fork(VALUE self) {
@@ -84,46 +60,9 @@ static VALUE Gyro_unref(VALUE self) {
84
60
  static VALUE Gyro_suspend(VALUE self) {
85
61
  VALUE ret = Thread_switch_fiber(rb_thread_current());
86
62
 
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
- }
63
+ TEST_RESUME_EXCEPTION(ret);
64
+ RB_GC_GUARD(ret);
65
+ return ret;
127
66
  }
128
67
 
129
68
  VALUE Gyro_trace(VALUE self, VALUE enabled) {
@@ -145,11 +84,6 @@ void Init_Gyro() {
145
84
  rb_define_global_function("snooze", Gyro_snooze, 0);
146
85
  rb_define_global_function("suspend", Gyro_suspend, 0);
147
86
 
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
87
  ID_call = rb_intern("call");
154
88
  ID_caller = rb_intern("caller");
155
89
  ID_clear = rb_intern("clear");
@@ -164,7 +98,7 @@ void Init_Gyro() {
164
98
  ID_raise = rb_intern("raise");
165
99
  ID_runnable = rb_intern("runnable");
166
100
  ID_runnable_value = rb_intern("runnable_value");
167
- ID_signal_bang = rb_intern("signal!");
101
+ ID_signal = rb_intern("signal");
168
102
  ID_size = rb_intern("size");
169
103
  ID_switch_fiber = rb_intern("switch_fiber");
170
104
  ID_transfer = rb_intern("transfer");
@@ -172,32 +106,4 @@ void Init_Gyro() {
172
106
  ID_R = rb_intern("r");
173
107
  ID_RW = rb_intern("rw");
174
108
  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
109
  }
data/ext/gyro/gyro.h CHANGED
@@ -5,61 +5,17 @@
5
5
  #include "ruby/io.h"
6
6
  #include "libev.h"
7
7
 
8
- enum {
9
- FIBER_STATE_NOT_SCHEDULED = 0,
10
- FIBER_STATE_WAITING = 1,
11
- FIBER_STATE_SCHEDULED = 2
12
- };
13
-
14
- // void Gyro_add_watcher_ref(VALUE obj);
15
- // void Gyro_del_watcher_ref(VALUE obj);
16
- VALUE Gyro_snooze(VALUE self);
17
-
18
- void Gyro_schedule_fiber(VALUE fiber, VALUE value);
19
-
20
- int Gyro_ref_count();
21
- void Gyro_ref_count_incr();
22
- void Gyro_ref_count_decr();
23
-
24
- VALUE Gyro_Async_await(VALUE async);
25
- VALUE Gyro_Async_await_no_raise(VALUE async);
26
-
27
- VALUE IO_read_watcher(VALUE io);
28
- VALUE IO_write_watcher(VALUE io);
29
- VALUE Gyro_IO_await(VALUE self);
30
-
31
- VALUE Gyro_Selector_run(VALUE self, VALUE current_fiber);
32
- void Gyro_Selector_run_no_wait(VALUE self, VALUE current_fiber, long runnable_count);
33
-
34
- VALUE Gyro_Timer_await(VALUE self);
35
-
36
- int io_setstrbuf(VALUE *str, long len);
37
- void io_set_read_length(VALUE str, long n, int shrinkable);
38
- VALUE io_enc_str(VALUE str, rb_io_t *fptr);
39
-
40
- struct ev_loop *Gyro_Selector_ev_loop(VALUE selector);
41
- ev_tstamp Gyro_Selector_now(VALUE selector);
42
- struct ev_loop *Gyro_Selector_current_thread_ev_loop();
43
- long Gyro_Selector_pending_count(VALUE self);
44
- VALUE Gyro_Selector_post_fork(VALUE self);
45
-
46
- VALUE Thread_current_event_selector();
47
- VALUE Thread_ref(VALUE thread);
48
- VALUE Thread_unref(VALUE thread);
49
- VALUE Thread_switch_fiber(VALUE thread);
50
- VALUE Fiber_await();
51
- VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
52
- VALUE Thread_post_fork(VALUE thread);
53
- VALUE Gyro_Selector_break_out_of_ev_loop(VALUE self);
54
-
55
- VALUE Gyro_Queue_push(VALUE self, VALUE value);
56
-
8
+ // debugging
57
9
  #define OBJ_ID(obj) (NUM2LONG(rb_funcall(obj, rb_intern("object_id"), 0)))
58
10
  #define INSPECT(...) (rb_funcall(rb_cObject, rb_intern("p"), __VA_ARGS__))
59
11
  #define FIBER_TRACE(...) if (__tracing_enabled__) { \
60
12
  rb_funcall(rb_cObject, ID_fiber_trace, __VA_ARGS__); \
61
13
  }
62
14
 
15
+ #define TEST_RESUME_EXCEPTION(ret) if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) { \
16
+ return rb_funcall(rb_mKernel, ID_raise, 1, ret); \
17
+ }
18
+
63
19
  extern VALUE mGyro;
64
20
  extern VALUE cGyro_Async;
65
21
  extern VALUE cGyro_IO;
@@ -79,7 +35,7 @@ extern ID ID_new;
79
35
  extern ID ID_raise;
80
36
  extern ID ID_runnable;
81
37
  extern ID ID_runnable_value;
82
- extern ID ID_signal_bang;
38
+ extern ID ID_signal;
83
39
  extern ID ID_size;
84
40
  extern ID ID_switch_fiber;
85
41
  extern ID ID_transfer;
@@ -97,4 +53,52 @@ extern VALUE SYM_fiber_terminate;
97
53
 
98
54
  extern int __tracing_enabled__;
99
55
 
56
+ enum {
57
+ FIBER_STATE_NOT_SCHEDULED = 0,
58
+ FIBER_STATE_WAITING = 1,
59
+ FIBER_STATE_SCHEDULED = 2
60
+ };
61
+
62
+ VALUE Fiber_auto_async(VALUE self);
63
+ VALUE Fiber_auto_io(VALUE self);
64
+ void Fiber_make_runnable(VALUE fiber, VALUE value);
65
+
66
+ VALUE Gyro_Async_await(VALUE async);
67
+ VALUE Gyro_Async_await_no_raise(VALUE async);
68
+
69
+ VALUE Gyro_IO_auto_io(int fd, int events);
70
+ VALUE Gyro_IO_await(VALUE self);
71
+
72
+ void Gyro_Selector_add_active_watcher(VALUE self, VALUE watcher);
73
+ VALUE Gyro_Selector_break_out_of_ev_loop(VALUE self);
74
+ struct ev_loop *Gyro_Selector_current_thread_ev_loop();
75
+ struct ev_loop *Gyro_Selector_ev_loop(VALUE selector);
76
+ ev_tstamp Gyro_Selector_now(VALUE selector);
77
+ long Gyro_Selector_pending_count(VALUE self);
78
+ VALUE Gyro_Selector_post_fork(VALUE self);
79
+ void Gyro_Selector_remove_active_watcher(VALUE self, VALUE watcher);
80
+ VALUE Gyro_Selector_run(VALUE self, VALUE current_fiber);
81
+ void Gyro_Selector_run_no_wait(VALUE self, VALUE current_fiber, long runnable_count);
82
+ VALUE Gyro_switchpoint();
83
+
84
+
85
+ VALUE Gyro_snooze(VALUE self);
86
+ VALUE Gyro_Timer_await(VALUE self);
87
+
88
+ VALUE IO_read_watcher(VALUE io);
89
+ VALUE IO_write_watcher(VALUE io);
90
+
91
+ VALUE Gyro_Queue_push(VALUE self, VALUE value);
92
+
93
+ VALUE Thread_current_event_selector();
94
+ VALUE Thread_post_fork(VALUE thread);
95
+ VALUE Thread_ref(VALUE thread);
96
+ VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
97
+ VALUE Thread_switch_fiber(VALUE thread);
98
+ VALUE Thread_unref(VALUE thread);
99
+
100
+ int io_setstrbuf(VALUE *str, long len);
101
+ void io_set_read_length(VALUE str, long n, int shrinkable);
102
+ VALUE io_enc_str(VALUE str, rb_io_t *fptr);
103
+
100
104
  #endif /* RUBY_EV_H */
data/ext/gyro/gyro_ext.c CHANGED
@@ -1,5 +1,6 @@
1
1
  #include "gyro.h"
2
2
 
3
+ void Init_Fiber();
3
4
  void Init_Gyro();
4
5
  void Init_Gyro_Async();
5
6
  void Init_Gyro_Child();
@@ -24,6 +25,7 @@ void Init_gyro_ext() {
24
25
  Init_Gyro_Signal();
25
26
  Init_Gyro_Timer();
26
27
 
28
+ Init_Fiber();
27
29
  Init_Socket();
28
30
  Init_Thread();
29
31
 
data/ext/gyro/io.c CHANGED
@@ -10,8 +10,9 @@ struct Gyro_IO {
10
10
  struct ev_io ev_io;
11
11
  struct ev_loop *ev_loop;
12
12
  int active;
13
- int event_mask;
13
+ VALUE self;
14
14
  VALUE fiber;
15
+ VALUE selector;
15
16
  };
16
17
 
17
18
  VALUE cGyro_IO = Qnil;
@@ -26,12 +27,16 @@ static void Gyro_IO_mark(void *ptr) {
26
27
  if (io->fiber != Qnil) {
27
28
  rb_gc_mark(io->fiber);
28
29
  }
30
+ if (io->selector != Qnil) {
31
+ rb_gc_mark(io->selector);
32
+ }
29
33
  }
30
34
 
31
35
  static void Gyro_IO_free(void *ptr) {
32
36
  struct Gyro_IO *io = ptr;
33
37
  if (io->active) {
34
- printf("IO watcher garbage collected while still active!\n");
38
+ ev_clear_pending(io->ev_loop, &io->ev_io);
39
+ ev_io_stop(io->ev_loop, &io->ev_io);
35
40
  }
36
41
  xfree(io);
37
42
  }
@@ -43,30 +48,42 @@ static size_t Gyro_IO_size(const void *ptr) {
43
48
  static const rb_data_type_t Gyro_IO_type = {
44
49
  "Gyro_IO",
45
50
  {Gyro_IO_mark, Gyro_IO_free, Gyro_IO_size,},
46
- 0, 0,
47
- RUBY_TYPED_FREE_IMMEDIATELY,
51
+ 0, 0, 0
48
52
  };
49
53
 
50
54
  static VALUE Gyro_IO_allocate(VALUE klass) {
51
- struct Gyro_IO *io = (struct Gyro_IO *)xmalloc(sizeof(struct Gyro_IO));
55
+ struct Gyro_IO *io = ALLOC(struct Gyro_IO);
52
56
 
53
57
  return TypedData_Wrap_Struct(klass, &Gyro_IO_type, io);
54
58
  }
55
59
 
56
- static const char * S_IO = "IO";
57
- static const char * S_to_io = "to_io";
60
+ inline void Gyro_IO_activate(struct Gyro_IO *io) {
61
+ if (io->active) return;
58
62
 
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;
63
+ io->active = 1;
64
+ io->fiber = rb_fiber_current();
65
+ io->selector = Thread_current_event_selector();
66
+ io->ev_loop = Gyro_Selector_ev_loop(io->selector);
67
+ Gyro_Selector_add_active_watcher(io->selector, io->self);
68
+ ev_io_start(io->ev_loop, &io->ev_io);
69
+ }
70
+
71
+ inline void Gyro_IO_deactivate(struct Gyro_IO *io) {
72
+ if (!io->active) return;
61
73
 
62
- ev_io_stop(io->ev_loop, ev_io);
74
+ ev_io_stop(io->ev_loop, &io->ev_io);
75
+ Gyro_Selector_remove_active_watcher(io->selector, io->self);
63
76
  io->active = 0;
77
+ io->ev_loop = 0;
78
+ io->selector = Qnil;
79
+ io->fiber = Qnil;
80
+ }
64
81
 
65
- if (io->fiber != Qnil) {
66
- VALUE fiber = io->fiber;
67
- io->fiber = Qnil;
68
- Gyro_schedule_fiber(fiber, Qnil);
69
- }
82
+ void Gyro_IO_callback(struct ev_loop *ev_loop, struct ev_io *ev_io, int revents) {
83
+ struct Gyro_IO *io = (struct Gyro_IO*)ev_io;
84
+
85
+ Fiber_make_runnable(io->fiber, Qnil);
86
+ Gyro_IO_deactivate(io);
70
87
  }
71
88
 
72
89
  static int Gyro_IO_symbol2event_mask(VALUE sym) {
@@ -92,51 +109,61 @@ static int Gyro_IO_symbol2event_mask(VALUE sym) {
92
109
 
93
110
  #define GetGyro_IO(obj, io) TypedData_Get_Struct((obj), struct Gyro_IO, &Gyro_IO_type, (io))
94
111
 
112
+ static const char * S_IO = "IO";
113
+ static const char * S_to_io = "to_io";
114
+
95
115
  static VALUE Gyro_IO_initialize(VALUE self, VALUE io_obj, VALUE event_mask) {
96
116
  struct Gyro_IO *io;
97
117
  rb_io_t *fptr;
98
118
 
99
119
  GetGyro_IO(self, io);
100
120
 
101
- io->event_mask = Gyro_IO_symbol2event_mask(event_mask);
121
+ io->self = self;
102
122
  io->fiber = Qnil;
123
+ io->selector = Qnil;
103
124
  io->active = 0;
125
+ io->ev_loop = 0;
104
126
 
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);
127
+ int fd;
128
+ if (NIL_P(io_obj)) {
129
+ fd = 0;
130
+ }
131
+ else {
132
+ GetOpenFile(rb_convert_type(io_obj, T_FILE, S_IO, S_to_io), fptr);
133
+ fd = FPTR_TO_FD(fptr);
134
+ }
135
+ int events = Gyro_IO_symbol2event_mask(event_mask);
136
+
137
+ ev_io_init(&io->ev_io, Gyro_IO_callback, fd, events);
107
138
 
108
139
  return Qnil;
109
140
  }
110
141
 
111
142
  VALUE Gyro_IO_await(VALUE self) {
112
143
  struct Gyro_IO *io;
113
- VALUE ret;
114
-
115
144
  GetGyro_IO(self, io);
116
145
 
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);
146
+ Gyro_IO_activate(io);
147
+ VALUE ret = Gyro_switchpoint();
148
+ Gyro_IO_deactivate(io);
121
149
 
122
- ret = Fiber_await();
150
+ TEST_RESUME_EXCEPTION(ret);
123
151
  RB_GC_GUARD(ret);
152
+ return ret;
153
+ }
124
154
 
125
- if (io->active) {
126
- io->active = 0;
127
- io->fiber = Qnil;
128
- ev_io_stop(io->ev_loop, &io->ev_io);
129
- }
155
+ VALUE Gyro_IO_auto_io(int fd, int events) {
156
+ VALUE watcher = Fiber_auto_io(rb_fiber_current());
157
+ struct Gyro_IO *io;
158
+ GetGyro_IO(watcher, io);
130
159
 
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
- }
160
+ ev_io_set(&io->ev_io, fd, events);
161
+
162
+ RB_GC_GUARD(watcher);
163
+ return watcher;
138
164
  }
139
165
 
166
+
140
167
  //////////////////////////////////////////////////////////////////////
141
168
  //////////////////////////////////////////////////////////////////////
142
169
  // the following is copied verbatim from the Ruby source code (io.c)
@@ -233,8 +260,8 @@ static VALUE IO_read(int argc, VALUE *argv, VALUE io) {
233
260
  if (n < 0) {
234
261
  int e = errno;
235
262
  if ((e == EWOULDBLOCK || e == EAGAIN)) {
236
- if (read_watcher == Qnil)
237
- read_watcher = IO_read_watcher(io);
263
+ if (NIL_P(read_watcher))
264
+ read_watcher = Gyro_IO_auto_io(fptr->fd, EV_READ);
238
265
  Gyro_IO_await(read_watcher);
239
266
  }
240
267
  else
@@ -311,8 +338,8 @@ static VALUE IO_readpartial(int argc, VALUE *argv, VALUE io) {
311
338
  if (n < 0) {
312
339
  int e = errno;
313
340
  if (e == EWOULDBLOCK || e == EAGAIN) {
314
- if (read_watcher == Qnil)
315
- read_watcher = IO_read_watcher(io);
341
+ if (NIL_P(read_watcher))
342
+ read_watcher = Gyro_IO_auto_io(fptr->fd, EV_READ);
316
343
  Gyro_IO_await(read_watcher);
317
344
  }
318
345
  else
@@ -367,8 +394,8 @@ static VALUE IO_write(int argc, VALUE *argv, VALUE io) {
367
394
  if (n < 0) {
368
395
  int e = errno;
369
396
  if (e == EWOULDBLOCK || e == EAGAIN) {
370
- if (write_watcher == Qnil)
371
- write_watcher = IO_write_watcher(io);
397
+ if (NIL_P(write_watcher))
398
+ write_watcher = Gyro_IO_auto_io(fptr->fd, EV_WRITE);
372
399
  Gyro_IO_await(write_watcher);
373
400
  }
374
401
  else {