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.
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ Exception.__disable_sanitized_backtrace__ = true
7
+
8
+ timers = 10.times.map do
9
+ spin do
10
+ t = Gyro::Timer.new(1, 1)
11
+ t.await
12
+ end
13
+ end
14
+
15
+ sleep 0.1
16
+ GC.start
17
+ sleep 0.1
data/ext/gyro/async.c CHANGED
@@ -4,8 +4,10 @@ struct Gyro_Async {
4
4
  struct ev_async ev_async;
5
5
  struct ev_loop *ev_loop;
6
6
  int active;
7
+ VALUE self;
7
8
  VALUE fiber;
8
9
  VALUE value;
10
+ VALUE selector;
9
11
  };
10
12
 
11
13
  VALUE cGyro_Async = Qnil;
@@ -18,12 +20,16 @@ static void Gyro_Async_mark(void *ptr) {
18
20
  if (async->value != Qnil) {
19
21
  rb_gc_mark(async->value);
20
22
  }
23
+ if (async->selector != Qnil) {
24
+ rb_gc_mark(async->selector);
25
+ }
21
26
  }
22
27
 
23
28
  static void Gyro_Async_free(void *ptr) {
24
29
  struct Gyro_Async *async = ptr;
25
30
  if (async->active) {
26
- printf("Async watcher garbage collected while still active!\n");
31
+ ev_clear_pending(async->ev_loop, &async->ev_async);
32
+ ev_async_stop(async->ev_loop, &async->ev_async);
27
33
  }
28
34
  xfree(async);
29
35
  }
@@ -35,26 +41,42 @@ static size_t Gyro_Async_size(const void *ptr) {
35
41
  static const rb_data_type_t Gyro_Async_type = {
36
42
  "Gyro_Async",
37
43
  {Gyro_Async_mark, Gyro_Async_free, Gyro_Async_size,},
38
- 0, 0,
39
- RUBY_TYPED_FREE_IMMEDIATELY,
44
+ 0, 0, 0
40
45
  };
41
46
 
42
47
  static VALUE Gyro_Async_allocate(VALUE klass) {
43
- struct Gyro_Async *async = (struct Gyro_Async *)xmalloc(sizeof(struct Gyro_Async));
48
+ struct Gyro_Async *async = ALLOC(struct Gyro_Async);
44
49
  return TypedData_Wrap_Struct(klass, &Gyro_Async_type, async);
45
50
  }
46
51
 
47
- void Gyro_Async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
48
- struct Gyro_Async *async = (struct Gyro_Async*)ev_async;
52
+ inline void Gyro_Async_activate(struct Gyro_Async *async) {
53
+ if (async->active) return;
49
54
 
50
- ev_async_stop(async->ev_loop, ev_async);
55
+ async->active = 1;
56
+ async->fiber = rb_fiber_current();
57
+ async->selector = Thread_current_event_selector();
58
+ async->ev_loop = Gyro_Selector_ev_loop(async->selector);
59
+ Gyro_Selector_add_active_watcher(async->selector, async->self);
60
+ ev_async_start(async->ev_loop, &async->ev_async);
61
+ }
62
+
63
+ inline void Gyro_Async_deactivate(struct Gyro_Async *async) {
64
+ if (!async->active) return;
65
+
66
+ ev_async_stop(async->ev_loop, &async->ev_async);
67
+ Gyro_Selector_remove_active_watcher(async->selector, async->self);
51
68
  async->active = 0;
69
+ async->ev_loop = 0;
70
+ async->selector = Qnil;
71
+ async->fiber = Qnil;
72
+ async->value = Qnil;
73
+ }
52
74
 
53
- if (async->fiber != Qnil) {
54
- Gyro_schedule_fiber(async->fiber, async->value);
55
- async->fiber = Qnil;
56
- async->value = Qnil;
57
- }
75
+ void Gyro_Async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
76
+ struct Gyro_Async *async = (struct Gyro_Async*)ev_async;
77
+
78
+ Fiber_make_runnable(async->fiber, async->value);
79
+ Gyro_Async_deactivate(async);
58
80
  }
59
81
 
60
82
  #define GetGyro_Async(obj, async) \
@@ -64,12 +86,14 @@ static VALUE Gyro_Async_initialize(VALUE self) {
64
86
  struct Gyro_Async *async;
65
87
  GetGyro_Async(self, async);
66
88
 
89
+ async->self = self;
67
90
  async->fiber = Qnil;
68
91
  async->value = Qnil;
92
+ async->selector = Qnil;
69
93
  async->active = 0;
94
+ async->ev_loop = 0;
70
95
 
71
96
  ev_async_init(&async->ev_async, Gyro_Async_callback);
72
- async->ev_loop = 0;
73
97
 
74
98
  return Qnil;
75
99
  }
@@ -79,7 +103,7 @@ static VALUE Gyro_Async_signal(int argc, VALUE *argv, VALUE self) {
79
103
  GetGyro_Async(self, async);
80
104
 
81
105
  if (!async->active) {
82
- // printf("signal! called before await\n");
106
+ // printf("signal called before await\n");
83
107
  return Qnil;
84
108
  }
85
109
 
@@ -91,68 +115,34 @@ static VALUE Gyro_Async_signal(int argc, VALUE *argv, VALUE self) {
91
115
 
92
116
  VALUE Gyro_Async_await(VALUE self) {
93
117
  struct Gyro_Async *async;
94
- VALUE ret;
95
-
96
118
  GetGyro_Async(self, async);
97
119
 
98
- async->fiber = rb_fiber_current();
99
- if (!async->active) {
100
- async->active = 1;
101
- async->ev_loop = Gyro_Selector_current_thread_ev_loop();
102
- ev_async_start(async->ev_loop, &async->ev_async);
103
- }
120
+ Gyro_Async_activate(async);
121
+ VALUE ret = Gyro_switchpoint();
122
+ Gyro_Async_deactivate(async);
104
123
 
105
- ret = Fiber_await();
124
+ TEST_RESUME_EXCEPTION(ret);
106
125
  RB_GC_GUARD(ret);
107
-
108
- if (async->active) {
109
- async->active = 0;
110
- async->fiber = Qnil;
111
- ev_async_stop(async->ev_loop, &async->ev_async);
112
- async->value = Qnil;
113
- }
114
-
115
- // fiber is resumed
116
- if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
117
- return rb_funcall(rb_mKernel, ID_raise, 1, ret);
118
- }
119
- else {
120
- return ret;
121
- }
126
+ return ret;
122
127
  }
123
128
 
124
129
  VALUE Gyro_Async_await_no_raise(VALUE self) {
125
130
  struct Gyro_Async *async;
126
- VALUE ret;
127
-
128
131
  GetGyro_Async(self, async);
129
132
 
130
- async->fiber = rb_fiber_current();
131
- if (!async->active) {
132
- async->active = 1;
133
- async->ev_loop = Gyro_Selector_current_thread_ev_loop();
134
- ev_async_start(async->ev_loop, &async->ev_async);
135
- }
136
-
137
- ret = Fiber_await();
133
+ Gyro_Async_activate(async);
134
+ VALUE ret = Gyro_switchpoint();
135
+ Gyro_Async_deactivate(async);
136
+
138
137
  RB_GC_GUARD(ret);
139
-
140
- if (async->active) {
141
- async->active = 0;
142
- async->fiber = Qnil;
143
- ev_async_stop(async->ev_loop, &async->ev_async);
144
- async->value = Qnil;
145
- }
146
-
147
138
  return ret;
148
139
  }
149
140
 
150
-
151
141
  void Init_Gyro_Async() {
152
142
  cGyro_Async = rb_define_class_under(mGyro, "Async", rb_cData);
153
143
  rb_define_alloc_func(cGyro_Async, Gyro_Async_allocate);
154
144
 
155
145
  rb_define_method(cGyro_Async, "initialize", Gyro_Async_initialize, 0);
156
- rb_define_method(cGyro_Async, "signal!", Gyro_Async_signal, -1);
146
+ rb_define_method(cGyro_Async, "signal", Gyro_Async_signal, -1);
157
147
  rb_define_method(cGyro_Async, "await", Gyro_Async_await, 0);
158
148
  }
data/ext/gyro/child.c CHANGED
@@ -7,6 +7,7 @@ struct Gyro_Child {
7
7
  int pid;
8
8
  VALUE self;
9
9
  VALUE fiber;
10
+ VALUE selector;
10
11
  };
11
12
 
12
13
  static VALUE cGyro_Child = Qnil;
@@ -16,12 +17,16 @@ static void Gyro_Child_mark(void *ptr) {
16
17
  if (child->fiber != Qnil) {
17
18
  rb_gc_mark(child->fiber);
18
19
  }
20
+ if (child->selector != Qnil) {
21
+ rb_gc_mark(child->selector);
22
+ }
19
23
  }
20
24
 
21
25
  static void Gyro_Child_free(void *ptr) {
22
26
  struct Gyro_Child *child = ptr;
23
27
  if (child->active) {
24
- printf("Child watcher garbage collected while still active!\n");
28
+ ev_clear_pending(child->ev_loop, &child->ev_child);
29
+ ev_child_stop(child->ev_loop, &child->ev_child);
25
30
  }
26
31
  xfree(child);
27
32
  }
@@ -33,31 +38,51 @@ static size_t Gyro_Child_size(const void *ptr) {
33
38
  static const rb_data_type_t Gyro_Child_type = {
34
39
  "Gyro_Child",
35
40
  {Gyro_Child_mark, Gyro_Child_free, Gyro_Child_size,},
36
- 0, 0,
37
- RUBY_TYPED_FREE_IMMEDIATELY,
41
+ 0, 0, 0
38
42
  };
39
43
 
40
44
  static VALUE Gyro_Child_allocate(VALUE klass) {
41
- struct Gyro_Child *child = (struct Gyro_Child *)xmalloc(sizeof(struct Gyro_Child));
45
+ struct Gyro_Child *child = ALLOC(struct Gyro_Child);
42
46
  return TypedData_Wrap_Struct(klass, &Gyro_Child_type, child);
43
47
  }
44
48
 
49
+ inline void Gyro_Child_activate(struct Gyro_Child *child) {
50
+ if (child->active) return;
51
+
52
+ child->active = 1;
53
+ child->fiber = rb_fiber_current();
54
+ child->selector = Thread_current_event_selector();
55
+ child->ev_loop = Gyro_Selector_ev_loop(child->selector);
56
+ Gyro_Selector_add_active_watcher(child->selector, child->self);
57
+ ev_child_start(child->ev_loop, &child->ev_child);
58
+ }
59
+
60
+ inline void Gyro_Child_deactivate(struct Gyro_Child *child) {
61
+ if (!child->active) return;
62
+
63
+ ev_child_stop(child->ev_loop, &child->ev_child);
64
+ Gyro_Selector_remove_active_watcher(child->selector, child->self);
65
+ child->active = 0;
66
+ child->ev_loop = 0;
67
+ child->selector = Qnil;
68
+ child->fiber = Qnil;
69
+ }
70
+
71
+ VALUE Gyro_Child_resume_value(struct ev_child *ev_child) {
72
+ int exit_status = ev_child->rstatus >> 8; // weird, why should we do this?
73
+
74
+ return rb_ary_new_from_args(
75
+ 2, INT2NUM(ev_child->rpid), INT2NUM(exit_status)
76
+ );
77
+ }
78
+
45
79
  void Gyro_Child_callback(struct ev_loop *ev_loop, struct ev_child *ev_child, int revents) {
46
80
  struct Gyro_Child *child = (struct Gyro_Child*)ev_child;
47
81
 
48
- child->active = 0;
49
- ev_child_stop(child->ev_loop, ev_child);
82
+ VALUE resume_value = Gyro_Child_resume_value(ev_child);
83
+ Fiber_make_runnable(child->fiber, resume_value);
50
84
 
51
- if (child->fiber != Qnil) {
52
- VALUE fiber = child->fiber;
53
- int exit_status = ev_child->rstatus >> 8; // weird, why should we do this?
54
-
55
- VALUE resume_value = rb_ary_new_from_args(
56
- 2, INT2NUM(ev_child->rpid), INT2NUM(exit_status)
57
- );
58
- child->fiber = Qnil;
59
- Gyro_schedule_fiber(fiber, resume_value);
60
- }
85
+ Gyro_Child_deactivate(child);
61
86
  }
62
87
 
63
88
  #define GetGyro_Child(obj, child) \
@@ -70,8 +95,10 @@ static VALUE Gyro_Child_initialize(VALUE self, VALUE pid) {
70
95
 
71
96
  child->self = self;
72
97
  child->fiber = Qnil;
98
+ child->selector = Qnil;
73
99
  child->pid = NUM2INT(pid);
74
100
  child->active = 0;
101
+ child->ev_loop = 0;
75
102
 
76
103
  ev_child_init(&child->ev_child, Gyro_Child_callback, child->pid, 0);
77
104
 
@@ -80,32 +107,15 @@ static VALUE Gyro_Child_initialize(VALUE self, VALUE pid) {
80
107
 
81
108
  static VALUE Gyro_Child_await(VALUE self) {
82
109
  struct Gyro_Child *child;
83
- VALUE ret;
84
-
85
110
  GetGyro_Child(self, child);
86
111
 
87
- if (child->active)
88
- child->active = 1;
89
- child->fiber = rb_fiber_current();
90
- child->ev_loop = Gyro_Selector_current_thread_ev_loop();
91
- ev_child_start(child->ev_loop, &child->ev_child);
112
+ Gyro_Child_activate(child);
113
+ VALUE ret = Gyro_switchpoint();
114
+ Gyro_Child_deactivate(child);
92
115
 
93
- ret = Fiber_await();
116
+ TEST_RESUME_EXCEPTION(ret);
94
117
  RB_GC_GUARD(ret);
95
-
96
- if (child->active) {
97
- child->active = 0;
98
- child->fiber = Qnil;
99
- ev_child_stop(child->ev_loop, &child->ev_child);
100
- }
101
-
102
- // fiber is resumed, check if resumed value is an exception
103
- if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
104
- return rb_funcall(rb_mKernel, ID_raise, 1, ret);
105
- }
106
- else {
107
- return ret;
108
- }
118
+ return ret;
109
119
  }
110
120
 
111
121
  void Init_Gyro_Child() {
data/ext/gyro/fiber.c ADDED
@@ -0,0 +1,113 @@
1
+ #include "gyro.h"
2
+
3
+ ID ID_fiber_trace;
4
+ ID ID_ivar_auto_async;
5
+ ID ID_ivar_auto_io;
6
+ ID ID_trace_ev_loop_enter;
7
+ ID ID_trace_ev_loop_leave;
8
+ ID ID_trace_run;
9
+ ID ID_trace_runnable;
10
+ ID ID_trace_terminate;
11
+ ID ID_trace_wait;
12
+
13
+ VALUE SYM_dead;
14
+ VALUE SYM_running;
15
+ VALUE SYM_runnable;
16
+ VALUE SYM_waiting;
17
+
18
+ VALUE SYM_fiber_create;
19
+ VALUE SYM_fiber_ev_loop_enter;
20
+ VALUE SYM_fiber_ev_loop_leave;
21
+ VALUE SYM_fiber_run;
22
+ VALUE SYM_fiber_schedule;
23
+ VALUE SYM_fiber_switchpoint;
24
+ VALUE SYM_fiber_terminate;
25
+
26
+ static VALUE Fiber_safe_transfer(int argc, VALUE *argv, VALUE self) {
27
+ VALUE arg = (argc == 0) ? Qnil : argv[0];
28
+ VALUE ret = rb_funcall(self, ID_transfer, 1, arg);
29
+
30
+ TEST_RESUME_EXCEPTION(ret);
31
+ RB_GC_GUARD(ret);
32
+ return ret;
33
+ }
34
+
35
+ inline VALUE Fiber_auto_async(VALUE self) {
36
+ VALUE async = rb_ivar_get(self, ID_ivar_auto_async);
37
+ if (async == Qnil) {
38
+ async = rb_funcall(cGyro_Async, ID_new, 0);
39
+ rb_ivar_set(self, ID_ivar_auto_async, async);
40
+ }
41
+ return async;
42
+ }
43
+
44
+ inline VALUE Fiber_auto_io(VALUE self) {
45
+ VALUE io = rb_ivar_get(self, ID_ivar_auto_io);
46
+ if (io == Qnil) {
47
+ io = rb_funcall(cGyro_IO, ID_new, 2, Qnil, Qnil);
48
+ rb_ivar_set(self, ID_ivar_auto_io, io);
49
+ }
50
+ return io;
51
+ }
52
+
53
+ static VALUE Fiber_schedule(int argc, VALUE *argv, VALUE self) {
54
+ VALUE value = (argc == 0) ? Qnil : argv[0];
55
+ Fiber_make_runnable(self, value);
56
+ return self;
57
+ }
58
+
59
+ static VALUE Fiber_state(VALUE self) {
60
+ if (!rb_fiber_alive_p(self) || (rb_ivar_get(self, ID_ivar_running) == Qfalse))
61
+ return SYM_dead;
62
+ if (rb_fiber_current() == self) return SYM_running;
63
+ if (rb_ivar_get(self, ID_runnable) != Qnil) return SYM_runnable;
64
+
65
+ return SYM_waiting;
66
+ }
67
+
68
+ void Fiber_make_runnable(VALUE fiber, VALUE value) {
69
+ VALUE thread = rb_ivar_get(fiber, ID_ivar_thread);
70
+ if (thread != Qnil) {
71
+ Thread_schedule_fiber(thread, fiber, value);
72
+ }
73
+ else {
74
+ rb_warn("No thread set for fiber");
75
+ }
76
+ }
77
+
78
+ void Init_Fiber() {
79
+ VALUE cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
80
+ rb_define_method(cFiber, "auto_async", Fiber_auto_async, 0);
81
+ rb_define_method(cFiber, "safe_transfer", Fiber_safe_transfer, -1);
82
+ rb_define_method(cFiber, "schedule", Fiber_schedule, -1);
83
+ rb_define_method(cFiber, "state", Fiber_state, 0);
84
+
85
+ ID_ivar_auto_async = rb_intern("@auto_async");
86
+
87
+ SYM_dead = ID2SYM(rb_intern("dead"));
88
+ SYM_running = ID2SYM(rb_intern("running"));
89
+ SYM_runnable = ID2SYM(rb_intern("runnable"));
90
+ SYM_waiting = ID2SYM(rb_intern("waiting"));
91
+ rb_global_variable(&SYM_dead);
92
+ rb_global_variable(&SYM_running);
93
+ rb_global_variable(&SYM_runnable);
94
+ rb_global_variable(&SYM_waiting);
95
+
96
+ ID_fiber_trace = rb_intern("__fiber_trace__");
97
+
98
+ SYM_fiber_create = ID2SYM(rb_intern("fiber_create"));
99
+ SYM_fiber_ev_loop_enter = ID2SYM(rb_intern("fiber_ev_loop_enter"));
100
+ SYM_fiber_ev_loop_leave = ID2SYM(rb_intern("fiber_ev_loop_leave"));
101
+ SYM_fiber_run = ID2SYM(rb_intern("fiber_run"));
102
+ SYM_fiber_schedule = ID2SYM(rb_intern("fiber_schedule"));
103
+ SYM_fiber_switchpoint = ID2SYM(rb_intern("fiber_switchpoint"));
104
+ SYM_fiber_terminate = ID2SYM(rb_intern("fiber_terminate"));
105
+
106
+ rb_global_variable(&SYM_fiber_create);
107
+ rb_global_variable(&SYM_fiber_ev_loop_enter);
108
+ rb_global_variable(&SYM_fiber_ev_loop_leave);
109
+ rb_global_variable(&SYM_fiber_run);
110
+ rb_global_variable(&SYM_fiber_schedule);
111
+ rb_global_variable(&SYM_fiber_switchpoint);
112
+ rb_global_variable(&SYM_fiber_terminate);
113
+ }