polyphony 0.44.0 → 0.45.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -1
- data/CHANGELOG.md +41 -0
- data/Gemfile.lock +14 -8
- data/Rakefile +1 -1
- data/TODO.md +12 -15
- data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
- data/docs/api-reference/thread.md +1 -1
- data/docs/getting-started/overview.md +14 -14
- data/docs/getting-started/tutorial.md +1 -1
- data/examples/adapters/redis_client.rb +3 -1
- data/examples/adapters/redis_pubsub_perf.rb +11 -8
- data/examples/adapters/sequel_mysql.rb +1 -1
- data/examples/adapters/sequel_pg.rb +24 -0
- data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
- data/examples/core/{xx-channels.rb → channels.rb} +0 -0
- data/examples/core/deferring-an-operation.rb +16 -0
- data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
- data/examples/core/{xx-forking.rb → forking.rb} +1 -1
- data/examples/core/handling-signals.rb +11 -0
- data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
- data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
- data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
- data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
- data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
- data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
- data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
- data/examples/core/supervisor.rb +20 -0
- data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
- data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
- data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
- data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
- data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
- data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
- data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
- data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
- data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
- data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
- data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
- data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
- data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
- data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
- data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
- data/examples/io/{xx-irb.rb → irb.rb} +0 -0
- data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
- data/examples/io/{xx-open.rb → open.rb} +0 -0
- data/examples/io/pry.rb +18 -0
- data/examples/io/rack_server.rb +71 -0
- data/examples/io/raw.rb +14 -0
- data/examples/io/reline.rb +18 -0
- data/examples/io/{xx-system.rb → system.rb} +1 -1
- data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
- data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
- data/examples/io/tunnel.rb +6 -1
- data/examples/io/{xx-zip.rb → zip.rb} +0 -0
- data/examples/performance/fiber_transfer.rb +2 -1
- data/examples/performance/fs_read.rb +5 -6
- data/examples/performance/multi_snooze.rb +0 -1
- data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
- data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
- data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
- data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
- data/examples/performance/thread_pool_perf.rb +6 -7
- data/ext/polyphony/backend.h +40 -0
- data/ext/polyphony/event.c +3 -3
- data/ext/polyphony/extconf.rb +1 -1
- data/ext/polyphony/fiber.c +90 -13
- data/ext/polyphony/{libev_agent.c → libev_backend.c} +226 -224
- data/ext/polyphony/polyphony.c +5 -7
- data/ext/polyphony/polyphony.h +18 -18
- data/ext/polyphony/polyphony_ext.c +5 -4
- data/ext/polyphony/queue.c +5 -6
- data/ext/polyphony/ring_buffer.c +0 -1
- data/ext/polyphony/runqueue.c +102 -0
- data/ext/polyphony/runqueue_ring_buffer.c +85 -0
- data/ext/polyphony/runqueue_ring_buffer.h +31 -0
- data/ext/polyphony/thread.c +53 -102
- data/lib/polyphony.rb +15 -14
- data/lib/polyphony/adapters/fs.rb +1 -1
- data/lib/polyphony/adapters/irb.rb +2 -17
- data/lib/polyphony/adapters/mysql2.rb +1 -1
- data/lib/polyphony/adapters/postgres.rb +5 -5
- data/lib/polyphony/adapters/process.rb +2 -5
- data/lib/polyphony/adapters/readline.rb +17 -0
- data/lib/polyphony/adapters/redis.rb +1 -1
- data/lib/polyphony/adapters/sequel.rb +1 -1
- data/lib/polyphony/core/global_api.rb +19 -14
- data/lib/polyphony/core/resource_pool.rb +2 -2
- data/lib/polyphony/core/sync.rb +43 -3
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/extensions/core.rb +25 -32
- data/lib/polyphony/extensions/fiber.rb +22 -45
- data/lib/polyphony/extensions/io.rb +60 -16
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +14 -15
- data/lib/polyphony/extensions/thread.rb +6 -5
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +5 -3
- data/test/helper.rb +1 -1
- data/test/{test_agent.rb → test_backend.rb} +22 -22
- data/test/test_fiber.rb +13 -12
- data/test/test_global_api.rb +29 -0
- data/test/test_io.rb +59 -1
- data/test/test_kernel.rb +5 -0
- data/test/test_signal.rb +14 -11
- data/test/test_socket.rb +17 -0
- data/test/test_sync.rb +73 -0
- metadata +99 -98
- data/.gitbook.yaml +0 -4
- data/examples/adapters/concurrent-ruby.rb +0 -9
- data/examples/core/04-handling-signals.rb +0 -19
- data/examples/core/xx-agent.rb +0 -102
- data/examples/core/xx-at_exit.rb +0 -29
- data/examples/core/xx-caller.rb +0 -12
- data/examples/core/xx-daemon.rb +0 -14
- data/examples/core/xx-deadlock.rb +0 -8
- data/examples/core/xx-deferring-an-operation.rb +0 -14
- data/examples/core/xx-exception-backtrace.rb +0 -40
- data/examples/core/xx-fork-cleanup.rb +0 -22
- data/examples/core/xx-fork-spin.rb +0 -42
- data/examples/core/xx-fork-terminate.rb +0 -27
- data/examples/core/xx-move_on.rb +0 -23
- data/examples/core/xx-queue-async.rb +0 -120
- data/examples/core/xx-readpartial.rb +0 -18
- data/examples/core/xx-signals.rb +0 -16
- data/examples/core/xx-sleep-forever.rb +0 -9
- data/examples/core/xx-sleeping.rb +0 -25
- data/examples/core/xx-snooze-starve.rb +0 -16
- data/examples/core/xx-spin-fork.rb +0 -49
- data/examples/core/xx-state-machine.rb +0 -51
- data/examples/core/xx-stop.rb +0 -20
- data/examples/core/xx-supervisors.rb +0 -21
- data/examples/core/xx-thread-selector-sleep.rb +0 -51
- data/examples/core/xx-thread-selector-snooze.rb +0 -46
- data/examples/core/xx-thread-snooze.rb +0 -34
- data/examples/core/xx-timer-gc.rb +0 -17
- data/examples/core/xx-trace.rb +0 -79
- data/examples/performance/xx-array.rb +0 -11
- data/examples/performance/xx-fiber-switch.rb +0 -9
- data/examples/performance/xx-snooze.rb +0 -15
- data/examples/xx-spin.rb +0 -32
- data/ext/polyphony/agent.h +0 -41
data/ext/polyphony/polyphony.c
CHANGED
@@ -9,11 +9,10 @@ ID ID_each;
|
|
9
9
|
ID ID_inspect;
|
10
10
|
ID ID_invoke;
|
11
11
|
ID ID_new;
|
12
|
-
ID
|
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
|
-
|
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");
|
data/ext/polyphony/polyphony.h
CHANGED
@@ -4,7 +4,8 @@
|
|
4
4
|
#include "ruby.h"
|
5
5
|
#include "ruby/io.h"
|
6
6
|
#include "libev.h"
|
7
|
-
#include "
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
}
|
data/ext/polyphony/queue.c
CHANGED
@@ -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
|
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 =
|
88
|
+
|
89
|
+
VALUE switchpoint_result = __BACKEND__.wait_event(backend, Qnil);
|
90
90
|
ring_buffer_delete(&queue->shift_queue, fiber);
|
91
91
|
|
92
|
-
|
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
|
}
|
data/ext/polyphony/ring_buffer.c
CHANGED
@@ -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 */
|
data/ext/polyphony/thread.c
CHANGED
@@ -1,183 +1,137 @@
|
|
1
1
|
#include "polyphony.h"
|
2
2
|
|
3
3
|
ID ID_deactivate_all_watchers_post_fork;
|
4
|
-
ID
|
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
|
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
|
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,
|
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
|
24
|
-
return NUM2INT(
|
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
|
29
|
-
|
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
|
34
|
+
VALUE backend = rb_ivar_get(self,ID_ivar_backend);
|
37
35
|
VALUE stats = rb_hash_new();
|
38
|
-
VALUE
|
36
|
+
VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
|
39
37
|
long pending_count;
|
40
38
|
|
41
|
-
long scheduled_count =
|
39
|
+
long scheduled_count = Runqueue_len(runqueue);
|
42
40
|
rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
|
43
41
|
|
44
|
-
pending_count =
|
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
|
-
|
51
|
-
VALUE
|
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
|
-
|
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
|
-
|
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
|
81
|
-
|
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
|
-
|
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
|
122
|
-
|
123
|
-
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
|
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 =
|
93
|
+
ref_count = __BACKEND__.ref_count(backend);
|
132
94
|
while (1) {
|
133
|
-
|
134
|
-
if (
|
135
|
-
if (
|
136
|
-
// this
|
137
|
-
|
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
|
-
|
145
|
-
|
105
|
+
__BACKEND__.poll(backend, Qnil, current_fiber, runqueue);
|
106
|
+
backend_was_polled = 1;
|
146
107
|
}
|
147
108
|
|
148
|
-
if (
|
109
|
+
if (next.fiber == Qnil) return Qnil;
|
149
110
|
|
150
111
|
// run next fiber
|
151
|
-
|
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
|
-
|
162
|
-
|
163
|
-
|
164
|
-
return
|
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,
|
169
|
-
|
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
|
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 (
|
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
|
-
|
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
|
-
|
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"));
|