polyphony 0.44.0 → 0.45.5

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