polyphony 0.44.0 → 0.45.5

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 (145) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -1
  3. data/CHANGELOG.md +41 -0
  4. data/Gemfile.lock +14 -8
  5. data/Rakefile +1 -1
  6. data/TODO.md +12 -15
  7. data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
  8. data/docs/api-reference/thread.md +1 -1
  9. data/docs/getting-started/overview.md +14 -14
  10. data/docs/getting-started/tutorial.md +1 -1
  11. data/examples/adapters/redis_client.rb +3 -1
  12. data/examples/adapters/redis_pubsub_perf.rb +11 -8
  13. data/examples/adapters/sequel_mysql.rb +1 -1
  14. data/examples/adapters/sequel_pg.rb +24 -0
  15. data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
  16. data/examples/core/{xx-channels.rb → channels.rb} +0 -0
  17. data/examples/core/deferring-an-operation.rb +16 -0
  18. data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
  19. data/examples/core/{xx-forking.rb → forking.rb} +1 -1
  20. data/examples/core/handling-signals.rb +11 -0
  21. data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
  22. data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
  23. data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
  24. data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
  25. data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
  26. data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
  27. data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
  28. data/examples/core/supervisor.rb +20 -0
  29. data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
  30. data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
  31. data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
  32. data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
  33. data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
  34. data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
  35. data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
  36. data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
  37. data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
  38. data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
  39. data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
  40. data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
  41. data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
  42. data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
  43. data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
  44. data/examples/io/{xx-irb.rb → irb.rb} +0 -0
  45. data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
  46. data/examples/io/{xx-open.rb → open.rb} +0 -0
  47. data/examples/io/pry.rb +18 -0
  48. data/examples/io/rack_server.rb +71 -0
  49. data/examples/io/raw.rb +14 -0
  50. data/examples/io/reline.rb +18 -0
  51. data/examples/io/{xx-system.rb → system.rb} +1 -1
  52. data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
  53. data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
  54. data/examples/io/tunnel.rb +6 -1
  55. data/examples/io/{xx-zip.rb → zip.rb} +0 -0
  56. data/examples/performance/fiber_transfer.rb +2 -1
  57. data/examples/performance/fs_read.rb +5 -6
  58. data/examples/performance/multi_snooze.rb +0 -1
  59. data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
  60. data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
  61. data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
  62. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
  63. data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
  64. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
  65. data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
  66. data/examples/performance/thread_pool_perf.rb +6 -7
  67. data/ext/polyphony/backend.h +40 -0
  68. data/ext/polyphony/event.c +3 -3
  69. data/ext/polyphony/extconf.rb +1 -1
  70. data/ext/polyphony/fiber.c +90 -13
  71. data/ext/polyphony/{libev_agent.c → libev_backend.c} +226 -224
  72. data/ext/polyphony/polyphony.c +5 -7
  73. data/ext/polyphony/polyphony.h +18 -18
  74. data/ext/polyphony/polyphony_ext.c +5 -4
  75. data/ext/polyphony/queue.c +5 -6
  76. data/ext/polyphony/ring_buffer.c +0 -1
  77. data/ext/polyphony/runqueue.c +102 -0
  78. data/ext/polyphony/runqueue_ring_buffer.c +85 -0
  79. data/ext/polyphony/runqueue_ring_buffer.h +31 -0
  80. data/ext/polyphony/thread.c +53 -102
  81. data/lib/polyphony.rb +15 -14
  82. data/lib/polyphony/adapters/fs.rb +1 -1
  83. data/lib/polyphony/adapters/irb.rb +2 -17
  84. data/lib/polyphony/adapters/mysql2.rb +1 -1
  85. data/lib/polyphony/adapters/postgres.rb +5 -5
  86. data/lib/polyphony/adapters/process.rb +2 -5
  87. data/lib/polyphony/adapters/readline.rb +17 -0
  88. data/lib/polyphony/adapters/redis.rb +1 -1
  89. data/lib/polyphony/adapters/sequel.rb +1 -1
  90. data/lib/polyphony/core/global_api.rb +19 -14
  91. data/lib/polyphony/core/resource_pool.rb +2 -2
  92. data/lib/polyphony/core/sync.rb +43 -3
  93. data/lib/polyphony/core/throttler.rb +1 -1
  94. data/lib/polyphony/extensions/core.rb +25 -32
  95. data/lib/polyphony/extensions/fiber.rb +22 -45
  96. data/lib/polyphony/extensions/io.rb +60 -16
  97. data/lib/polyphony/extensions/openssl.rb +6 -6
  98. data/lib/polyphony/extensions/socket.rb +14 -15
  99. data/lib/polyphony/extensions/thread.rb +6 -5
  100. data/lib/polyphony/version.rb +1 -1
  101. data/polyphony.gemspec +5 -3
  102. data/test/helper.rb +1 -1
  103. data/test/{test_agent.rb → test_backend.rb} +22 -22
  104. data/test/test_fiber.rb +13 -12
  105. data/test/test_global_api.rb +29 -0
  106. data/test/test_io.rb +59 -1
  107. data/test/test_kernel.rb +5 -0
  108. data/test/test_signal.rb +14 -11
  109. data/test/test_socket.rb +17 -0
  110. data/test/test_sync.rb +73 -0
  111. metadata +99 -98
  112. data/.gitbook.yaml +0 -4
  113. data/examples/adapters/concurrent-ruby.rb +0 -9
  114. data/examples/core/04-handling-signals.rb +0 -19
  115. data/examples/core/xx-agent.rb +0 -102
  116. data/examples/core/xx-at_exit.rb +0 -29
  117. data/examples/core/xx-caller.rb +0 -12
  118. data/examples/core/xx-daemon.rb +0 -14
  119. data/examples/core/xx-deadlock.rb +0 -8
  120. data/examples/core/xx-deferring-an-operation.rb +0 -14
  121. data/examples/core/xx-exception-backtrace.rb +0 -40
  122. data/examples/core/xx-fork-cleanup.rb +0 -22
  123. data/examples/core/xx-fork-spin.rb +0 -42
  124. data/examples/core/xx-fork-terminate.rb +0 -27
  125. data/examples/core/xx-move_on.rb +0 -23
  126. data/examples/core/xx-queue-async.rb +0 -120
  127. data/examples/core/xx-readpartial.rb +0 -18
  128. data/examples/core/xx-signals.rb +0 -16
  129. data/examples/core/xx-sleep-forever.rb +0 -9
  130. data/examples/core/xx-sleeping.rb +0 -25
  131. data/examples/core/xx-snooze-starve.rb +0 -16
  132. data/examples/core/xx-spin-fork.rb +0 -49
  133. data/examples/core/xx-state-machine.rb +0 -51
  134. data/examples/core/xx-stop.rb +0 -20
  135. data/examples/core/xx-supervisors.rb +0 -21
  136. data/examples/core/xx-thread-selector-sleep.rb +0 -51
  137. data/examples/core/xx-thread-selector-snooze.rb +0 -46
  138. data/examples/core/xx-thread-snooze.rb +0 -34
  139. data/examples/core/xx-timer-gc.rb +0 -17
  140. data/examples/core/xx-trace.rb +0 -79
  141. data/examples/performance/xx-array.rb +0 -11
  142. data/examples/performance/xx-fiber-switch.rb +0 -9
  143. data/examples/performance/xx-snooze.rb +0 -15
  144. data/examples/xx-spin.rb +0 -32
  145. data/ext/polyphony/agent.h +0 -41
@@ -9,11 +9,10 @@ ID ID_each;
9
9
  ID ID_inspect;
10
10
  ID ID_invoke;
11
11
  ID ID_new;
12
- ID ID_raise;
12
+ ID ID_ivar_result;
13
+ ID ID_ivar_runnable;
13
14
  ID ID_ivar_running;
14
15
  ID ID_ivar_thread;
15
- ID ID_runnable;
16
- ID ID_runnable_value;
17
16
  ID ID_size;
18
17
  ID ID_signal;
19
18
  ID ID_switch_fiber;
@@ -22,7 +21,7 @@ ID ID_R;
22
21
  ID ID_W;
23
22
  ID ID_RW;
24
23
 
25
- agent_interface_t agent_interface;
24
+ backend_interface_t backend_interface;
26
25
 
27
26
  VALUE Polyphony_snooze(VALUE self) {
28
27
  VALUE ret;
@@ -62,12 +61,11 @@ void Init_Polyphony() {
62
61
  ID_each = rb_intern("each");
63
62
  ID_inspect = rb_intern("inspect");
64
63
  ID_invoke = rb_intern("invoke");
64
+ ID_ivar_result = rb_intern("@result");
65
+ ID_ivar_runnable = rb_intern("runnable");
65
66
  ID_ivar_running = rb_intern("@running");
66
67
  ID_ivar_thread = rb_intern("@thread");
67
68
  ID_new = rb_intern("new");
68
- ID_raise = rb_intern("raise");
69
- ID_runnable = rb_intern("runnable");
70
- ID_runnable_value = rb_intern("runnable_value");
71
69
  ID_signal = rb_intern("signal");
72
70
  ID_size = rb_intern("size");
73
71
  ID_switch_fiber = rb_intern("switch_fiber");
@@ -4,7 +4,8 @@
4
4
  #include "ruby.h"
5
5
  #include "ruby/io.h"
6
6
  #include "libev.h"
7
- #include "agent.h"
7
+ #include "backend.h"
8
+ #include "runqueue_ring_buffer.h"
8
9
 
9
10
  // debugging
10
11
  #define OBJ_ID(obj) (NUM2LONG(rb_funcall(obj, rb_intern("object_id"), 0)))
@@ -24,13 +25,13 @@
24
25
  return RAISE_EXCEPTION(ret); \
25
26
  }
26
27
 
27
-
28
- extern agent_interface_t agent_interface;
29
- #define __AGENT__ (agent_interface)
28
+ extern backend_interface_t backend_interface;
29
+ #define __BACKEND__ (backend_interface)
30
30
 
31
31
  extern VALUE mPolyphony;
32
32
  extern VALUE cQueue;
33
33
  extern VALUE cEvent;
34
+ extern VALUE cRunqueue;
34
35
 
35
36
  extern ID ID_call;
36
37
  extern ID ID_caller;
@@ -39,13 +40,13 @@ extern ID ID_each;
39
40
  extern ID ID_fiber_trace;
40
41
  extern ID ID_inspect;
41
42
  extern ID ID_invoke;
42
- extern ID ID_ivar_agent;
43
+ extern ID ID_ivar_backend;
44
+ extern ID ID_ivar_result;
45
+ extern ID ID_ivar_runnable;
43
46
  extern ID ID_ivar_running;
44
47
  extern ID ID_ivar_thread;
45
48
  extern ID ID_new;
46
49
  extern ID ID_raise;
47
- extern ID ID_runnable;
48
- extern ID ID_runnable_value;
49
50
  extern ID ID_signal;
50
51
  extern ID ID_size;
51
52
  extern ID ID_switch_fiber;
@@ -67,31 +68,30 @@ enum {
67
68
  FIBER_STATE_SCHEDULED = 2
68
69
  };
69
70
 
70
- // watcher flags
71
- enum {
72
- // a watcher's active field will be set to this after fork
73
- GYRO_WATCHER_POST_FORK = 0xFF
74
- };
75
-
76
71
  VALUE Fiber_auto_watcher(VALUE self);
77
72
  void Fiber_make_runnable(VALUE fiber, VALUE value);
78
73
 
79
74
  VALUE Queue_push(VALUE self, VALUE value);
80
75
  VALUE Queue_unshift(VALUE self, VALUE value);
81
76
  VALUE Queue_shift(VALUE self);
77
+ VALUE Queue_shift_all(VALUE self);
82
78
  VALUE Queue_shift_no_wait(VALUE self);
83
79
  VALUE Queue_clear(VALUE self);
84
80
  VALUE Queue_delete(VALUE self, VALUE value);
85
81
  long Queue_len(VALUE self);
86
82
  void Queue_trace(VALUE self);
87
83
 
88
- VALUE Polyphony_snooze(VALUE self);
84
+
85
+ void Runqueue_push(VALUE self, VALUE fiber, VALUE value, int reschedule);
86
+ void Runqueue_unshift(VALUE self, VALUE fiber, VALUE value, int reschedule);
87
+ runqueue_entry Runqueue_shift(VALUE self);
88
+ void Runqueue_delete(VALUE self, VALUE fiber);
89
+ void Runqueue_clear(VALUE self);
90
+ long Runqueue_len(VALUE self);
91
+ int Runqueue_empty_p(VALUE self);
89
92
 
90
93
  VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
94
+ VALUE Thread_schedule_fiber_with_priority(VALUE thread, VALUE fiber, VALUE value);
91
95
  VALUE Thread_switch_fiber(VALUE thread);
92
96
 
93
- int io_setstrbuf(VALUE *str, long len);
94
- void io_set_read_length(VALUE str, long n, int shrinkable);
95
- VALUE io_enc_str(VALUE str, rb_io_t *fptr);
96
-
97
97
  #endif /* POLYPHONY_H */
@@ -2,9 +2,10 @@
2
2
 
3
3
  void Init_Fiber();
4
4
  void Init_Polyphony();
5
- void Init_LibevAgent();
5
+ void Init_LibevBackend();
6
6
  void Init_Queue();
7
7
  void Init_Event();
8
+ void Init_Runqueue();
8
9
  void Init_Thread();
9
10
  void Init_Tracing();
10
11
 
@@ -12,12 +13,12 @@ void Init_polyphony_ext() {
12
13
  ev_set_allocator(xrealloc);
13
14
 
14
15
  Init_Polyphony();
15
- Init_LibevAgent();
16
+
17
+ Init_LibevBackend();
16
18
  Init_Queue();
17
19
  Init_Event();
18
-
20
+ Init_Runqueue();
19
21
  Init_Fiber();
20
22
  Init_Thread();
21
-
22
23
  Init_Tracing();
23
24
  }
@@ -80,17 +80,16 @@ VALUE Queue_shift(VALUE self) {
80
80
 
81
81
  VALUE fiber = rb_fiber_current();
82
82
  VALUE thread = rb_thread_current();
83
- VALUE agent = rb_ivar_get(thread, ID_ivar_agent);
83
+ VALUE backend = rb_ivar_get(thread, ID_ivar_backend);
84
84
 
85
85
  while (1) {
86
86
  ring_buffer_push(&queue->shift_queue, fiber);
87
87
  if (queue->values.count > 0) Fiber_make_runnable(fiber, Qnil);
88
-
89
- VALUE switchpoint_result = __AGENT__.wait_event(agent, Qnil);
88
+
89
+ VALUE switchpoint_result = __BACKEND__.wait_event(backend, Qnil);
90
90
  ring_buffer_delete(&queue->shift_queue, fiber);
91
91
 
92
- if (RTEST(rb_obj_is_kind_of(switchpoint_result, rb_eException)))
93
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
92
+ TEST_RESUME_EXCEPTION(switchpoint_result);
94
93
  RB_GC_GUARD(switchpoint_result);
95
94
 
96
95
  if (queue->values.count > 0)
@@ -152,7 +151,7 @@ VALUE Queue_flush_waiters(VALUE self, VALUE value) {
152
151
  while(1) {
153
152
  VALUE fiber = ring_buffer_shift(&queue->shift_queue);
154
153
  if (fiber == Qnil) return self;
155
-
154
+
156
155
  Fiber_make_runnable(fiber, value);
157
156
  }
158
157
  }
@@ -24,7 +24,6 @@ VALUE ring_buffer_shift(ring_buffer *buffer) {
24
24
  value = buffer->entries[buffer->head];
25
25
  buffer->head = (buffer->head + 1) % buffer->size;
26
26
  buffer->count--;
27
- // INSPECT(value);
28
27
  return value;
29
28
  }
30
29
 
@@ -0,0 +1,102 @@
1
+ #include "polyphony.h"
2
+ #include "runqueue_ring_buffer.h"
3
+
4
+ typedef struct queue {
5
+ runqueue_ring_buffer entries;
6
+ } Runqueue_t;
7
+
8
+ VALUE cRunqueue = Qnil;
9
+
10
+ static void Runqueue_mark(void *ptr) {
11
+ Runqueue_t *runqueue = ptr;
12
+ runqueue_ring_buffer_mark(&runqueue->entries);
13
+ }
14
+
15
+ static void Runqueue_free(void *ptr) {
16
+ Runqueue_t *runqueue = ptr;
17
+ runqueue_ring_buffer_free(&runqueue->entries);
18
+ xfree(ptr);
19
+ }
20
+
21
+ static size_t Runqueue_size(const void *ptr) {
22
+ return sizeof(Runqueue_t);
23
+ }
24
+
25
+ static const rb_data_type_t Runqueue_type = {
26
+ "Runqueue",
27
+ {Runqueue_mark, Runqueue_free, Runqueue_size,},
28
+ 0, 0, 0
29
+ };
30
+
31
+ static VALUE Runqueue_allocate(VALUE klass) {
32
+ Runqueue_t *runqueue;
33
+
34
+ runqueue = ALLOC(Runqueue_t);
35
+ return TypedData_Wrap_Struct(klass, &Runqueue_type, runqueue);
36
+ }
37
+
38
+ #define GetRunqueue(obj, runqueue) \
39
+ TypedData_Get_Struct((obj), Runqueue_t, &Runqueue_type, (runqueue))
40
+
41
+ static VALUE Runqueue_initialize(VALUE self) {
42
+ Runqueue_t *runqueue;
43
+ GetRunqueue(self, runqueue);
44
+
45
+ runqueue_ring_buffer_init(&runqueue->entries);
46
+
47
+ return self;
48
+ }
49
+
50
+ void Runqueue_push(VALUE self, VALUE fiber, VALUE value, int reschedule) {
51
+ Runqueue_t *runqueue;
52
+ GetRunqueue(self, runqueue);
53
+
54
+ if (reschedule) runqueue_ring_buffer_delete(&runqueue->entries, fiber);
55
+ runqueue_ring_buffer_push(&runqueue->entries, fiber, value);
56
+ }
57
+
58
+ void Runqueue_unshift(VALUE self, VALUE fiber, VALUE value, int reschedule) {
59
+ Runqueue_t *runqueue;
60
+ GetRunqueue(self, runqueue);
61
+ if (reschedule) runqueue_ring_buffer_delete(&runqueue->entries, fiber);
62
+ runqueue_ring_buffer_unshift(&runqueue->entries, fiber, value);
63
+ }
64
+
65
+ runqueue_entry Runqueue_shift(VALUE self) {
66
+ Runqueue_t *runqueue;
67
+ GetRunqueue(self, runqueue);
68
+ return runqueue_ring_buffer_shift(&runqueue->entries);
69
+ }
70
+
71
+ void Runqueue_delete(VALUE self, VALUE fiber) {
72
+ Runqueue_t *runqueue;
73
+ GetRunqueue(self, runqueue);
74
+ runqueue_ring_buffer_delete(&runqueue->entries, fiber);
75
+ }
76
+
77
+ void Runqueue_clear(VALUE self) {
78
+ Runqueue_t *runqueue;
79
+ GetRunqueue(self, runqueue);
80
+ runqueue_ring_buffer_clear(&runqueue->entries);
81
+ }
82
+
83
+ long Runqueue_len(VALUE self) {
84
+ Runqueue_t *runqueue;
85
+ GetRunqueue(self, runqueue);
86
+
87
+ return runqueue->entries.count;
88
+ }
89
+
90
+ int Runqueue_empty_p(VALUE self) {
91
+ Runqueue_t *runqueue;
92
+ GetRunqueue(self, runqueue);
93
+
94
+ return (runqueue->entries.count == 0);
95
+ }
96
+
97
+ void Init_Runqueue() {
98
+ cRunqueue = rb_define_class_under(mPolyphony, "Runqueue", rb_cData);
99
+ rb_define_alloc_func(cRunqueue, Runqueue_allocate);
100
+
101
+ rb_define_method(cRunqueue, "initialize", Runqueue_initialize, 0);
102
+ }
@@ -0,0 +1,85 @@
1
+ #include "polyphony.h"
2
+ #include "runqueue_ring_buffer.h"
3
+
4
+ void runqueue_ring_buffer_init(runqueue_ring_buffer *buffer) {
5
+ buffer->size = 1;
6
+ buffer->count = 0;
7
+ buffer->entries = malloc(buffer->size * sizeof(runqueue_entry));
8
+ buffer->head = 0;
9
+ buffer->tail = 0;
10
+ }
11
+
12
+ void runqueue_ring_buffer_free(runqueue_ring_buffer *buffer) {
13
+ free(buffer->entries);
14
+ }
15
+
16
+ int runqueue_ring_buffer_empty_p(runqueue_ring_buffer *buffer) {
17
+ return buffer->count == 0;
18
+ }
19
+
20
+ static runqueue_entry nil_runqueue_entry = {(Qnil), (Qnil)};
21
+
22
+ runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer) {
23
+ if (buffer->count == 0) return nil_runqueue_entry;
24
+
25
+ runqueue_entry value = buffer->entries[buffer->head];
26
+ buffer->head = (buffer->head + 1) % buffer->size;
27
+ buffer->count--;
28
+ return value;
29
+ }
30
+
31
+ void runqueue_ring_buffer_resize(runqueue_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(runqueue_entry));
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 runqueue_ring_buffer_unshift(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value) {
41
+ if (buffer->count == buffer->size) runqueue_ring_buffer_resize(buffer);
42
+
43
+ buffer->head = (buffer->head - 1) % buffer->size;
44
+ buffer->entries[buffer->head].fiber = fiber;
45
+ buffer->entries[buffer->head].value = value;
46
+ buffer->count++;
47
+ }
48
+
49
+ void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value) {
50
+ if (buffer->count == buffer->size) runqueue_ring_buffer_resize(buffer);
51
+
52
+ buffer->entries[buffer->tail].fiber = fiber;
53
+ buffer->entries[buffer->tail].value = value;
54
+ buffer->tail = (buffer->tail + 1) % buffer->size;
55
+ buffer->count++;
56
+ }
57
+
58
+ void runqueue_ring_buffer_mark(runqueue_ring_buffer *buffer) {
59
+ for (unsigned int i = 0; i < buffer->count; i++) {
60
+ rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].fiber);
61
+ rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].value);
62
+ }
63
+ }
64
+
65
+ void runqueue_ring_buffer_delete_at(runqueue_ring_buffer *buffer, unsigned int idx) {
66
+ for (unsigned int idx2 = idx; idx2 != buffer->tail; idx2 = (idx2 + 1) % buffer->size) {
67
+ buffer->entries[idx2] = buffer->entries[(idx2 + 1) % buffer->size];
68
+ }
69
+ buffer->count--;
70
+ buffer->tail = (buffer->tail - 1) % buffer->size;
71
+ }
72
+
73
+ void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber) {
74
+ for (unsigned int i = 0; i < buffer->count; i++) {
75
+ unsigned int idx = (buffer->head + i) % buffer->size;
76
+ if (buffer->entries[idx].fiber == fiber) {
77
+ runqueue_ring_buffer_delete_at(buffer, idx);
78
+ return;
79
+ }
80
+ }
81
+ }
82
+
83
+ void runqueue_ring_buffer_clear(runqueue_ring_buffer *buffer) {
84
+ buffer->count = buffer->head = buffer->tail = 0;
85
+ }
@@ -0,0 +1,31 @@
1
+ #ifndef RUNQUEUE_RING_BUFFER_H
2
+ #define RUNQUEUE_RING_BUFFER_H
3
+
4
+ #include "ruby.h"
5
+
6
+ typedef struct runqueue_entry {
7
+ VALUE fiber;
8
+ VALUE value;
9
+ } runqueue_entry;
10
+
11
+ typedef struct runqueue_ring_buffer {
12
+ runqueue_entry *entries;
13
+ unsigned int size;
14
+ unsigned int count;
15
+ unsigned int head;
16
+ unsigned int tail;
17
+ } runqueue_ring_buffer;
18
+
19
+ void runqueue_ring_buffer_init(runqueue_ring_buffer *buffer);
20
+ void runqueue_ring_buffer_free(runqueue_ring_buffer *buffer);
21
+ void runqueue_ring_buffer_mark(runqueue_ring_buffer *buffer);
22
+ int runqueue_ring_buffer_empty_p(runqueue_ring_buffer *buffer);
23
+ void runqueue_ring_buffer_clear(runqueue_ring_buffer *buffer);
24
+
25
+ runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer);
26
+ void runqueue_ring_buffer_unshift(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value);
27
+ void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value);
28
+
29
+ void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber);
30
+
31
+ #endif /* RUNQUEUE_RING_BUFFER_H */
@@ -1,183 +1,137 @@
1
1
  #include "polyphony.h"
2
2
 
3
3
  ID ID_deactivate_all_watchers_post_fork;
4
- ID ID_ivar_agent;
4
+ ID ID_ivar_backend;
5
5
  ID ID_ivar_join_wait_queue;
6
6
  ID ID_ivar_main_fiber;
7
- ID ID_ivar_result;
8
7
  ID ID_ivar_terminated;
9
- ID ID_run_queue;
10
- ID ID_runnable_next;
8
+ ID ID_ivar_runqueue;
11
9
  ID ID_stop;
12
10
 
13
11
  static VALUE Thread_setup_fiber_scheduling(VALUE self) {
14
- VALUE queue = rb_funcall(cQueue, ID_new, 0);
15
-
12
+ VALUE runqueue = rb_funcall(cRunqueue, ID_new, 0);
13
+
16
14
  rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
17
- rb_ivar_set(self, ID_run_queue, queue);
15
+ rb_ivar_set(self, ID_ivar_runqueue, runqueue);
18
16
 
19
17
  return self;
20
18
  }
21
19
 
22
20
  int Thread_fiber_ref_count(VALUE self) {
23
- VALUE agent = rb_ivar_get(self, ID_ivar_agent);
24
- return NUM2INT(__AGENT__.ref_count(agent));
21
+ VALUE backend = rb_ivar_get(self, ID_ivar_backend);
22
+ return NUM2INT(__BACKEND__.ref_count(backend));
25
23
  }
26
24
 
27
25
  inline void Thread_fiber_reset_ref_count(VALUE self) {
28
- VALUE agent = rb_ivar_get(self, ID_ivar_agent);
29
- __AGENT__.reset_ref_count(agent);
26
+ VALUE backend = rb_ivar_get(self, ID_ivar_backend);
27
+ __BACKEND__.reset_ref_count(backend);
30
28
  }
31
29
 
32
30
  static VALUE SYM_scheduled_fibers;
33
31
  static VALUE SYM_pending_watchers;
34
32
 
35
33
  static VALUE Thread_fiber_scheduling_stats(VALUE self) {
36
- VALUE agent = rb_ivar_get(self,ID_ivar_agent);
34
+ VALUE backend = rb_ivar_get(self,ID_ivar_backend);
37
35
  VALUE stats = rb_hash_new();
38
- VALUE queue = rb_ivar_get(self, ID_run_queue);
36
+ VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
39
37
  long pending_count;
40
38
 
41
- long scheduled_count = RARRAY_LEN(queue);
39
+ long scheduled_count = Runqueue_len(runqueue);
42
40
  rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
43
41
 
44
- pending_count = __AGENT__.pending_count(agent);
42
+ pending_count = __BACKEND__.pending_count(backend);
45
43
  rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(pending_count));
46
44
 
47
45
  return stats;
48
46
  }
49
47
 
50
- VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
51
- VALUE queue;
52
-
53
- if (rb_fiber_alive_p(fiber) != Qtrue) return self;
48
+ void schedule_fiber(VALUE self, VALUE fiber, VALUE value, int prioritize) {
49
+ VALUE runqueue;
50
+ int already_runnable;
54
51
 
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;
64
- }
52
+ if (rb_fiber_alive_p(fiber) != Qtrue) return;
53
+ already_runnable = rb_ivar_get(fiber, ID_ivar_runnable) != Qnil;
65
54
 
66
- rb_ivar_set(fiber, ID_runnable_value, value);
67
55
  COND_TRACE(3, SYM_fiber_schedule, fiber, value);
68
-
56
+ runqueue = rb_ivar_get(self, ID_ivar_runqueue);
57
+ (prioritize ? Runqueue_unshift : Runqueue_push)(runqueue, fiber, value, already_runnable);
69
58
  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
-
59
+ rb_ivar_set(fiber, ID_ivar_runnable, Qtrue);
74
60
  if (rb_thread_current() != self) {
75
61
  // If the fiber scheduling is done across threads, we need to make sure the
76
62
  // target thread is woken up in case it is in the middle of running its
77
63
  // event selector. Otherwise it's gonna be stuck waiting for an event to
78
64
  // happen, not knowing that it there's already a fiber ready to run in its
79
65
  // run queue.
80
- VALUE agent = rb_ivar_get(self,ID_ivar_agent);
81
- __AGENT__.wakeup(agent);
66
+ VALUE backend = rb_ivar_get(self,ID_ivar_backend);
67
+ __BACKEND__.wakeup(backend);
82
68
  }
83
69
  }
70
+ }
71
+
72
+ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
73
+ schedule_fiber(self, fiber, value, 0);
84
74
  return self;
85
75
  }
86
76
 
87
77
  VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value) {
88
- VALUE queue;
89
-
90
- if (rb_fiber_alive_p(fiber) != Qtrue) return self;
91
-
92
- COND_TRACE(3, SYM_fiber_schedule, fiber, value);
93
- rb_ivar_set(fiber, ID_runnable_value, value);
94
-
95
- queue = rb_ivar_get(self, ID_run_queue);
96
-
97
- // if fiber is already scheduled, remove it from the run queue
98
- if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
99
- Queue_delete(queue, fiber);
100
- } else {
101
- rb_ivar_set(fiber, ID_runnable, Qtrue);
102
- }
103
-
104
- // the fiber is given priority by putting it at the front of the run queue
105
- Queue_unshift(queue, fiber);
106
-
107
- if (rb_thread_current() != self) {
108
- // if the fiber scheduling is done across threads, we need to make sure the
109
- // target thread is woken up in case it is in the middle of running its
110
- // event loop. Otherwise it's gonna be stuck waiting for an event to
111
- // happen, not knowing that it there's already a fiber ready to run in its
112
- // run queue.
113
- VALUE agent = rb_ivar_get(self, ID_ivar_agent);
114
- __AGENT__.wakeup(agent);
115
- }
78
+ schedule_fiber(self, fiber, value, 1);
116
79
  return self;
117
80
  }
118
81
 
119
82
  VALUE Thread_switch_fiber(VALUE self) {
120
83
  VALUE current_fiber = rb_fiber_current();
121
- VALUE queue = rb_ivar_get(self, ID_run_queue);
122
- VALUE next_fiber;
123
- VALUE value;
124
- VALUE agent = rb_ivar_get(self, ID_ivar_agent);
84
+ VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
85
+ runqueue_entry next;
86
+ VALUE backend = rb_ivar_get(self, ID_ivar_backend);
125
87
  int ref_count;
126
- int agent_was_polled = 0;
88
+ int backend_was_polled = 0;
127
89
 
128
90
  if (__tracing_enabled__ && (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse))
129
91
  TRACE(2, SYM_fiber_switchpoint, current_fiber);
130
92
 
131
- ref_count = __AGENT__.ref_count(agent);
93
+ ref_count = __BACKEND__.ref_count(backend);
132
94
  while (1) {
133
- next_fiber = Queue_shift_no_wait(queue);
134
- if (next_fiber != Qnil) {
135
- if (agent_was_polled == 0 && ref_count > 0) {
136
- // this mechanism prevents event starvation in case the run queue never
137
- // empties
138
- __AGENT__.poll(agent, Qtrue, current_fiber, queue);
95
+ next = Runqueue_shift(runqueue);
96
+ if (next.fiber != Qnil) {
97
+ if (backend_was_polled == 0 && ref_count > 0) {
98
+ // this prevents event starvation in case the run queue never empties
99
+ __BACKEND__.poll(backend, Qtrue, current_fiber, runqueue);
139
100
  }
140
101
  break;
141
102
  }
142
103
  if (ref_count == 0) break;
143
104
 
144
- __AGENT__.poll(agent, Qnil, current_fiber, queue);
145
- agent_was_polled = 1;
105
+ __BACKEND__.poll(backend, Qnil, current_fiber, runqueue);
106
+ backend_was_polled = 1;
146
107
  }
147
108
 
148
- if (next_fiber == Qnil) return Qnil;
109
+ if (next.fiber == Qnil) return Qnil;
149
110
 
150
111
  // run next fiber
151
- value = rb_ivar_get(next_fiber, ID_runnable_value);
152
- COND_TRACE(3, SYM_fiber_run, next_fiber, value);
153
-
154
- rb_ivar_set(next_fiber, ID_runnable, Qnil);
155
- RB_GC_GUARD(next_fiber);
156
- RB_GC_GUARD(value);
157
- return (next_fiber == current_fiber) ?
158
- value : rb_funcall(next_fiber, ID_transfer, 1, value);
159
- }
112
+ COND_TRACE(3, SYM_fiber_run, next.fiber, next.value);
160
113
 
161
- VALUE Thread_run_queue_trace(VALUE self) {
162
- VALUE queue = rb_ivar_get(self, ID_run_queue);
163
- Queue_trace(queue);
164
- return self;
114
+ rb_ivar_set(next.fiber, ID_ivar_runnable, Qnil);
115
+ RB_GC_GUARD(next.fiber);
116
+ RB_GC_GUARD(next.value);
117
+ return (next.fiber == current_fiber) ?
118
+ next.value : rb_funcall(next.fiber, ID_transfer, 1, next.value);
165
119
  }
166
120
 
167
121
  VALUE Thread_reset_fiber_scheduling(VALUE self) {
168
- VALUE queue = rb_ivar_get(self, ID_run_queue);
169
- Queue_clear(queue);
122
+ VALUE queue = rb_ivar_get(self, ID_ivar_runqueue);
123
+ Runqueue_clear(queue);
170
124
  Thread_fiber_reset_ref_count(self);
171
125
  return self;
172
126
  }
173
127
 
174
128
  VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_obj) {
175
- VALUE agent = rb_ivar_get(self, ID_ivar_agent);
129
+ VALUE backend = rb_ivar_get(self, ID_ivar_backend);
176
130
  if (fiber != Qnil) {
177
131
  Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
178
132
  }
179
133
 
180
- if (__AGENT__.wakeup(agent) == Qnil) {
134
+ if (__BACKEND__.wakeup(backend) == Qnil) {
181
135
  // we're not inside the ev_loop, so we just do a switchpoint
182
136
  Thread_switch_fiber(self);
183
137
  }
@@ -200,18 +154,15 @@ void Init_Thread() {
200
154
  rb_define_method(rb_cThread, "schedule_fiber_with_priority",
201
155
  Thread_schedule_fiber_with_priority, 2);
202
156
  rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
203
- rb_define_method(rb_cThread, "run_queue_trace", Thread_run_queue_trace, 0);
204
157
 
205
158
  rb_define_method(rb_cThread, "debug!", Thread_debug, 0);
206
159
 
207
160
  ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
208
- ID_ivar_agent = rb_intern("@agent");
161
+ ID_ivar_backend = rb_intern("@backend");
209
162
  ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
210
163
  ID_ivar_main_fiber = rb_intern("@main_fiber");
211
- ID_ivar_result = rb_intern("@result");
164
+ ID_ivar_runqueue = rb_intern("@runqueue");
212
165
  ID_ivar_terminated = rb_intern("@terminated");
213
- ID_run_queue = rb_intern("run_queue");
214
- ID_runnable_next = rb_intern("runnable_next");
215
166
  ID_stop = rb_intern("stop");
216
167
 
217
168
  SYM_scheduled_fibers = ID2SYM(rb_intern("scheduled_fibers"));