polyphony 0.34 → 0.36

Sign up to get free protection for your applications and to get access to all the features.
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 {