polyphony 0.45.0 → 0.46.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +2 -0
- data/.gitmodules +0 -0
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +38 -0
- data/Gemfile.lock +11 -3
- data/README.md +3 -3
- data/Rakefile +1 -1
- data/TODO.md +10 -18
- 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/{xx-pry.rb → pry.rb} +0 -0
- data/examples/io/{xx-rack_server.rb → rack_server.rb} +0 -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 -2
- data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
- data/examples/performance/thread_pool_perf.rb +6 -7
- data/ext/liburing/liburing.h +585 -0
- data/ext/liburing/liburing/README.md +4 -0
- data/ext/liburing/liburing/barrier.h +73 -0
- data/ext/liburing/liburing/compat.h +15 -0
- data/ext/liburing/liburing/io_uring.h +343 -0
- data/ext/liburing/queue.c +333 -0
- data/ext/liburing/register.c +187 -0
- data/ext/liburing/setup.c +210 -0
- data/ext/liburing/syscall.c +54 -0
- data/ext/liburing/syscall.h +18 -0
- data/ext/polyphony/backend.h +1 -16
- data/ext/polyphony/backend_common.h +109 -0
- data/ext/polyphony/backend_io_uring.c +884 -0
- data/ext/polyphony/backend_io_uring_context.c +73 -0
- data/ext/polyphony/backend_io_uring_context.h +52 -0
- data/ext/polyphony/{libev_backend.c → backend_libev.c} +255 -345
- data/ext/polyphony/event.c +1 -1
- data/ext/polyphony/extconf.rb +31 -13
- data/ext/polyphony/fiber.c +111 -27
- data/ext/polyphony/libev.c +4 -0
- data/ext/polyphony/libev.h +8 -2
- data/ext/polyphony/liburing.c +8 -0
- data/ext/polyphony/playground.c +51 -0
- data/ext/polyphony/polyphony.c +6 -8
- data/ext/polyphony/polyphony.h +29 -25
- data/ext/polyphony/polyphony_ext.c +13 -6
- data/ext/polyphony/queue.c +3 -4
- 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 +45 -92
- data/lib/polyphony.rb +2 -2
- data/lib/polyphony/adapters/fs.rb +1 -1
- data/lib/polyphony/adapters/process.rb +0 -3
- data/lib/polyphony/adapters/redis.rb +1 -1
- data/lib/polyphony/adapters/trace.rb +2 -2
- data/lib/polyphony/core/global_api.rb +9 -12
- data/lib/polyphony/core/sync.rb +6 -2
- data/lib/polyphony/extensions/core.rb +6 -24
- data/lib/polyphony/extensions/debug.rb +13 -0
- data/lib/polyphony/extensions/fiber.rb +21 -44
- data/lib/polyphony/extensions/io.rb +55 -10
- data/lib/polyphony/extensions/socket.rb +70 -12
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +3 -2
- data/test/helper.rb +36 -4
- data/test/io_uring_test.rb +55 -0
- data/test/stress.rb +5 -2
- data/test/test_backend.rb +4 -6
- data/test/test_ext.rb +1 -2
- data/test/test_fiber.rb +31 -24
- data/test/test_global_api.rb +58 -31
- data/test/test_io.rb +58 -0
- data/test/test_signal.rb +11 -8
- data/test/test_socket.rb +17 -0
- data/test/test_sync.rb +21 -0
- data/test/test_throttler.rb +3 -6
- data/test/test_trace.rb +7 -5
- metadata +86 -76
- data/examples/adapters/concurrent-ruby.rb +0 -9
- data/examples/core/04-handling-signals.rb +0 -19
- data/examples/core/xx-at_exit.rb +0 -29
- data/examples/core/xx-backend.rb +0 -102
- 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
@@ -2,22 +2,29 @@
|
|
2
2
|
|
3
3
|
void Init_Fiber();
|
4
4
|
void Init_Polyphony();
|
5
|
-
void
|
5
|
+
void Init_Backend();
|
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
|
|
11
|
-
|
12
|
-
|
12
|
+
#ifdef POLYPHONY_PLAYGROUND
|
13
|
+
extern void playground();
|
14
|
+
#endif
|
13
15
|
|
16
|
+
void Init_polyphony_ext() {
|
14
17
|
Init_Polyphony();
|
15
|
-
|
18
|
+
|
19
|
+
Init_Backend();
|
16
20
|
Init_Queue();
|
17
21
|
Init_Event();
|
18
|
-
|
22
|
+
Init_Runqueue();
|
19
23
|
Init_Fiber();
|
20
24
|
Init_Thread();
|
21
|
-
|
22
25
|
Init_Tracing();
|
26
|
+
|
27
|
+
#ifdef POLYPHONY_PLAYGROUND
|
28
|
+
playground();
|
29
|
+
#endif
|
23
30
|
}
|
data/ext/polyphony/queue.c
CHANGED
@@ -85,12 +85,11 @@ VALUE Queue_shift(VALUE self) {
|
|
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
|
-
|
88
|
+
|
89
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
|
+
RAISE_IF_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
@@ -6,15 +6,14 @@ ID ID_ivar_join_wait_queue;
|
|
6
6
|
ID ID_ivar_main_fiber;
|
7
7
|
ID ID_ivar_result;
|
8
8
|
ID ID_ivar_terminated;
|
9
|
-
ID
|
10
|
-
ID ID_runnable_next;
|
9
|
+
ID ID_ivar_runqueue;
|
11
10
|
ID ID_stop;
|
12
11
|
|
13
12
|
static VALUE Thread_setup_fiber_scheduling(VALUE self) {
|
14
|
-
VALUE
|
15
|
-
|
13
|
+
VALUE runqueue = rb_funcall(cRunqueue, ID_new, 0);
|
14
|
+
|
16
15
|
rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
|
17
|
-
rb_ivar_set(self,
|
16
|
+
rb_ivar_set(self, ID_ivar_runqueue, runqueue);
|
18
17
|
|
19
18
|
return self;
|
20
19
|
}
|
@@ -35,10 +34,10 @@ static VALUE SYM_pending_watchers;
|
|
35
34
|
static VALUE Thread_fiber_scheduling_stats(VALUE self) {
|
36
35
|
VALUE backend = rb_ivar_get(self,ID_ivar_backend);
|
37
36
|
VALUE stats = rb_hash_new();
|
38
|
-
VALUE
|
37
|
+
VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
|
39
38
|
long pending_count;
|
40
39
|
|
41
|
-
long scheduled_count =
|
40
|
+
long scheduled_count = Runqueue_len(runqueue);
|
42
41
|
rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
|
43
42
|
|
44
43
|
pending_count = __BACKEND__.pending_count(backend);
|
@@ -47,30 +46,18 @@ static VALUE Thread_fiber_scheduling_stats(VALUE self) {
|
|
47
46
|
return stats;
|
48
47
|
}
|
49
48
|
|
50
|
-
|
51
|
-
VALUE
|
52
|
-
|
53
|
-
if (rb_fiber_alive_p(fiber) != Qtrue) return self;
|
49
|
+
void schedule_fiber(VALUE self, VALUE fiber, VALUE value, int prioritize) {
|
50
|
+
VALUE runqueue;
|
51
|
+
int already_runnable;
|
54
52
|
|
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
|
-
}
|
53
|
+
if (rb_fiber_alive_p(fiber) != Qtrue) return;
|
54
|
+
already_runnable = rb_ivar_get(fiber, ID_ivar_runnable) != Qnil;
|
65
55
|
|
66
|
-
rb_ivar_set(fiber, ID_runnable_value, value);
|
67
56
|
COND_TRACE(3, SYM_fiber_schedule, fiber, value);
|
68
|
-
|
57
|
+
runqueue = rb_ivar_get(self, ID_ivar_runqueue);
|
58
|
+
(prioritize ? Runqueue_unshift : Runqueue_push)(runqueue, fiber, value, already_runnable);
|
69
59
|
if (!already_runnable) {
|
70
|
-
|
71
|
-
Queue_push(queue, fiber);
|
72
|
-
rb_ivar_set(fiber, ID_runnable, Qtrue);
|
73
|
-
|
60
|
+
rb_ivar_set(fiber, ID_ivar_runnable, Qtrue);
|
74
61
|
if (rb_thread_current() != self) {
|
75
62
|
// If the fiber scheduling is done across threads, we need to make sure the
|
76
63
|
// target thread is woken up in case it is in the middle of running its
|
@@ -81,46 +68,22 @@ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
|
81
68
|
__BACKEND__.wakeup(backend);
|
82
69
|
}
|
83
70
|
}
|
71
|
+
}
|
72
|
+
|
73
|
+
VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
74
|
+
schedule_fiber(self, fiber, value, 0);
|
84
75
|
return self;
|
85
76
|
}
|
86
77
|
|
87
78
|
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 backend = rb_ivar_get(self, ID_ivar_backend);
|
114
|
-
__BACKEND__.wakeup(backend);
|
115
|
-
}
|
79
|
+
schedule_fiber(self, fiber, value, 1);
|
116
80
|
return self;
|
117
81
|
}
|
118
82
|
|
119
83
|
VALUE Thread_switch_fiber(VALUE self) {
|
120
84
|
VALUE current_fiber = rb_fiber_current();
|
121
|
-
VALUE
|
122
|
-
|
123
|
-
VALUE value;
|
85
|
+
VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
|
86
|
+
runqueue_entry next;
|
124
87
|
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
125
88
|
int ref_count;
|
126
89
|
int backend_was_polled = 0;
|
@@ -130,48 +93,40 @@ VALUE Thread_switch_fiber(VALUE self) {
|
|
130
93
|
|
131
94
|
ref_count = __BACKEND__.ref_count(backend);
|
132
95
|
while (1) {
|
133
|
-
|
134
|
-
if (
|
96
|
+
next = Runqueue_shift(runqueue);
|
97
|
+
if (next.fiber != Qnil) {
|
135
98
|
if (backend_was_polled == 0 && ref_count > 0) {
|
136
|
-
// this
|
137
|
-
|
138
|
-
__BACKEND__.poll(backend, Qtrue, current_fiber, queue);
|
99
|
+
// this prevents event starvation in case the run queue never empties
|
100
|
+
__BACKEND__.poll(backend, Qtrue, current_fiber, runqueue);
|
139
101
|
}
|
140
102
|
break;
|
141
103
|
}
|
142
104
|
if (ref_count == 0) break;
|
143
105
|
|
144
|
-
__BACKEND__.poll(backend, Qnil, current_fiber,
|
106
|
+
__BACKEND__.poll(backend, Qnil, current_fiber, runqueue);
|
145
107
|
backend_was_polled = 1;
|
146
108
|
}
|
147
109
|
|
148
|
-
if (
|
110
|
+
if (next.fiber == Qnil) return Qnil;
|
149
111
|
|
150
112
|
// 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
|
-
}
|
113
|
+
COND_TRACE(3, SYM_fiber_run, next.fiber, next.value);
|
160
114
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
return
|
115
|
+
rb_ivar_set(next.fiber, ID_ivar_runnable, Qnil);
|
116
|
+
RB_GC_GUARD(next.fiber);
|
117
|
+
RB_GC_GUARD(next.value);
|
118
|
+
return (next.fiber == current_fiber) ?
|
119
|
+
next.value : rb_funcall(next.fiber, ID_transfer, 1, next.value);
|
165
120
|
}
|
166
121
|
|
167
122
|
VALUE Thread_reset_fiber_scheduling(VALUE self) {
|
168
|
-
VALUE queue = rb_ivar_get(self,
|
169
|
-
|
123
|
+
VALUE queue = rb_ivar_get(self, ID_ivar_runqueue);
|
124
|
+
Runqueue_clear(queue);
|
170
125
|
Thread_fiber_reset_ref_count(self);
|
171
126
|
return self;
|
172
127
|
}
|
173
128
|
|
174
|
-
VALUE
|
129
|
+
VALUE Thread_fiber_schedule_and_wakeup(VALUE self, VALUE fiber, VALUE resume_obj) {
|
175
130
|
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
176
131
|
if (fiber != Qnil) {
|
177
132
|
Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
|
@@ -194,25 +149,23 @@ void Init_Thread() {
|
|
194
149
|
rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
|
195
150
|
rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
|
196
151
|
rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
|
197
|
-
rb_define_method(rb_cThread, "
|
152
|
+
rb_define_method(rb_cThread, "schedule_and_wakeup", Thread_fiber_schedule_and_wakeup, 2);
|
198
153
|
|
199
154
|
rb_define_method(rb_cThread, "schedule_fiber", Thread_schedule_fiber, 2);
|
200
155
|
rb_define_method(rb_cThread, "schedule_fiber_with_priority",
|
201
156
|
Thread_schedule_fiber_with_priority, 2);
|
202
157
|
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
158
|
|
205
159
|
rb_define_method(rb_cThread, "debug!", Thread_debug, 0);
|
206
160
|
|
207
|
-
ID_deactivate_all_watchers_post_fork
|
208
|
-
ID_ivar_backend
|
209
|
-
ID_ivar_join_wait_queue
|
210
|
-
ID_ivar_main_fiber
|
211
|
-
ID_ivar_result
|
212
|
-
ID_ivar_terminated
|
213
|
-
|
214
|
-
|
215
|
-
ID_stop = rb_intern("stop");
|
161
|
+
ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
|
162
|
+
ID_ivar_backend = rb_intern("@backend");
|
163
|
+
ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
|
164
|
+
ID_ivar_main_fiber = rb_intern("@main_fiber");
|
165
|
+
ID_ivar_result = rb_intern("@result");
|
166
|
+
ID_ivar_terminated = rb_intern("@terminated");
|
167
|
+
ID_ivar_runqueue = rb_intern("@runqueue");
|
168
|
+
ID_stop = rb_intern("stop");
|
216
169
|
|
217
170
|
SYM_scheduled_fibers = ID2SYM(rb_intern("scheduled_fibers"));
|
218
171
|
SYM_pending_watchers = ID2SYM(rb_intern("pending_watchers"));
|