polyphony 0.46.0 → 0.47.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/Gemfile.lock +1 -1
- data/TODO.md +54 -23
- data/bin/test +4 -0
- data/examples/core/enumerable.rb +64 -0
- data/examples/performance/fiber_resume.rb +43 -0
- data/examples/performance/fiber_transfer.rb +13 -4
- data/examples/performance/thread-vs-fiber/compare.rb +59 -0
- data/examples/performance/thread-vs-fiber/em_server.rb +33 -0
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +9 -19
- data/examples/performance/thread-vs-fiber/threaded_server.rb +22 -15
- data/examples/performance/thread_switch.rb +44 -0
- data/ext/polyphony/backend_common.h +20 -0
- data/ext/polyphony/backend_io_uring.c +127 -16
- data/ext/polyphony/backend_io_uring_context.c +1 -0
- data/ext/polyphony/backend_io_uring_context.h +1 -0
- data/ext/polyphony/backend_libev.c +102 -0
- data/ext/polyphony/fiber.c +11 -7
- data/ext/polyphony/polyphony.c +3 -0
- data/ext/polyphony/polyphony.h +7 -7
- data/ext/polyphony/queue.c +99 -34
- data/ext/polyphony/thread.c +1 -3
- data/lib/polyphony/core/exceptions.rb +0 -4
- data/lib/polyphony/core/global_api.rb +49 -31
- data/lib/polyphony/extensions/core.rb +9 -15
- data/lib/polyphony/extensions/fiber.rb +8 -2
- data/lib/polyphony/extensions/openssl.rb +6 -0
- data/lib/polyphony/extensions/socket.rb +18 -4
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +1 -1
- data/test/stress.rb +1 -1
- data/test/test_backend.rb +59 -0
- data/test/test_fiber.rb +33 -4
- data/test/test_global_api.rb +85 -1
- data/test/test_queue.rb +117 -0
- data/test/test_signal.rb +18 -0
- data/test/test_socket.rb +2 -2
- metadata +8 -2
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fiber'
|
4
|
+
|
5
|
+
class Fiber
|
6
|
+
attr_accessor :next
|
7
|
+
end
|
8
|
+
|
9
|
+
# This program shows how the performance of Fiber.transfer degrades as the fiber
|
10
|
+
# count increases
|
11
|
+
|
12
|
+
def run(num_threads)
|
13
|
+
count = 0
|
14
|
+
|
15
|
+
GC.start
|
16
|
+
GC.disable
|
17
|
+
|
18
|
+
threads = []
|
19
|
+
t0 = Time.now
|
20
|
+
limit = 10_000_000 / num_threads
|
21
|
+
num_threads.times do
|
22
|
+
threads << Thread.new do
|
23
|
+
individual_count = 0
|
24
|
+
loop do
|
25
|
+
individual_count += 1
|
26
|
+
count += 1
|
27
|
+
break if individual_count == limit
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
threads.each(&:join)
|
33
|
+
elapsed = Time.now - t0
|
34
|
+
|
35
|
+
puts "threads: #{num_threads} count: #{count} rate: #{count / elapsed}"
|
36
|
+
rescue Exception => e
|
37
|
+
puts "Stopped at #{count} threads"
|
38
|
+
p e
|
39
|
+
end
|
40
|
+
|
41
|
+
run(100)
|
42
|
+
run(1000)
|
43
|
+
run(10000)
|
44
|
+
run(100000)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
#include <time.h>
|
2
|
+
|
1
3
|
#include "ruby.h"
|
2
4
|
#include "ruby/io.h"
|
3
5
|
|
@@ -107,3 +109,21 @@ inline void rectify_io_file_pos(rb_io_t *fptr) {
|
|
107
109
|
fptr->rbuf.len = 0;
|
108
110
|
}
|
109
111
|
}
|
112
|
+
|
113
|
+
inline double current_time() {
|
114
|
+
struct timespec ts;
|
115
|
+
clock_gettime(CLOCK_MONOTONIC, &ts);
|
116
|
+
long long ns = ts.tv_sec;
|
117
|
+
ns = ns * 1000000000 + ts.tv_nsec;
|
118
|
+
double t = ns;
|
119
|
+
return t / 1e9;
|
120
|
+
}
|
121
|
+
|
122
|
+
inline VALUE backend_timeout_exception(VALUE exception) {
|
123
|
+
if (RTEST(rb_obj_is_kind_of(exception, rb_cArray)))
|
124
|
+
return rb_funcall(rb_ary_entry(exception, 0), ID_new, 1, rb_ary_entry(exception, 1));
|
125
|
+
else if (RTEST(rb_obj_is_kind_of(exception, rb_cClass)))
|
126
|
+
return rb_funcall(exception, ID_new, 0);
|
127
|
+
else
|
128
|
+
return rb_funcall(rb_eRuntimeError, ID_new, 1, exception);
|
129
|
+
}
|
@@ -7,18 +7,18 @@
|
|
7
7
|
#include <fcntl.h>
|
8
8
|
#include <netinet/in.h>
|
9
9
|
#include <arpa/inet.h>
|
10
|
-
|
11
|
-
#include "polyphony.h"
|
12
|
-
#include "../liburing/liburing.h"
|
13
|
-
#include "ruby/thread.h"
|
14
|
-
#include "backend_io_uring_context.h"
|
15
|
-
|
10
|
+
#include <stdnoreturn.h>
|
16
11
|
#include <poll.h>
|
17
12
|
#include <sys/types.h>
|
18
13
|
#include <sys/eventfd.h>
|
19
14
|
#include <sys/wait.h>
|
20
15
|
#include <errno.h>
|
21
16
|
|
17
|
+
#include "polyphony.h"
|
18
|
+
#include "../liburing/liburing.h"
|
19
|
+
#include "ruby/thread.h"
|
20
|
+
#include "backend_io_uring_context.h"
|
21
|
+
|
22
22
|
#ifndef __NR_pidfd_open
|
23
23
|
#define __NR_pidfd_open 434 /* System call # on most architectures */
|
24
24
|
#endif
|
@@ -171,7 +171,7 @@ void io_uring_backend_handle_completion(struct io_uring_cqe *cqe, Backend_t *bac
|
|
171
171
|
// otherwise, we mark it as completed, schedule the fiber and let it deal
|
172
172
|
// with releasing the context
|
173
173
|
ctx->completed = 1;
|
174
|
-
if (ctx->result != -ECANCELED) Fiber_make_runnable(ctx->fiber,
|
174
|
+
if (ctx->result != -ECANCELED) Fiber_make_runnable(ctx->fiber, ctx->resume_value);
|
175
175
|
}
|
176
176
|
}
|
177
177
|
|
@@ -304,6 +304,7 @@ int io_uring_backend_defer_submit_and_await(
|
|
304
304
|
|
305
305
|
if (value_ptr) (*value_ptr) = switchpoint_result;
|
306
306
|
RB_GC_GUARD(switchpoint_result);
|
307
|
+
RB_GC_GUARD(ctx->fiber);
|
307
308
|
return ctx->result;
|
308
309
|
}
|
309
310
|
|
@@ -773,29 +774,137 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
|
|
773
774
|
return self;
|
774
775
|
}
|
775
776
|
|
776
|
-
|
777
|
-
Backend_t *backend;
|
778
|
-
struct io_uring_sqe *sqe;
|
777
|
+
inline struct __kernel_timespec double_to_timespec(double duration) {
|
779
778
|
double duration_integral;
|
780
|
-
double duration_fraction = modf(
|
779
|
+
double duration_fraction = modf(duration, &duration_integral);
|
781
780
|
struct __kernel_timespec ts;
|
782
|
-
|
783
|
-
GetBackend(self, backend);
|
784
|
-
sqe = io_uring_get_sqe(&backend->ring);
|
785
781
|
ts.tv_sec = duration_integral;
|
786
782
|
ts.tv_nsec = floor(duration_fraction * 1000000000);
|
783
|
+
return ts;
|
784
|
+
}
|
785
|
+
|
786
|
+
inline struct __kernel_timespec duration_to_timespec(VALUE duration) {
|
787
|
+
return double_to_timespec(NUM2DBL(duration));
|
788
|
+
}
|
789
|
+
|
790
|
+
// returns true if completed, 0 otherwise
|
791
|
+
int io_uring_backend_submit_timeout_and_await(Backend_t *backend, double duration, VALUE *resume_value) {
|
792
|
+
struct __kernel_timespec ts = double_to_timespec(duration);
|
793
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
787
794
|
|
788
|
-
VALUE resume_value = Qnil;
|
789
795
|
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_TIMEOUT);
|
790
796
|
io_uring_prep_timeout(sqe, &ts, 0, 0);
|
791
797
|
|
792
|
-
io_uring_backend_defer_submit_and_await(backend, sqe, ctx,
|
798
|
+
io_uring_backend_defer_submit_and_await(backend, sqe, ctx, resume_value);
|
793
799
|
OP_CONTEXT_RELEASE(&backend->store, ctx);
|
800
|
+
return ctx->completed;
|
801
|
+
}
|
802
|
+
|
803
|
+
VALUE Backend_sleep(VALUE self, VALUE duration) {
|
804
|
+
Backend_t *backend;
|
805
|
+
GetBackend(self, backend);
|
806
|
+
|
807
|
+
VALUE resume_value = Qnil;
|
808
|
+
io_uring_backend_submit_timeout_and_await(backend, NUM2DBL(duration), &resume_value);
|
794
809
|
RAISE_IF_EXCEPTION(resume_value);
|
795
810
|
RB_GC_GUARD(resume_value);
|
796
811
|
return resume_value;
|
797
812
|
}
|
798
813
|
|
814
|
+
VALUE Backend_timer_loop(VALUE self, VALUE interval) {
|
815
|
+
Backend_t *backend;
|
816
|
+
double interval_d = NUM2DBL(interval);
|
817
|
+
GetBackend(self, backend);
|
818
|
+
double next_time = 0.;
|
819
|
+
|
820
|
+
while (1) {
|
821
|
+
double now = current_time();
|
822
|
+
if (next_time == 0.) next_time = current_time() + interval_d;
|
823
|
+
double sleep_duration = next_time - now;
|
824
|
+
if (sleep_duration < 0) sleep_duration = 0;
|
825
|
+
|
826
|
+
VALUE resume_value = Qnil;
|
827
|
+
int completed = io_uring_backend_submit_timeout_and_await(backend, sleep_duration, &resume_value);
|
828
|
+
RAISE_IF_EXCEPTION(resume_value);
|
829
|
+
if (!completed) return resume_value;
|
830
|
+
RB_GC_GUARD(resume_value);
|
831
|
+
|
832
|
+
rb_yield(Qnil);
|
833
|
+
|
834
|
+
while (1) {
|
835
|
+
next_time += interval_d;
|
836
|
+
if (next_time > now) break;
|
837
|
+
}
|
838
|
+
}
|
839
|
+
}
|
840
|
+
|
841
|
+
VALUE Backend_timeout_safe(VALUE arg) {
|
842
|
+
return rb_yield(arg);
|
843
|
+
}
|
844
|
+
|
845
|
+
VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
|
846
|
+
return exception;
|
847
|
+
}
|
848
|
+
|
849
|
+
VALUE Backend_timeout_ensure_safe(VALUE arg) {
|
850
|
+
return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
|
851
|
+
}
|
852
|
+
|
853
|
+
struct Backend_timeout_ctx {
|
854
|
+
Backend_t *backend;
|
855
|
+
op_context_t *ctx;
|
856
|
+
};
|
857
|
+
|
858
|
+
VALUE Backend_timeout_ensure(VALUE arg) {
|
859
|
+
struct Backend_timeout_ctx *timeout_ctx = (struct Backend_timeout_ctx *)arg;
|
860
|
+
if (!timeout_ctx->ctx->completed) {
|
861
|
+
timeout_ctx->ctx->result = -ECANCELED;
|
862
|
+
|
863
|
+
// op was not completed, so we need to cancel it
|
864
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&timeout_ctx->backend->ring);
|
865
|
+
io_uring_prep_cancel(sqe, timeout_ctx->ctx, 0);
|
866
|
+
timeout_ctx->backend->pending_sqes = 0;
|
867
|
+
io_uring_submit(&timeout_ctx->backend->ring);
|
868
|
+
}
|
869
|
+
OP_CONTEXT_RELEASE(&timeout_ctx->backend->store, timeout_ctx->ctx);
|
870
|
+
return Qnil;
|
871
|
+
}
|
872
|
+
|
873
|
+
VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
|
874
|
+
VALUE duration;
|
875
|
+
VALUE exception;
|
876
|
+
VALUE move_on_value = Qnil;
|
877
|
+
rb_scan_args(argc, argv, "21", &duration, &exception, &move_on_value);
|
878
|
+
|
879
|
+
struct __kernel_timespec ts = duration_to_timespec(duration);
|
880
|
+
Backend_t *backend;
|
881
|
+
GetBackend(self, backend);
|
882
|
+
VALUE result = Qnil;
|
883
|
+
VALUE timeout = rb_funcall(cTimeoutException, ID_new, 0);
|
884
|
+
|
885
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&backend->ring);
|
886
|
+
|
887
|
+
op_context_t *ctx = OP_CONTEXT_ACQUIRE(&backend->store, OP_TIMEOUT);
|
888
|
+
ctx->resume_value = timeout;
|
889
|
+
io_uring_prep_timeout(sqe, &ts, 0, 0);
|
890
|
+
io_uring_sqe_set_data(sqe, ctx);
|
891
|
+
io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
|
892
|
+
io_uring_backend_defer_submit(backend);
|
893
|
+
|
894
|
+
struct Backend_timeout_ctx timeout_ctx = {backend, ctx};
|
895
|
+
result = rb_ensure(Backend_timeout_ensure_safe, Qnil, Backend_timeout_ensure, (VALUE)&timeout_ctx);
|
896
|
+
|
897
|
+
if (result == timeout) {
|
898
|
+
if (exception == Qnil) return move_on_value;
|
899
|
+
RAISE_EXCEPTION(backend_timeout_exception(exception));
|
900
|
+
}
|
901
|
+
|
902
|
+
RAISE_IF_EXCEPTION(result);
|
903
|
+
RB_GC_GUARD(result);
|
904
|
+
RB_GC_GUARD(timeout);
|
905
|
+
return result;
|
906
|
+
}
|
907
|
+
|
799
908
|
VALUE Backend_waitpid(VALUE self, VALUE pid) {
|
800
909
|
Backend_t *backend;
|
801
910
|
int pid_int = NUM2INT(pid);
|
@@ -864,6 +973,8 @@ void Init_Backend() {
|
|
864
973
|
rb_define_method(cBackend, "connect", Backend_connect, 3);
|
865
974
|
rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
|
866
975
|
rb_define_method(cBackend, "sleep", Backend_sleep, 1);
|
976
|
+
rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
|
977
|
+
rb_define_method(cBackend, "timeout", Backend_timeout, -1);
|
867
978
|
rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
|
868
979
|
rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
|
869
980
|
|
@@ -7,6 +7,7 @@
|
|
7
7
|
#include <fcntl.h>
|
8
8
|
#include <netinet/in.h>
|
9
9
|
#include <arpa/inet.h>
|
10
|
+
#include <stdnoreturn.h>
|
10
11
|
|
11
12
|
#include "polyphony.h"
|
12
13
|
#include "../libev/ev.h"
|
@@ -688,6 +689,105 @@ VALUE Backend_sleep(VALUE self, VALUE duration) {
|
|
688
689
|
return switchpoint_result;
|
689
690
|
}
|
690
691
|
|
692
|
+
noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
|
693
|
+
Backend_t *backend;
|
694
|
+
struct libev_timer watcher;
|
695
|
+
double interval_d = NUM2DBL(interval);
|
696
|
+
|
697
|
+
GetBackend(self, backend);
|
698
|
+
watcher.fiber = rb_fiber_current();
|
699
|
+
|
700
|
+
double next_time = 0.;
|
701
|
+
|
702
|
+
while (1) {
|
703
|
+
double now = current_time();
|
704
|
+
if (next_time == 0.) next_time = current_time() + interval_d;
|
705
|
+
double sleep_duration = next_time - now;
|
706
|
+
if (sleep_duration < 0) sleep_duration = 0;
|
707
|
+
|
708
|
+
VALUE switchpoint_result = Qnil;
|
709
|
+
ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
|
710
|
+
ev_timer_start(backend->ev_loop, &watcher.timer);
|
711
|
+
switchpoint_result = backend_await(backend);
|
712
|
+
ev_timer_stop(backend->ev_loop, &watcher.timer);
|
713
|
+
RAISE_IF_EXCEPTION(switchpoint_result);
|
714
|
+
RB_GC_GUARD(switchpoint_result);
|
715
|
+
|
716
|
+
rb_yield(Qnil);
|
717
|
+
|
718
|
+
while (1) {
|
719
|
+
next_time += interval_d;
|
720
|
+
if (next_time > now) break;
|
721
|
+
}
|
722
|
+
}
|
723
|
+
}
|
724
|
+
|
725
|
+
VALUE Backend_timeout_safe(VALUE arg) {
|
726
|
+
return rb_yield(arg);
|
727
|
+
}
|
728
|
+
|
729
|
+
VALUE Backend_timeout_rescue(VALUE arg, VALUE exception) {
|
730
|
+
return exception;
|
731
|
+
}
|
732
|
+
|
733
|
+
VALUE Backend_timeout_ensure_safe(VALUE arg) {
|
734
|
+
return rb_rescue2(Backend_timeout_safe, Qnil, Backend_timeout_rescue, Qnil, rb_eException, (VALUE)0);
|
735
|
+
}
|
736
|
+
|
737
|
+
struct libev_timeout {
|
738
|
+
struct ev_timer timer;
|
739
|
+
VALUE fiber;
|
740
|
+
VALUE resume_value;
|
741
|
+
};
|
742
|
+
|
743
|
+
struct Backend_timeout_ctx {
|
744
|
+
Backend_t *backend;
|
745
|
+
struct libev_timeout *watcher;
|
746
|
+
};
|
747
|
+
|
748
|
+
VALUE Backend_timeout_ensure(VALUE arg) {
|
749
|
+
struct Backend_timeout_ctx *timeout_ctx = (struct Backend_timeout_ctx *)arg;
|
750
|
+
ev_timer_stop(timeout_ctx->backend->ev_loop, &(timeout_ctx->watcher->timer));
|
751
|
+
return Qnil;
|
752
|
+
}
|
753
|
+
|
754
|
+
void Backend_timeout_callback(EV_P_ ev_timer *w, int revents)
|
755
|
+
{
|
756
|
+
struct libev_timeout *watcher = (struct libev_timeout *)w;
|
757
|
+
Fiber_make_runnable(watcher->fiber, watcher->resume_value);
|
758
|
+
}
|
759
|
+
|
760
|
+
VALUE Backend_timeout(int argc,VALUE *argv, VALUE self) {
|
761
|
+
VALUE duration;
|
762
|
+
VALUE exception;
|
763
|
+
VALUE move_on_value = Qnil;
|
764
|
+
rb_scan_args(argc, argv, "21", &duration, &exception, &move_on_value);
|
765
|
+
|
766
|
+
Backend_t *backend;
|
767
|
+
struct libev_timeout watcher;
|
768
|
+
VALUE result = Qnil;
|
769
|
+
VALUE timeout = rb_funcall(cTimeoutException, ID_new, 0);
|
770
|
+
|
771
|
+
GetBackend(self, backend);
|
772
|
+
watcher.fiber = rb_fiber_current();
|
773
|
+
watcher.resume_value = timeout;
|
774
|
+
ev_timer_init(&watcher.timer, Backend_timeout_callback, NUM2DBL(duration), 0.);
|
775
|
+
ev_timer_start(backend->ev_loop, &watcher.timer);
|
776
|
+
|
777
|
+
struct Backend_timeout_ctx timeout_ctx = {backend, &watcher};
|
778
|
+
result = rb_ensure(Backend_timeout_ensure_safe, Qnil, Backend_timeout_ensure, (VALUE)&timeout_ctx);
|
779
|
+
|
780
|
+
if (result == timeout) {
|
781
|
+
if (exception == Qnil) return move_on_value;
|
782
|
+
RAISE_EXCEPTION(backend_timeout_exception(exception));
|
783
|
+
}
|
784
|
+
|
785
|
+
RAISE_IF_EXCEPTION(result);
|
786
|
+
RB_GC_GUARD(result);
|
787
|
+
RB_GC_GUARD(timeout);
|
788
|
+
return result;
|
789
|
+
}
|
790
|
+
|
691
791
|
struct libev_child {
|
692
792
|
struct ev_child child;
|
693
793
|
VALUE fiber;
|
@@ -777,6 +877,8 @@ void Init_Backend() {
|
|
777
877
|
rb_define_method(cBackend, "send", Backend_write, 2);
|
778
878
|
rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
|
779
879
|
rb_define_method(cBackend, "sleep", Backend_sleep, 1);
|
880
|
+
rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
|
881
|
+
rb_define_method(cBackend, "timeout", Backend_timeout, -1);
|
780
882
|
rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
|
781
883
|
rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
|
782
884
|
|
data/ext/polyphony/fiber.c
CHANGED
@@ -21,7 +21,7 @@ VALUE SYM_fiber_terminate;
|
|
21
21
|
|
22
22
|
static VALUE Fiber_safe_transfer(int argc, VALUE *argv, VALUE self) {
|
23
23
|
VALUE arg = (argc == 0) ? Qnil : argv[0];
|
24
|
-
VALUE ret =
|
24
|
+
VALUE ret = FIBER_TRANSFER(self, arg);
|
25
25
|
|
26
26
|
RAISE_IF_EXCEPTION(ret);
|
27
27
|
RB_GC_GUARD(ret);
|
@@ -42,10 +42,6 @@ inline VALUE Fiber_auto_watcher(VALUE self) {
|
|
42
42
|
void Fiber_make_runnable(VALUE fiber, VALUE value) {
|
43
43
|
VALUE thread = rb_ivar_get(fiber, ID_ivar_thread);
|
44
44
|
if (thread == Qnil) {
|
45
|
-
INSPECT("Fiber with no thread", fiber);
|
46
|
-
TRACE_CALLER();
|
47
|
-
TRACE_C_STACK();
|
48
|
-
exit(-1);
|
49
45
|
rb_raise(rb_eRuntimeError, "No thread set for fiber");
|
50
46
|
// rb_warn("No thread set for fiber");
|
51
47
|
return;
|
@@ -57,7 +53,6 @@ void Fiber_make_runnable(VALUE fiber, VALUE value) {
|
|
57
53
|
void Fiber_make_runnable_with_priority(VALUE fiber, VALUE value) {
|
58
54
|
VALUE thread = rb_ivar_get(fiber, ID_ivar_thread);
|
59
55
|
if (thread == Qnil) {
|
60
|
-
INSPECT("Fiber with no thread", fiber);
|
61
56
|
rb_raise(rb_eRuntimeError, "No thread set for fiber");
|
62
57
|
// rb_warn("No thread set for fiber");
|
63
58
|
return;
|
@@ -133,6 +128,15 @@ VALUE Fiber_receive(VALUE self) {
|
|
133
128
|
return Queue_shift(mailbox);
|
134
129
|
}
|
135
130
|
|
131
|
+
VALUE Fiber_mailbox(VALUE self) {
|
132
|
+
VALUE mailbox = rb_ivar_get(self, ID_ivar_mailbox);
|
133
|
+
if (mailbox == Qnil) {
|
134
|
+
mailbox = rb_funcall(cQueue, ID_new, 0);
|
135
|
+
rb_ivar_set(self, ID_ivar_mailbox, mailbox);
|
136
|
+
}
|
137
|
+
return mailbox;
|
138
|
+
}
|
139
|
+
|
136
140
|
VALUE Fiber_receive_all_pending(VALUE self) {
|
137
141
|
VALUE mailbox = rb_ivar_get(self, ID_ivar_mailbox);
|
138
142
|
return (mailbox == Qnil) ? rb_ary_new() : Queue_shift_all(mailbox);
|
@@ -151,9 +155,9 @@ void Init_Fiber() {
|
|
151
155
|
|
152
156
|
rb_define_method(cFiber, "<<", Fiber_send, 1);
|
153
157
|
rb_define_method(cFiber, "send", Fiber_send, 1);
|
154
|
-
|
155
158
|
rb_define_method(cFiber, "receive", Fiber_receive, 0);
|
156
159
|
rb_define_method(cFiber, "receive_all_pending", Fiber_receive_all_pending, 0);
|
160
|
+
rb_define_method(cFiber, "mailbox", Fiber_mailbox, 0);
|
157
161
|
|
158
162
|
SYM_dead = ID2SYM(rb_intern("dead"));
|
159
163
|
SYM_running = ID2SYM(rb_intern("running"));
|