polyphony 0.43.6 → 0.44.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/Gemfile.lock +5 -1
  4. data/README.md +20 -5
  5. data/TODO.md +10 -14
  6. data/bin/stress.rb +28 -0
  7. data/docs/getting-started/overview.md +2 -2
  8. data/examples/adapters/sequel_mysql.rb +23 -0
  9. data/examples/adapters/sequel_mysql_pool.rb +33 -0
  10. data/examples/core/xx-channels.rb +4 -2
  11. data/examples/core/xx-using-a-mutex.rb +2 -1
  12. data/examples/performance/fiber_transfer.rb +47 -0
  13. data/ext/polyphony/agent.h +41 -0
  14. data/ext/polyphony/event.c +86 -0
  15. data/ext/polyphony/fiber.c +0 -5
  16. data/ext/polyphony/libev_agent.c +201 -128
  17. data/ext/polyphony/polyphony.c +4 -2
  18. data/ext/polyphony/polyphony.h +24 -24
  19. data/ext/polyphony/polyphony_ext.c +4 -2
  20. data/ext/polyphony/queue.c +208 -0
  21. data/ext/polyphony/ring_buffer.c +0 -24
  22. data/ext/polyphony/thread.c +53 -38
  23. data/lib/polyphony.rb +13 -31
  24. data/lib/polyphony/adapters/mysql2.rb +19 -0
  25. data/lib/polyphony/adapters/sequel.rb +45 -0
  26. data/lib/polyphony/core/channel.rb +3 -34
  27. data/lib/polyphony/core/exceptions.rb +11 -0
  28. data/lib/polyphony/core/resource_pool.rb +23 -72
  29. data/lib/polyphony/core/sync.rb +12 -9
  30. data/lib/polyphony/extensions/core.rb +15 -8
  31. data/lib/polyphony/extensions/fiber.rb +4 -0
  32. data/lib/polyphony/extensions/socket.rb +9 -9
  33. data/lib/polyphony/extensions/thread.rb +1 -1
  34. data/lib/polyphony/net.rb +2 -1
  35. data/lib/polyphony/version.rb +1 -1
  36. data/polyphony.gemspec +2 -0
  37. data/test/helper.rb +2 -2
  38. data/test/test_agent.rb +2 -2
  39. data/test/test_event.rb +12 -0
  40. data/test/test_fiber.rb +17 -1
  41. data/test/test_io.rb +14 -0
  42. data/test/test_queue.rb +33 -0
  43. data/test/test_resource_pool.rb +34 -43
  44. data/test/test_signal.rb +2 -26
  45. data/test/test_socket.rb +0 -43
  46. data/test/test_trace.rb +18 -17
  47. metadata +40 -5
  48. data/ext/polyphony/libev_queue.c +0 -288
  49. data/lib/polyphony/event.rb +0 -27
@@ -2,12 +2,12 @@
2
2
 
3
3
  VALUE mPolyphony;
4
4
 
5
- ID ID_await_no_raise;
6
5
  ID ID_call;
7
6
  ID ID_caller;
8
7
  ID ID_clear;
9
8
  ID ID_each;
10
9
  ID ID_inspect;
10
+ ID ID_invoke;
11
11
  ID ID_new;
12
12
  ID ID_raise;
13
13
  ID ID_ivar_running;
@@ -22,6 +22,8 @@ ID ID_R;
22
22
  ID ID_W;
23
23
  ID ID_RW;
24
24
 
25
+ agent_interface_t agent_interface;
26
+
25
27
  VALUE Polyphony_snooze(VALUE self) {
26
28
  VALUE ret;
27
29
  VALUE fiber = rb_fiber_current();
@@ -54,12 +56,12 @@ void Init_Polyphony() {
54
56
  rb_define_global_function("snooze", Polyphony_snooze, 0);
55
57
  rb_define_global_function("suspend", Polyphony_suspend, 0);
56
58
 
57
- ID_await_no_raise = rb_intern("await_no_raise");
58
59
  ID_call = rb_intern("call");
59
60
  ID_caller = rb_intern("caller");
60
61
  ID_clear = rb_intern("clear");
61
62
  ID_each = rb_intern("each");
62
63
  ID_inspect = rb_intern("inspect");
64
+ ID_invoke = rb_intern("invoke");
63
65
  ID_ivar_running = rb_intern("@running");
64
66
  ID_ivar_thread = rb_intern("@thread");
65
67
  ID_new = rb_intern("new");
@@ -4,31 +4,41 @@
4
4
  #include "ruby.h"
5
5
  #include "ruby/io.h"
6
6
  #include "libev.h"
7
+ #include "agent.h"
7
8
 
8
9
  // debugging
9
10
  #define OBJ_ID(obj) (NUM2LONG(rb_funcall(obj, rb_intern("object_id"), 0)))
10
- #define INSPECT(obj) { VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); printf("%s\n", StringValueCStr(s));}
11
- #define FIBER_TRACE(...) if (__tracing_enabled__) { \
12
- rb_funcall(rb_cObject, ID_fiber_trace, __VA_ARGS__); \
11
+ #define INSPECT(str, obj) { printf(str); VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); printf("%s\n", StringValueCStr(s)); }
12
+ #define TRACE_CALLER() { VALUE c = rb_funcall(rb_mKernel, rb_intern("caller"), 0); INSPECT("caller: ", c); }
13
+
14
+ // tracing
15
+ #define TRACE(...) rb_funcall(rb_cObject, ID_fiber_trace, __VA_ARGS__)
16
+ #define COND_TRACE(...) if (__tracing_enabled__) { \
17
+ TRACE(__VA_ARGS__); \
13
18
  }
14
19
 
15
20
  #define TEST_EXCEPTION(ret) (RTEST(rb_obj_is_kind_of(ret, rb_eException)))
16
21
 
22
+ #define RAISE_EXCEPTION(e) rb_funcall(e, ID_invoke, 0);
17
23
  #define TEST_RESUME_EXCEPTION(ret) if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) { \
18
- return rb_funcall(rb_mKernel, ID_raise, 1, ret); \
24
+ return RAISE_EXCEPTION(ret); \
19
25
  }
20
26
 
27
+
28
+ extern agent_interface_t agent_interface;
29
+ #define __AGENT__ (agent_interface)
30
+
21
31
  extern VALUE mPolyphony;
22
- extern VALUE cLibevQueue;
32
+ extern VALUE cQueue;
23
33
  extern VALUE cEvent;
24
34
 
25
- extern ID ID_await_no_raise;
26
35
  extern ID ID_call;
27
36
  extern ID ID_caller;
28
37
  extern ID ID_clear;
29
38
  extern ID ID_each;
30
39
  extern ID ID_fiber_trace;
31
40
  extern ID ID_inspect;
41
+ extern ID ID_invoke;
32
42
  extern ID ID_ivar_agent;
33
43
  extern ID ID_ivar_running;
34
44
  extern ID ID_ivar_thread;
@@ -66,24 +76,14 @@ enum {
66
76
  VALUE Fiber_auto_watcher(VALUE self);
67
77
  void Fiber_make_runnable(VALUE fiber, VALUE value);
68
78
 
69
- VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue);
70
- VALUE LibevAgent_break(VALUE self);
71
- VALUE LibevAgent_pending_count(VALUE self);
72
- VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write);
73
-
74
- VALUE LibevAgent_ref(VALUE self);
75
- VALUE LibevAgent_unref(VALUE self);
76
- int LibevAgent_ref_count(VALUE self);
77
- void LibevAgent_reset_ref_count(VALUE self);
78
-
79
- VALUE LibevQueue_push(VALUE self, VALUE value);
80
- VALUE LibevQueue_unshift(VALUE self, VALUE value);
81
- VALUE LibevQueue_shift(VALUE self);
82
- VALUE LibevQueue_shift_no_wait(VALUE self);
83
- VALUE LibevQueue_clear(VALUE self);
84
- VALUE LibevQueue_delete(VALUE self, VALUE value);
85
- long LibevQueue_len(VALUE self);
86
- void LibevQueue_trace(VALUE self);
79
+ VALUE Queue_push(VALUE self, VALUE value);
80
+ VALUE Queue_unshift(VALUE self, VALUE value);
81
+ VALUE Queue_shift(VALUE self);
82
+ VALUE Queue_shift_no_wait(VALUE self);
83
+ VALUE Queue_clear(VALUE self);
84
+ VALUE Queue_delete(VALUE self, VALUE value);
85
+ long Queue_len(VALUE self);
86
+ void Queue_trace(VALUE self);
87
87
 
88
88
  VALUE Polyphony_snooze(VALUE self);
89
89
 
@@ -3,7 +3,8 @@
3
3
  void Init_Fiber();
4
4
  void Init_Polyphony();
5
5
  void Init_LibevAgent();
6
- void Init_LibevQueue();
6
+ void Init_Queue();
7
+ void Init_Event();
7
8
  void Init_Thread();
8
9
  void Init_Tracing();
9
10
 
@@ -12,7 +13,8 @@ void Init_polyphony_ext() {
12
13
 
13
14
  Init_Polyphony();
14
15
  Init_LibevAgent();
15
- Init_LibevQueue();
16
+ Init_Queue();
17
+ Init_Event();
16
18
 
17
19
  Init_Fiber();
18
20
  Init_Thread();
@@ -0,0 +1,208 @@
1
+ #include "polyphony.h"
2
+ #include "ring_buffer.h"
3
+
4
+ typedef struct queue {
5
+ ring_buffer values;
6
+ ring_buffer shift_queue;
7
+ } Queue_t;
8
+
9
+ VALUE cQueue = Qnil;
10
+
11
+ static void Queue_mark(void *ptr) {
12
+ Queue_t *queue = ptr;
13
+ ring_buffer_mark(&queue->values);
14
+ ring_buffer_mark(&queue->shift_queue);
15
+ }
16
+
17
+ static void Queue_free(void *ptr) {
18
+ Queue_t *queue = ptr;
19
+ ring_buffer_free(&queue->values);
20
+ ring_buffer_free(&queue->shift_queue);
21
+ xfree(ptr);
22
+ }
23
+
24
+ static size_t Queue_size(const void *ptr) {
25
+ return sizeof(Queue_t);
26
+ }
27
+
28
+ static const rb_data_type_t Queue_type = {
29
+ "Queue",
30
+ {Queue_mark, Queue_free, Queue_size,},
31
+ 0, 0, 0
32
+ };
33
+
34
+ static VALUE Queue_allocate(VALUE klass) {
35
+ Queue_t *queue;
36
+
37
+ queue = ALLOC(Queue_t);
38
+ return TypedData_Wrap_Struct(klass, &Queue_type, queue);
39
+ }
40
+
41
+ #define GetQueue(obj, queue) \
42
+ TypedData_Get_Struct((obj), Queue_t, &Queue_type, (queue))
43
+
44
+ static VALUE Queue_initialize(VALUE self) {
45
+ Queue_t *queue;
46
+ GetQueue(self, queue);
47
+
48
+ ring_buffer_init(&queue->values);
49
+ ring_buffer_init(&queue->shift_queue);
50
+
51
+ return self;
52
+ }
53
+
54
+ VALUE Queue_push(VALUE self, VALUE value) {
55
+ Queue_t *queue;
56
+ GetQueue(self, queue);
57
+
58
+ if (queue->shift_queue.count > 0) {
59
+ VALUE fiber = ring_buffer_shift(&queue->shift_queue);
60
+ if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
61
+ }
62
+ ring_buffer_push(&queue->values, value);
63
+ return self;
64
+ }
65
+
66
+ VALUE Queue_unshift(VALUE self, VALUE value) {
67
+ Queue_t *queue;
68
+ GetQueue(self, queue);
69
+ if (queue->shift_queue.count > 0) {
70
+ VALUE fiber = ring_buffer_shift(&queue->shift_queue);
71
+ if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
72
+ }
73
+ ring_buffer_unshift(&queue->values, value);
74
+ return self;
75
+ }
76
+
77
+ VALUE Queue_shift(VALUE self) {
78
+ Queue_t *queue;
79
+ GetQueue(self, queue);
80
+
81
+ VALUE fiber = rb_fiber_current();
82
+ VALUE thread = rb_thread_current();
83
+ VALUE agent = rb_ivar_get(thread, ID_ivar_agent);
84
+
85
+ while (1) {
86
+ ring_buffer_push(&queue->shift_queue, fiber);
87
+ if (queue->values.count > 0) Fiber_make_runnable(fiber, Qnil);
88
+
89
+ VALUE switchpoint_result = __AGENT__.wait_event(agent, Qnil);
90
+ ring_buffer_delete(&queue->shift_queue, fiber);
91
+
92
+ if (RTEST(rb_obj_is_kind_of(switchpoint_result, rb_eException)))
93
+ return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
94
+ RB_GC_GUARD(switchpoint_result);
95
+
96
+ if (queue->values.count > 0)
97
+ return ring_buffer_shift(&queue->values);
98
+ }
99
+
100
+ return Qnil;
101
+ }
102
+
103
+ VALUE Queue_shift_no_wait(VALUE self) {
104
+ Queue_t *queue;
105
+ GetQueue(self, queue);
106
+
107
+ return ring_buffer_shift(&queue->values);
108
+ }
109
+
110
+ VALUE Queue_delete(VALUE self, VALUE value) {
111
+ Queue_t *queue;
112
+ GetQueue(self, queue);
113
+
114
+ ring_buffer_delete(&queue->values, value);
115
+ return self;
116
+ }
117
+
118
+ VALUE Queue_clear(VALUE self) {
119
+ Queue_t *queue;
120
+ GetQueue(self, queue);
121
+
122
+ ring_buffer_clear(&queue->values);
123
+ return self;
124
+ }
125
+
126
+ long Queue_len(VALUE self) {
127
+ Queue_t *queue;
128
+ GetQueue(self, queue);
129
+
130
+ return queue->values.count;
131
+ }
132
+
133
+ VALUE Queue_shift_each(VALUE self) {
134
+ Queue_t *queue;
135
+ GetQueue(self, queue);
136
+
137
+ ring_buffer_shift_each(&queue->values);
138
+ return self;
139
+ }
140
+
141
+ VALUE Queue_shift_all(VALUE self) {
142
+ Queue_t *queue;
143
+ GetQueue(self, queue);
144
+
145
+ return ring_buffer_shift_all(&queue->values);
146
+ }
147
+
148
+ VALUE Queue_flush_waiters(VALUE self, VALUE value) {
149
+ Queue_t *queue;
150
+ GetQueue(self, queue);
151
+
152
+ while(1) {
153
+ VALUE fiber = ring_buffer_shift(&queue->shift_queue);
154
+ if (fiber == Qnil) return self;
155
+
156
+ Fiber_make_runnable(fiber, value);
157
+ }
158
+ }
159
+
160
+ VALUE Queue_empty_p(VALUE self) {
161
+ Queue_t *queue;
162
+ GetQueue(self, queue);
163
+
164
+ return (queue->values.count == 0) ? Qtrue : Qfalse;
165
+ }
166
+
167
+ VALUE Queue_pending_p(VALUE self) {
168
+ Queue_t *queue;
169
+ GetQueue(self, queue);
170
+
171
+ return (queue->shift_queue.count > 0) ? Qtrue : Qfalse;
172
+ }
173
+
174
+ VALUE Queue_size_m(VALUE self) {
175
+ Queue_t *queue;
176
+ GetQueue(self, queue);
177
+
178
+ return INT2NUM(queue->values.count);
179
+ }
180
+
181
+ void Queue_trace(VALUE self) {
182
+ Queue_t *queue;
183
+ GetQueue(self, queue);
184
+
185
+ printf("run queue size: %d count: %d\n", queue->values.size, queue->values.count);
186
+ }
187
+
188
+ void Init_Queue() {
189
+ cQueue = rb_define_class_under(mPolyphony, "Queue", rb_cData);
190
+ rb_define_alloc_func(cQueue, Queue_allocate);
191
+
192
+ rb_define_method(cQueue, "initialize", Queue_initialize, 0);
193
+ rb_define_method(cQueue, "push", Queue_push, 1);
194
+ rb_define_method(cQueue, "<<", Queue_push, 1);
195
+ rb_define_method(cQueue, "unshift", Queue_unshift, 1);
196
+
197
+ rb_define_method(cQueue, "shift", Queue_shift, 0);
198
+ rb_define_method(cQueue, "pop", Queue_shift, 0);
199
+ rb_define_method(cQueue, "shift_no_wait", Queue_shift_no_wait, 0);
200
+ rb_define_method(cQueue, "delete", Queue_delete, 1);
201
+
202
+ rb_define_method(cQueue, "shift_each", Queue_shift_each, 0);
203
+ rb_define_method(cQueue, "shift_all", Queue_shift_all, 0);
204
+ rb_define_method(cQueue, "flush_waiters", Queue_flush_waiters, 1);
205
+ rb_define_method(cQueue, "empty?", Queue_empty_p, 0);
206
+ rb_define_method(cQueue, "pending?", Queue_pending_p, 0);
207
+ rb_define_method(cQueue, "size", Queue_size_m, 0);
208
+ }
@@ -17,18 +17,7 @@ int ring_buffer_empty_p(ring_buffer *buffer) {
17
17
  return buffer->count == 0;
18
18
  }
19
19
 
20
- #define TRACE_RING_BUFFER(func, buffer) printf( \
21
- "%s size: %d count: %d head: %d tail: %d\n", \
22
- func, \
23
- buffer->size, \
24
- buffer->count, \
25
- buffer->head, \
26
- buffer->tail \
27
- )
28
-
29
20
  VALUE ring_buffer_shift(ring_buffer *buffer) {
30
- // TRACE_RING_BUFFER("ring_buffer_shift", buffer);
31
-
32
21
  VALUE value;
33
22
  if (buffer->count == 0) return Qnil;
34
23
 
@@ -40,11 +29,8 @@ VALUE ring_buffer_shift(ring_buffer *buffer) {
40
29
  }
41
30
 
42
31
  void ring_buffer_resize(ring_buffer *buffer) {
43
- // TRACE_RING_BUFFER("ring_buffer_resize", buffer);
44
-
45
32
  unsigned int old_size = buffer->size;
46
33
  buffer->size = old_size == 1 ? 4 : old_size * 2;
47
- // printf("new size: %d\n", buffer->size);
48
34
  buffer->entries = realloc(buffer->entries, buffer->size * sizeof(VALUE));
49
35
  for (unsigned int idx = 0; idx < buffer->head && idx < buffer->tail; idx++)
50
36
  buffer->entries[old_size + idx] = buffer->entries[idx];
@@ -52,9 +38,6 @@ void ring_buffer_resize(ring_buffer *buffer) {
52
38
  }
53
39
 
54
40
  void ring_buffer_unshift(ring_buffer *buffer, VALUE value) {
55
- // TRACE_RING_BUFFER("ring_buffer_unshift", buffer);
56
- // INSPECT(value);
57
-
58
41
  if (buffer->count == buffer->size) ring_buffer_resize(buffer);
59
42
 
60
43
  buffer->head = (buffer->head - 1) % buffer->size;
@@ -63,8 +46,6 @@ void ring_buffer_unshift(ring_buffer *buffer, VALUE value) {
63
46
  }
64
47
 
65
48
  void ring_buffer_push(ring_buffer *buffer, VALUE value) {
66
- // TRACE_RING_BUFFER("ring_buffer_push", buffer);
67
- // INSPECT(value);
68
49
  if (buffer->count == buffer->size) ring_buffer_resize(buffer);
69
50
 
70
51
  buffer->entries[buffer->tail] = value;
@@ -78,8 +59,6 @@ void ring_buffer_mark(ring_buffer *buffer) {
78
59
  }
79
60
 
80
61
  void ring_buffer_shift_each(ring_buffer *buffer) {
81
- // TRACE_RING_BUFFER("ring_buffer_shift_each", buffer);
82
-
83
62
  for (unsigned int i = 0; i < buffer->count; i++)
84
63
  rb_yield(buffer->entries[(buffer->head + i) % buffer->size]);
85
64
 
@@ -87,7 +66,6 @@ void ring_buffer_shift_each(ring_buffer *buffer) {
87
66
  }
88
67
 
89
68
  VALUE ring_buffer_shift_all(ring_buffer *buffer) {
90
- // TRACE_RING_BUFFER("ring_buffer_all", buffer);
91
69
  VALUE array = rb_ary_new_capa(buffer->count);
92
70
  for (unsigned int i = 0; i < buffer->count; i++)
93
71
  rb_ary_push(array, buffer->entries[(buffer->head + i) % buffer->size]);
@@ -104,7 +82,6 @@ void ring_buffer_delete_at(ring_buffer *buffer, unsigned int idx) {
104
82
  }
105
83
 
106
84
  void ring_buffer_delete(ring_buffer *buffer, VALUE value) {
107
- // TRACE_RING_BUFFER("ring_buffer_delete", buffer);
108
85
  for (unsigned int i = 0; i < buffer->count; i++) {
109
86
  unsigned int idx = (buffer->head + i) % buffer->size;
110
87
  if (buffer->entries[idx] == value) {
@@ -115,6 +92,5 @@ void ring_buffer_delete(ring_buffer *buffer, VALUE value) {
115
92
  }
116
93
 
117
94
  void ring_buffer_clear(ring_buffer *buffer) {
118
- // TRACE_RING_BUFFER("ring_buffer_clear", buffer);
119
95
  buffer->count = buffer->head = buffer->tail = 0;
120
96
  }
@@ -11,7 +11,7 @@ ID ID_runnable_next;
11
11
  ID ID_stop;
12
12
 
13
13
  static VALUE Thread_setup_fiber_scheduling(VALUE self) {
14
- VALUE queue = rb_funcall(cLibevQueue, ID_new, 0);
14
+ VALUE queue = rb_funcall(cQueue, ID_new, 0);
15
15
 
16
16
  rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
17
17
  rb_ivar_set(self, ID_run_queue, queue);
@@ -21,12 +21,12 @@ static VALUE Thread_setup_fiber_scheduling(VALUE self) {
21
21
 
22
22
  int Thread_fiber_ref_count(VALUE self) {
23
23
  VALUE agent = rb_ivar_get(self, ID_ivar_agent);
24
- return NUM2INT(LibevAgent_ref_count(agent));
24
+ return NUM2INT(__AGENT__.ref_count(agent));
25
25
  }
26
26
 
27
27
  inline void Thread_fiber_reset_ref_count(VALUE self) {
28
28
  VALUE agent = rb_ivar_get(self, ID_ivar_agent);
29
- LibevAgent_reset_ref_count(agent);
29
+ __AGENT__.reset_ref_count(agent);
30
30
  }
31
31
 
32
32
  static VALUE SYM_scheduled_fibers;
@@ -41,7 +41,7 @@ static VALUE Thread_fiber_scheduling_stats(VALUE self) {
41
41
  long scheduled_count = RARRAY_LEN(queue);
42
42
  rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
43
43
 
44
- pending_count = LibevAgent_pending_count(agent);
44
+ pending_count = __AGENT__.pending_count(agent);
45
45
  rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(pending_count));
46
46
 
47
47
  return stats;
@@ -52,25 +52,34 @@ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
52
52
 
53
53
  if (rb_fiber_alive_p(fiber) != Qtrue) return self;
54
54
 
55
- FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
56
- // if fiber is already scheduled, just set the scheduled value, then return
57
- rb_ivar_set(fiber, ID_runnable_value, value);
58
- if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
59
- return self;
55
+ int already_runnable = rb_ivar_get(fiber, ID_runnable) != Qnil;
56
+
57
+ if (already_runnable) {
58
+ VALUE current_runnable_value = rb_ivar_get(fiber, ID_runnable_value);
59
+
60
+ // If the fiber is already runnable and the runnable value is an exception,
61
+ // we don't update the value, in order to prevent a race condition where
62
+ // exceptions will be lost (see issue #33)
63
+ if (TEST_EXCEPTION(current_runnable_value)) return self;
60
64
  }
61
65
 
62
- queue = rb_ivar_get(self, ID_run_queue);
63
- LibevQueue_push(queue, fiber);
64
- rb_ivar_set(fiber, ID_runnable, Qtrue);
66
+ rb_ivar_set(fiber, ID_runnable_value, value);
67
+ COND_TRACE(3, SYM_fiber_schedule, fiber, value);
65
68
 
66
- if (rb_thread_current() != self) {
67
- // if the fiber scheduling is done across threads, we need to make sure the
68
- // target thread is woken up in case it is in the middle of running its
69
- // event selector. Otherwise it's gonna be stuck waiting for an event to
70
- // happen, not knowing that it there's already a fiber ready to run in its
71
- // run queue.
72
- VALUE agent = rb_ivar_get(self,ID_ivar_agent);
73
- LibevAgent_break(agent);
69
+ if (!already_runnable) {
70
+ queue = rb_ivar_get(self, ID_run_queue);
71
+ Queue_push(queue, fiber);
72
+ rb_ivar_set(fiber, ID_runnable, Qtrue);
73
+
74
+ if (rb_thread_current() != self) {
75
+ // If the fiber scheduling is done across threads, we need to make sure the
76
+ // target thread is woken up in case it is in the middle of running its
77
+ // event selector. Otherwise it's gonna be stuck waiting for an event to
78
+ // happen, not knowing that it there's already a fiber ready to run in its
79
+ // run queue.
80
+ VALUE agent = rb_ivar_get(self,ID_ivar_agent);
81
+ __AGENT__.wakeup(agent);
82
+ }
74
83
  }
75
84
  return self;
76
85
  }
@@ -80,20 +89,20 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
80
89
 
81
90
  if (rb_fiber_alive_p(fiber) != Qtrue) return self;
82
91
 
83
- FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
92
+ COND_TRACE(3, SYM_fiber_schedule, fiber, value);
84
93
  rb_ivar_set(fiber, ID_runnable_value, value);
85
94
 
86
95
  queue = rb_ivar_get(self, ID_run_queue);
87
96
 
88
97
  // if fiber is already scheduled, remove it from the run queue
89
98
  if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
90
- LibevQueue_delete(queue, fiber);
99
+ Queue_delete(queue, fiber);
91
100
  } else {
92
101
  rb_ivar_set(fiber, ID_runnable, Qtrue);
93
102
  }
94
103
 
95
104
  // the fiber is given priority by putting it at the front of the run queue
96
- LibevQueue_unshift(queue, fiber);
105
+ Queue_unshift(queue, fiber);
97
106
 
98
107
  if (rb_thread_current() != self) {
99
108
  // if the fiber scheduling is done across threads, we need to make sure the
@@ -102,7 +111,7 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
102
111
  // happen, not knowing that it there's already a fiber ready to run in its
103
112
  // run queue.
104
113
  VALUE agent = rb_ivar_get(self, ID_ivar_agent);
105
- LibevAgent_break(agent);
114
+ __AGENT__.wakeup(agent);
106
115
  }
107
116
  return self;
108
117
  }
@@ -114,34 +123,33 @@ VALUE Thread_switch_fiber(VALUE self) {
114
123
  VALUE value;
115
124
  VALUE agent = rb_ivar_get(self, ID_ivar_agent);
116
125
  int ref_count;
126
+ int agent_was_polled = 0;
117
127
 
118
- if (__tracing_enabled__) {
119
- if (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse) {
120
- rb_funcall(rb_cObject, ID_fiber_trace, 2, SYM_fiber_switchpoint, current_fiber);
121
- }
122
- }
128
+ if (__tracing_enabled__ && (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse))
129
+ TRACE(2, SYM_fiber_switchpoint, current_fiber);
123
130
 
124
- ref_count = LibevAgent_ref_count(agent);
131
+ ref_count = __AGENT__.ref_count(agent);
125
132
  while (1) {
126
- next_fiber = LibevQueue_shift_no_wait(queue);
133
+ next_fiber = Queue_shift_no_wait(queue);
127
134
  if (next_fiber != Qnil) {
128
- if (ref_count > 0) {
135
+ if (agent_was_polled == 0 && ref_count > 0) {
129
136
  // this mechanism prevents event starvation in case the run queue never
130
137
  // empties
131
- LibevAgent_poll(agent, Qtrue, current_fiber, queue);
138
+ __AGENT__.poll(agent, Qtrue, current_fiber, queue);
132
139
  }
133
140
  break;
134
141
  }
135
142
  if (ref_count == 0) break;
136
143
 
137
- LibevAgent_poll(agent, Qnil, current_fiber, queue);
144
+ __AGENT__.poll(agent, Qnil, current_fiber, queue);
145
+ agent_was_polled = 1;
138
146
  }
139
147
 
140
148
  if (next_fiber == Qnil) return Qnil;
141
149
 
142
150
  // run next fiber
143
151
  value = rb_ivar_get(next_fiber, ID_runnable_value);
144
- FIBER_TRACE(3, SYM_fiber_run, next_fiber, value);
152
+ COND_TRACE(3, SYM_fiber_run, next_fiber, value);
145
153
 
146
154
  rb_ivar_set(next_fiber, ID_runnable, Qnil);
147
155
  RB_GC_GUARD(next_fiber);
@@ -152,13 +160,13 @@ VALUE Thread_switch_fiber(VALUE self) {
152
160
 
153
161
  VALUE Thread_run_queue_trace(VALUE self) {
154
162
  VALUE queue = rb_ivar_get(self, ID_run_queue);
155
- LibevQueue_trace(queue);
163
+ Queue_trace(queue);
156
164
  return self;
157
165
  }
158
166
 
159
167
  VALUE Thread_reset_fiber_scheduling(VALUE self) {
160
168
  VALUE queue = rb_ivar_get(self, ID_run_queue);
161
- LibevQueue_clear(queue);
169
+ Queue_clear(queue);
162
170
  Thread_fiber_reset_ref_count(self);
163
171
  return self;
164
172
  }
@@ -169,7 +177,7 @@ VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_ob
169
177
  Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
170
178
  }
171
179
 
172
- if (LibevAgent_break(agent) == Qnil) {
180
+ if (__AGENT__.wakeup(agent) == Qnil) {
173
181
  // we're not inside the ev_loop, so we just do a switchpoint
174
182
  Thread_switch_fiber(self);
175
183
  }
@@ -177,6 +185,11 @@ VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_ob
177
185
  return self;
178
186
  }
179
187
 
188
+ VALUE Thread_debug(VALUE self) {
189
+ rb_ivar_set(self, rb_intern("@__debug__"), Qtrue);
190
+ return self;
191
+ }
192
+
180
193
  void Init_Thread() {
181
194
  rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
182
195
  rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
@@ -189,6 +202,8 @@ void Init_Thread() {
189
202
  rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
190
203
  rb_define_method(rb_cThread, "run_queue_trace", Thread_run_queue_trace, 0);
191
204
 
205
+ rb_define_method(rb_cThread, "debug!", Thread_debug, 0);
206
+
192
207
  ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
193
208
  ID_ivar_agent = rb_intern("@agent");
194
209
  ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");