polyphony 0.43.4 → 0.43.10

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +1 -1
  3. data/CHANGELOG.md +45 -0
  4. data/Gemfile.lock +1 -1
  5. data/README.md +21 -4
  6. data/TODO.md +1 -6
  7. data/bin/stress.rb +28 -0
  8. data/docs/_includes/head.html +40 -0
  9. data/docs/_includes/title.html +1 -0
  10. data/docs/_user-guide/web-server.md +11 -11
  11. data/docs/getting-started/overview.md +2 -2
  12. data/docs/index.md +4 -3
  13. data/docs/main-concepts/design-principles.md +23 -34
  14. data/docs/main-concepts/fiber-scheduling.md +1 -1
  15. data/docs/polyphony-logo.png +0 -0
  16. data/examples/core/xx-channels.rb +4 -2
  17. data/examples/core/xx-using-a-mutex.rb +2 -1
  18. data/examples/io/xx-happy-eyeballs.rb +21 -22
  19. data/examples/io/xx-zip.rb +19 -0
  20. data/examples/performance/fiber_transfer.rb +47 -0
  21. data/examples/performance/messaging.rb +29 -0
  22. data/examples/performance/multi_snooze.rb +11 -9
  23. data/examples/xx-spin.rb +32 -0
  24. data/ext/polyphony/agent.h +39 -0
  25. data/ext/polyphony/event.c +86 -0
  26. data/ext/polyphony/fiber.c +0 -5
  27. data/ext/polyphony/libev_agent.c +231 -79
  28. data/ext/polyphony/polyphony.c +2 -2
  29. data/ext/polyphony/polyphony.h +19 -16
  30. data/ext/polyphony/polyphony_ext.c +4 -2
  31. data/ext/polyphony/queue.c +194 -0
  32. data/ext/polyphony/ring_buffer.c +96 -0
  33. data/ext/polyphony/ring_buffer.h +28 -0
  34. data/ext/polyphony/thread.c +48 -31
  35. data/lib/polyphony.rb +5 -6
  36. data/lib/polyphony/core/channel.rb +3 -34
  37. data/lib/polyphony/core/resource_pool.rb +13 -75
  38. data/lib/polyphony/core/sync.rb +12 -9
  39. data/lib/polyphony/core/thread_pool.rb +1 -1
  40. data/lib/polyphony/extensions/core.rb +9 -0
  41. data/lib/polyphony/extensions/fiber.rb +9 -2
  42. data/lib/polyphony/extensions/io.rb +16 -15
  43. data/lib/polyphony/extensions/openssl.rb +8 -0
  44. data/lib/polyphony/extensions/socket.rb +13 -9
  45. data/lib/polyphony/extensions/thread.rb +1 -1
  46. data/lib/polyphony/version.rb +1 -1
  47. data/test/helper.rb +2 -2
  48. data/test/q.rb +24 -0
  49. data/test/test_agent.rb +2 -2
  50. data/test/test_event.rb +12 -0
  51. data/test/test_global_api.rb +2 -2
  52. data/test/test_io.rb +24 -2
  53. data/test/test_queue.rb +59 -1
  54. data/test/test_resource_pool.rb +0 -43
  55. data/test/test_trace.rb +18 -17
  56. metadata +16 -5
  57. data/ext/polyphony/libev_queue.c +0 -217
  58. data/lib/polyphony/event.rb +0 -27
@@ -2,7 +2,6 @@
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;
@@ -22,6 +21,8 @@ ID ID_R;
22
21
  ID ID_W;
23
22
  ID ID_RW;
24
23
 
24
+ agent_interface_t agent_interface;
25
+
25
26
  VALUE Polyphony_snooze(VALUE self) {
26
27
  VALUE ret;
27
28
  VALUE fiber = rb_fiber_current();
@@ -54,7 +55,6 @@ void Init_Polyphony() {
54
55
  rb_define_global_function("snooze", Polyphony_snooze, 0);
55
56
  rb_define_global_function("suspend", Polyphony_suspend, 0);
56
57
 
57
- ID_await_no_raise = rb_intern("await_no_raise");
58
58
  ID_call = rb_intern("call");
59
59
  ID_caller = rb_intern("caller");
60
60
  ID_clear = rb_intern("clear");
@@ -1,9 +1,10 @@
1
- #ifndef RUBY_EV_H
2
- #define RUBY_EV_H
1
+ #ifndef POLYPHONY_H
2
+ #define POLYPHONY_H
3
3
 
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)))
@@ -18,11 +19,16 @@
18
19
  return rb_funcall(rb_mKernel, ID_raise, 1, ret); \
19
20
  }
20
21
 
22
+ extern agent_interface_t agent_interface;
23
+ // #define __AGENT_PASTER__(call) (agent_interface ## . ## call)
24
+ // #define __AGENT__(call) __AGENT_PASTER__(call)
25
+ #define __AGENT__ (agent_interface)
26
+
27
+
21
28
  extern VALUE mPolyphony;
22
- extern VALUE cLibevQueue;
29
+ extern VALUE cQueue;
23
30
  extern VALUE cEvent;
24
31
 
25
- extern ID ID_await_no_raise;
26
32
  extern ID ID_call;
27
33
  extern ID ID_caller;
28
34
  extern ID ID_clear;
@@ -66,20 +72,17 @@ enum {
66
72
  VALUE Fiber_auto_watcher(VALUE self);
67
73
  void Fiber_make_runnable(VALUE fiber, VALUE value);
68
74
 
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);
75
+ VALUE Queue_push(VALUE self, VALUE value);
76
+ VALUE Queue_unshift(VALUE self, VALUE value);
77
+ VALUE Queue_shift(VALUE self);
78
+ VALUE Queue_shift_no_wait(VALUE self);
79
+ VALUE Queue_clear(VALUE self);
80
+ VALUE Queue_delete(VALUE self, VALUE value);
81
+ long Queue_len(VALUE self);
82
+ void Queue_trace(VALUE self);
78
83
 
79
84
  VALUE Polyphony_snooze(VALUE self);
80
85
 
81
- VALUE Polyphony_Queue_push(VALUE self, VALUE value);
82
-
83
86
  VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
84
87
  VALUE Thread_switch_fiber(VALUE thread);
85
88
 
@@ -87,4 +90,4 @@ int io_setstrbuf(VALUE *str, long len);
87
90
  void io_set_read_length(VALUE str, long n, int shrinkable);
88
91
  VALUE io_enc_str(VALUE str, rb_io_t *fptr);
89
92
 
90
- #endif /* RUBY_EV_H */
93
+ #endif /* POLYPHONY_H */
@@ -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,194 @@
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
+ if (queue->shift_queue.count > 0) {
58
+ VALUE fiber = ring_buffer_shift(&queue->shift_queue);
59
+ if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
60
+ }
61
+ ring_buffer_push(&queue->values, value);
62
+ return self;
63
+ }
64
+
65
+ VALUE Queue_unshift(VALUE self, VALUE value) {
66
+ Queue_t *queue;
67
+ GetQueue(self, queue);
68
+ if (queue->shift_queue.count > 0) {
69
+ VALUE fiber = ring_buffer_shift(&queue->shift_queue);
70
+ if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
71
+ }
72
+ ring_buffer_unshift(&queue->values, value);
73
+ return self;
74
+ }
75
+
76
+ VALUE Queue_shift(VALUE self) {
77
+ Queue_t *queue;
78
+ GetQueue(self, queue);
79
+
80
+ if (queue->values.count == 0) {
81
+ VALUE agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
82
+ VALUE fiber = rb_fiber_current();
83
+ VALUE switchpoint_result = Qnil;
84
+ ring_buffer_push(&queue->shift_queue, fiber);
85
+ switchpoint_result = __AGENT__.wait_event(agent, Qnil);
86
+ if (RTEST(rb_obj_is_kind_of(switchpoint_result, rb_eException))) {
87
+ ring_buffer_delete(&queue->shift_queue, fiber);
88
+ return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
89
+ }
90
+ RB_GC_GUARD(agent);
91
+ RB_GC_GUARD(switchpoint_result);
92
+ }
93
+
94
+ return ring_buffer_shift(&queue->values);
95
+ }
96
+
97
+ VALUE Queue_shift_no_wait(VALUE self) {
98
+ Queue_t *queue;
99
+ GetQueue(self, queue);
100
+
101
+ return ring_buffer_shift(&queue->values);
102
+ }
103
+
104
+ VALUE Queue_delete(VALUE self, VALUE value) {
105
+ Queue_t *queue;
106
+ GetQueue(self, queue);
107
+
108
+ ring_buffer_delete(&queue->values, value);
109
+ return self;
110
+ }
111
+
112
+ VALUE Queue_clear(VALUE self) {
113
+ Queue_t *queue;
114
+ GetQueue(self, queue);
115
+
116
+ ring_buffer_clear(&queue->values);
117
+ return self;
118
+ }
119
+
120
+ long Queue_len(VALUE self) {
121
+ Queue_t *queue;
122
+ GetQueue(self, queue);
123
+
124
+ return queue->values.count;
125
+ }
126
+
127
+ VALUE Queue_shift_each(VALUE self) {
128
+ Queue_t *queue;
129
+ GetQueue(self, queue);
130
+
131
+ ring_buffer_shift_each(&queue->values);
132
+ return self;
133
+ }
134
+
135
+ VALUE Queue_shift_all(VALUE self) {
136
+ Queue_t *queue;
137
+ GetQueue(self, queue);
138
+
139
+ return ring_buffer_shift_all(&queue->values);
140
+ }
141
+
142
+ VALUE Queue_flush_waiters(VALUE self, VALUE value) {
143
+ Queue_t *queue;
144
+ GetQueue(self, queue);
145
+
146
+ while(1) {
147
+ VALUE fiber = ring_buffer_shift(&queue->shift_queue);
148
+ if (fiber == Qnil) return self;
149
+
150
+ Fiber_make_runnable(fiber, value);
151
+ }
152
+ }
153
+
154
+ VALUE Queue_empty_p(VALUE self) {
155
+ Queue_t *queue;
156
+ GetQueue(self, queue);
157
+
158
+ return (queue->values.count == 0) ? Qtrue : Qfalse;
159
+ }
160
+
161
+ VALUE Queue_size_m(VALUE self) {
162
+ Queue_t *queue;
163
+ GetQueue(self, queue);
164
+
165
+ return INT2NUM(queue->values.count);
166
+ }
167
+
168
+ void Queue_trace(VALUE self) {
169
+ Queue_t *queue;
170
+ GetQueue(self, queue);
171
+
172
+ printf("run queue size: %d count: %d\n", queue->values.size, queue->values.count);
173
+ }
174
+
175
+ void Init_Queue() {
176
+ cQueue = rb_define_class_under(mPolyphony, "Queue", rb_cData);
177
+ rb_define_alloc_func(cQueue, Queue_allocate);
178
+
179
+ rb_define_method(cQueue, "initialize", Queue_initialize, 0);
180
+ rb_define_method(cQueue, "push", Queue_push, 1);
181
+ rb_define_method(cQueue, "<<", Queue_push, 1);
182
+ rb_define_method(cQueue, "unshift", Queue_unshift, 1);
183
+
184
+ rb_define_method(cQueue, "shift", Queue_shift, 0);
185
+ rb_define_method(cQueue, "pop", Queue_shift, 0);
186
+ rb_define_method(cQueue, "shift_no_wait", Queue_shift_no_wait, 0);
187
+ rb_define_method(cQueue, "delete", Queue_delete, 1);
188
+
189
+ rb_define_method(cQueue, "shift_each", Queue_shift_each, 0);
190
+ rb_define_method(cQueue, "shift_all", Queue_shift_all, 0);
191
+ rb_define_method(cQueue, "flush_waiters", Queue_flush_waiters, 1);
192
+ rb_define_method(cQueue, "empty?", Queue_empty_p, 0);
193
+ rb_define_method(cQueue, "size", Queue_size_m, 0);
194
+ }
@@ -0,0 +1,96 @@
1
+ #include "polyphony.h"
2
+ #include "ring_buffer.h"
3
+
4
+ void ring_buffer_init(ring_buffer *buffer) {
5
+ buffer->size = 1;
6
+ buffer->count = 0;
7
+ buffer->entries = malloc(buffer->size * sizeof(VALUE));
8
+ buffer->head = 0;
9
+ buffer->tail = 0;
10
+ }
11
+
12
+ void ring_buffer_free(ring_buffer *buffer) {
13
+ free(buffer->entries);
14
+ }
15
+
16
+ int ring_buffer_empty_p(ring_buffer *buffer) {
17
+ return buffer->count == 0;
18
+ }
19
+
20
+ VALUE ring_buffer_shift(ring_buffer *buffer) {
21
+ VALUE value;
22
+ if (buffer->count == 0) return Qnil;
23
+
24
+ value = buffer->entries[buffer->head];
25
+ buffer->head = (buffer->head + 1) % buffer->size;
26
+ buffer->count--;
27
+ // INSPECT(value);
28
+ return value;
29
+ }
30
+
31
+ void ring_buffer_resize(ring_buffer *buffer) {
32
+ unsigned int old_size = buffer->size;
33
+ buffer->size = old_size == 1 ? 4 : old_size * 2;
34
+ buffer->entries = realloc(buffer->entries, buffer->size * sizeof(VALUE));
35
+ for (unsigned int idx = 0; idx < buffer->head && idx < buffer->tail; idx++)
36
+ buffer->entries[old_size + idx] = buffer->entries[idx];
37
+ buffer->tail = buffer->head + buffer->count;
38
+ }
39
+
40
+ void ring_buffer_unshift(ring_buffer *buffer, VALUE value) {
41
+ if (buffer->count == buffer->size) ring_buffer_resize(buffer);
42
+
43
+ buffer->head = (buffer->head - 1) % buffer->size;
44
+ buffer->entries[buffer->head] = value;
45
+ buffer->count++;
46
+ }
47
+
48
+ void ring_buffer_push(ring_buffer *buffer, VALUE value) {
49
+ if (buffer->count == buffer->size) ring_buffer_resize(buffer);
50
+
51
+ buffer->entries[buffer->tail] = value;
52
+ buffer->tail = (buffer->tail + 1) % buffer->size;
53
+ buffer->count++;
54
+ }
55
+
56
+ void ring_buffer_mark(ring_buffer *buffer) {
57
+ for (unsigned int i = 0; i < buffer->count; i++)
58
+ rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size]);
59
+ }
60
+
61
+ void ring_buffer_shift_each(ring_buffer *buffer) {
62
+ for (unsigned int i = 0; i < buffer->count; i++)
63
+ rb_yield(buffer->entries[(buffer->head + i) % buffer->size]);
64
+
65
+ buffer->count = buffer->head = buffer->tail = 0;
66
+ }
67
+
68
+ VALUE ring_buffer_shift_all(ring_buffer *buffer) {
69
+ VALUE array = rb_ary_new_capa(buffer->count);
70
+ for (unsigned int i = 0; i < buffer->count; i++)
71
+ rb_ary_push(array, buffer->entries[(buffer->head + i) % buffer->size]);
72
+ buffer->count = buffer->head = buffer->tail = 0;
73
+ return array;
74
+ }
75
+
76
+ void ring_buffer_delete_at(ring_buffer *buffer, unsigned int idx) {
77
+ for (unsigned int idx2 = idx; idx2 != buffer->tail; idx2 = (idx2 + 1) % buffer->size) {
78
+ buffer->entries[idx2] = buffer->entries[(idx2 + 1) % buffer->size];
79
+ }
80
+ buffer->count--;
81
+ buffer->tail = (buffer->tail - 1) % buffer->size;
82
+ }
83
+
84
+ void ring_buffer_delete(ring_buffer *buffer, VALUE value) {
85
+ for (unsigned int i = 0; i < buffer->count; i++) {
86
+ unsigned int idx = (buffer->head + i) % buffer->size;
87
+ if (buffer->entries[idx] == value) {
88
+ ring_buffer_delete_at(buffer, idx);
89
+ return;
90
+ }
91
+ }
92
+ }
93
+
94
+ void ring_buffer_clear(ring_buffer *buffer) {
95
+ buffer->count = buffer->head = buffer->tail = 0;
96
+ }
@@ -0,0 +1,28 @@
1
+ #ifndef RING_BUFFER_H
2
+ #define RING_BUFFER_H
3
+
4
+ #include "ruby.h"
5
+
6
+ typedef struct ring_buffer {
7
+ VALUE *entries;
8
+ unsigned int size;
9
+ unsigned int count;
10
+ unsigned int head;
11
+ unsigned int tail;
12
+ } ring_buffer;
13
+
14
+ void ring_buffer_init(ring_buffer *buffer);
15
+ void ring_buffer_free(ring_buffer *buffer);
16
+ void ring_buffer_mark(ring_buffer *buffer);
17
+ int ring_buffer_empty_p(ring_buffer *buffer);
18
+ void ring_buffer_clear(ring_buffer *buffer);
19
+
20
+ VALUE ring_buffer_shift(ring_buffer *buffer);
21
+ void ring_buffer_unshift(ring_buffer *buffer, VALUE value);
22
+ void ring_buffer_push(ring_buffer *buffer, VALUE value);
23
+
24
+ void ring_buffer_shift_each(ring_buffer *buffer);
25
+ VALUE ring_buffer_shift_all(ring_buffer *buffer);
26
+ void ring_buffer_delete(ring_buffer *buffer, VALUE value);
27
+
28
+ #endif /* RING_BUFFER_H */
@@ -11,10 +11,9 @@ ID ID_runnable_next;
11
11
  ID ID_stop;
12
12
 
13
13
  static VALUE Thread_setup_fiber_scheduling(VALUE self) {
14
- VALUE queue;
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
- queue = rb_ary_new();
18
17
  rb_ivar_set(self, ID_run_queue, queue);
19
18
 
20
19
  return self;
@@ -22,12 +21,12 @@ static VALUE Thread_setup_fiber_scheduling(VALUE self) {
22
21
 
23
22
  int Thread_fiber_ref_count(VALUE self) {
24
23
  VALUE agent = rb_ivar_get(self, ID_ivar_agent);
25
- return NUM2INT(LibevAgent_ref_count(agent));
24
+ return NUM2INT(__AGENT__.ref_count(agent));
26
25
  }
27
26
 
28
27
  inline void Thread_fiber_reset_ref_count(VALUE self) {
29
28
  VALUE agent = rb_ivar_get(self, ID_ivar_agent);
30
- LibevAgent_reset_ref_count(agent);
29
+ __AGENT__.reset_ref_count(agent);
31
30
  }
32
31
 
33
32
  static VALUE SYM_scheduled_fibers;
@@ -42,7 +41,7 @@ static VALUE Thread_fiber_scheduling_stats(VALUE self) {
42
41
  long scheduled_count = RARRAY_LEN(queue);
43
42
  rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
44
43
 
45
- pending_count = LibevAgent_pending_count(agent);
44
+ pending_count = __AGENT__.pending_count(agent);
46
45
  rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(pending_count));
47
46
 
48
47
  return stats;
@@ -53,25 +52,34 @@ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
53
52
 
54
53
  if (rb_fiber_alive_p(fiber) != Qtrue) return self;
55
54
 
56
- FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
57
- // if fiber is already scheduled, just set the scheduled value, then return
58
- rb_ivar_set(fiber, ID_runnable_value, value);
59
- if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
60
- 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;
61
64
  }
62
65
 
63
- queue = rb_ivar_get(self, ID_run_queue);
64
- rb_ary_push(queue, fiber);
65
- rb_ivar_set(fiber, ID_runnable, Qtrue);
66
+ rb_ivar_set(fiber, ID_runnable_value, value);
67
+ FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
66
68
 
67
- if (rb_thread_current() != self) {
68
- // if the fiber scheduling is done across threads, we need to make sure the
69
- // target thread is woken up in case it is in the middle of running its
70
- // event selector. Otherwise it's gonna be stuck waiting for an event to
71
- // happen, not knowing that it there's already a fiber ready to run in its
72
- // run queue.
73
- VALUE agent = rb_ivar_get(self,ID_ivar_agent);
74
- 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
+ }
75
83
  }
76
84
  return self;
77
85
  }
@@ -88,13 +96,13 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
88
96
 
89
97
  // if fiber is already scheduled, remove it from the run queue
90
98
  if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
91
- rb_ary_delete(queue, fiber);
99
+ Queue_delete(queue, fiber);
92
100
  } else {
93
101
  rb_ivar_set(fiber, ID_runnable, Qtrue);
94
102
  }
95
103
 
96
104
  // the fiber is given priority by putting it at the front of the run queue
97
- rb_ary_unshift(queue, fiber);
105
+ Queue_unshift(queue, fiber);
98
106
 
99
107
  if (rb_thread_current() != self) {
100
108
  // if the fiber scheduling is done across threads, we need to make sure the
@@ -103,7 +111,7 @@ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value)
103
111
  // happen, not knowing that it there's already a fiber ready to run in its
104
112
  // run queue.
105
113
  VALUE agent = rb_ivar_get(self, ID_ivar_agent);
106
- LibevAgent_break(agent);
114
+ __AGENT__.wakeup(agent);
107
115
  }
108
116
  return self;
109
117
  }
@@ -115,6 +123,7 @@ VALUE Thread_switch_fiber(VALUE self) {
115
123
  VALUE value;
116
124
  VALUE agent = rb_ivar_get(self, ID_ivar_agent);
117
125
  int ref_count;
126
+ int agent_was_polled = 0;1;
118
127
 
119
128
  if (__tracing_enabled__) {
120
129
  if (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse) {
@@ -122,20 +131,21 @@ VALUE Thread_switch_fiber(VALUE self) {
122
131
  }
123
132
  }
124
133
 
125
- ref_count = LibevAgent_ref_count(agent);
134
+ ref_count = __AGENT__.ref_count(agent);
126
135
  while (1) {
127
- next_fiber = rb_ary_shift(queue);
136
+ next_fiber = Queue_shift_no_wait(queue);
128
137
  if (next_fiber != Qnil) {
129
- if (ref_count > 0) {
138
+ if (agent_was_polled == 0 && ref_count > 0) {
130
139
  // this mechanism prevents event starvation in case the run queue never
131
140
  // empties
132
- LibevAgent_poll(agent, Qtrue, current_fiber, queue);
141
+ __AGENT__.poll(agent, Qtrue, current_fiber, queue);
133
142
  }
134
143
  break;
135
144
  }
136
145
  if (ref_count == 0) break;
137
146
 
138
- LibevAgent_poll(agent, Qnil, current_fiber, queue);
147
+ __AGENT__.poll(agent, Qnil, current_fiber, queue);
148
+ agent_was_polled = 1;
139
149
  }
140
150
 
141
151
  if (next_fiber == Qnil) return Qnil;
@@ -151,9 +161,15 @@ VALUE Thread_switch_fiber(VALUE self) {
151
161
  value : rb_funcall(next_fiber, ID_transfer, 1, value);
152
162
  }
153
163
 
164
+ VALUE Thread_run_queue_trace(VALUE self) {
165
+ VALUE queue = rb_ivar_get(self, ID_run_queue);
166
+ Queue_trace(queue);
167
+ return self;
168
+ }
169
+
154
170
  VALUE Thread_reset_fiber_scheduling(VALUE self) {
155
171
  VALUE queue = rb_ivar_get(self, ID_run_queue);
156
- rb_ary_clear(queue);
172
+ Queue_clear(queue);
157
173
  Thread_fiber_reset_ref_count(self);
158
174
  return self;
159
175
  }
@@ -164,7 +180,7 @@ VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_ob
164
180
  Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
165
181
  }
166
182
 
167
- if (LibevAgent_break(agent) == Qnil) {
183
+ if (__AGENT__.wakeup(agent) == Qnil) {
168
184
  // we're not inside the ev_loop, so we just do a switchpoint
169
185
  Thread_switch_fiber(self);
170
186
  }
@@ -182,6 +198,7 @@ void Init_Thread() {
182
198
  rb_define_method(rb_cThread, "schedule_fiber_with_priority",
183
199
  Thread_schedule_fiber_with_priority, 2);
184
200
  rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
201
+ rb_define_method(rb_cThread, "run_queue_trace", Thread_run_queue_trace, 0);
185
202
 
186
203
  ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
187
204
  ID_ivar_agent = rb_intern("@agent");