polyphony 0.41 → 0.43.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/Gemfile.lock +6 -6
  4. data/README.md +0 -1
  5. data/Rakefile +1 -1
  6. data/TODO.md +18 -9
  7. data/docs/_config.yml +56 -7
  8. data/docs/_sass/custom/custom.scss +6 -26
  9. data/docs/_sass/overrides.scss +0 -46
  10. data/docs/{user-guide → _user-guide}/all-about-timers.md +0 -0
  11. data/docs/_user-guide/index.md +9 -0
  12. data/docs/{user-guide → _user-guide}/web-server.md +0 -0
  13. data/docs/api-reference/index.md +9 -0
  14. data/docs/api-reference/polyphony-process.md +1 -1
  15. data/docs/api-reference/thread.md +1 -1
  16. data/docs/faq.md +21 -11
  17. data/docs/favicon.ico +0 -0
  18. data/docs/getting-started/index.md +10 -0
  19. data/docs/getting-started/installing.md +2 -6
  20. data/docs/getting-started/overview.md +486 -0
  21. data/docs/getting-started/tutorial.md +27 -19
  22. data/docs/index.md +6 -2
  23. data/docs/main-concepts/concurrency.md +0 -5
  24. data/docs/main-concepts/design-principles.md +2 -12
  25. data/docs/main-concepts/index.md +9 -0
  26. data/docs/polyphony-logo.png +0 -0
  27. data/examples/adapters/concurrent-ruby.rb +9 -0
  28. data/examples/adapters/redis_blpop.rb +12 -0
  29. data/examples/core/01-spinning-up-fibers.rb +1 -0
  30. data/examples/core/03-interrupting.rb +4 -1
  31. data/examples/core/04-handling-signals.rb +19 -0
  32. data/examples/core/xx-daemon.rb +14 -0
  33. data/examples/performance/thread-vs-fiber/polyphony_server.rb +6 -18
  34. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
  35. data/examples/performance/xx-array.rb +11 -0
  36. data/examples/performance/xx-fiber-switch.rb +9 -0
  37. data/examples/performance/xx-snooze.rb +15 -0
  38. data/ext/polyphony/fiber.c +0 -3
  39. data/ext/polyphony/libev_agent.c +303 -81
  40. data/ext/polyphony/libev_queue.c +8 -5
  41. data/ext/polyphony/polyphony.c +0 -16
  42. data/ext/polyphony/polyphony.h +6 -6
  43. data/ext/polyphony/polyphony_ext.c +0 -2
  44. data/ext/polyphony/thread.c +8 -42
  45. data/lib/polyphony.rb +29 -2
  46. data/lib/polyphony/adapters/redis.rb +3 -2
  47. data/lib/polyphony/core/channel.rb +2 -2
  48. data/lib/polyphony/core/global_api.rb +6 -4
  49. data/lib/polyphony/core/resource_pool.rb +19 -9
  50. data/lib/polyphony/extensions/core.rb +8 -3
  51. data/lib/polyphony/extensions/fiber.rb +0 -12
  52. data/lib/polyphony/extensions/io.rb +4 -0
  53. data/lib/polyphony/extensions/openssl.rb +34 -10
  54. data/lib/polyphony/extensions/socket.rb +2 -2
  55. data/lib/polyphony/version.rb +1 -1
  56. data/polyphony.gemspec +1 -1
  57. data/test/test_agent.rb +59 -6
  58. data/test/test_fiber.rb +3 -3
  59. data/test/test_global_api.rb +48 -15
  60. data/test/test_resource_pool.rb +12 -0
  61. data/test/test_socket.rb +5 -4
  62. data/test/test_throttler.rb +6 -5
  63. metadata +21 -21
  64. data/docs/_includes/head.html +0 -40
  65. data/docs/_includes/nav.html +0 -51
  66. data/docs/_includes/prevnext.html +0 -17
  67. data/docs/_layouts/default.html +0 -106
  68. data/docs/api-reference.md +0 -11
  69. data/docs/api-reference/gyro-async.md +0 -57
  70. data/docs/api-reference/gyro-child.md +0 -29
  71. data/docs/api-reference/gyro-queue.md +0 -44
  72. data/docs/api-reference/gyro-timer.md +0 -51
  73. data/docs/api-reference/gyro.md +0 -25
  74. data/docs/getting-started.md +0 -10
  75. data/docs/main-concepts.md +0 -10
  76. data/docs/user-guide.md +0 -10
  77. data/examples/core/forever_sleep.rb +0 -19
  78. data/ext/polyphony/socket.c +0 -213
@@ -120,11 +120,12 @@ static VALUE LibevQueue_initialize(VALUE self) {
120
120
 
121
121
  VALUE LibevQueue_push(VALUE self, VALUE value) {
122
122
  LibevQueue_t *queue;
123
- struct async_watcher *watcher;
124
123
  GetQueue(self, queue);
125
- watcher = async_queue_pop(&queue->shift_queue);
126
- if (watcher) {
127
- ev_async_send(watcher->ev_loop, &watcher->async);
124
+ if (queue->shift_queue.count > 0) {
125
+ struct async_watcher *watcher = async_queue_pop(&queue->shift_queue);
126
+ if (watcher) {
127
+ ev_async_send(watcher->ev_loop, &watcher->async);
128
+ }
128
129
  }
129
130
  rb_ary_push(queue->items, value);
130
131
  return self;
@@ -137,6 +138,8 @@ void async_queue_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, in
137
138
  Fiber_make_runnable(watcher->fiber, Qnil);
138
139
  }
139
140
 
141
+ VALUE libev_agent_await(VALUE self);
142
+
140
143
  VALUE LibevQueue_shift(VALUE self) {
141
144
  LibevQueue_t *queue;
142
145
  GetQueue(self, queue);
@@ -152,7 +155,7 @@ VALUE LibevQueue_shift(VALUE self) {
152
155
  ev_async_init(&watcher.async, async_queue_callback);
153
156
  ev_async_start(watcher.ev_loop, &watcher.async);
154
157
 
155
- switchpoint_result = Polyphony_switchpoint();
158
+ switchpoint_result = libev_agent_await(agent);
156
159
  ev_async_stop(watcher.ev_loop, &watcher.async);
157
160
 
158
161
  if (RTEST(rb_obj_is_kind_of(switchpoint_result, rb_eException))) {
@@ -7,11 +7,8 @@ ID ID_call;
7
7
  ID ID_caller;
8
8
  ID ID_clear;
9
9
  ID ID_each;
10
- ID ID_empty;
11
10
  ID ID_inspect;
12
11
  ID ID_new;
13
- ID ID_pop;
14
- ID ID_push;
15
12
  ID ID_raise;
16
13
  ID ID_ivar_running;
17
14
  ID ID_ivar_thread;
@@ -36,14 +33,6 @@ VALUE Polyphony_snooze(VALUE self) {
36
33
  return ret;
37
34
  }
38
35
 
39
- static VALUE Polyphony_ref(VALUE self) {
40
- return Thread_ref(rb_thread_current());
41
- }
42
-
43
- static VALUE Polyphony_unref(VALUE self) {
44
- return Thread_unref(rb_thread_current());
45
- }
46
-
47
36
  static VALUE Polyphony_suspend(VALUE self) {
48
37
  VALUE ret = Thread_switch_fiber(rb_thread_current());
49
38
 
@@ -60,9 +49,7 @@ VALUE Polyphony_trace(VALUE self, VALUE enabled) {
60
49
  void Init_Polyphony() {
61
50
  mPolyphony = rb_define_module("Polyphony");
62
51
 
63
- rb_define_singleton_method(mPolyphony, "ref", Polyphony_ref, 0);
64
52
  rb_define_singleton_method(mPolyphony, "trace", Polyphony_trace, 1);
65
- rb_define_singleton_method(mPolyphony, "unref", Polyphony_unref, 0);
66
53
 
67
54
  rb_define_global_function("snooze", Polyphony_snooze, 0);
68
55
  rb_define_global_function("suspend", Polyphony_suspend, 0);
@@ -72,13 +59,10 @@ void Init_Polyphony() {
72
59
  ID_caller = rb_intern("caller");
73
60
  ID_clear = rb_intern("clear");
74
61
  ID_each = rb_intern("each");
75
- ID_empty = rb_intern("empty?");
76
62
  ID_inspect = rb_intern("inspect");
77
63
  ID_ivar_running = rb_intern("@running");
78
64
  ID_ivar_thread = rb_intern("@thread");
79
65
  ID_new = rb_intern("new");
80
- ID_pop = rb_intern("pop");
81
- ID_push = rb_intern("push");
82
66
  ID_raise = rb_intern("raise");
83
67
  ID_runnable = rb_intern("runnable");
84
68
  ID_runnable_value = rb_intern("runnable_value");
@@ -7,7 +7,7 @@
7
7
 
8
8
  // debugging
9
9
  #define OBJ_ID(obj) (NUM2LONG(rb_funcall(obj, rb_intern("object_id"), 0)))
10
- #define INSPECT(...) (rb_funcall(rb_cObject, rb_intern("p"), __VA_ARGS__))
10
+ #define INSPECT(obj) { VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); printf("%s\n", StringValueCStr(s));}
11
11
  #define FIBER_TRACE(...) if (__tracing_enabled__) { \
12
12
  rb_funcall(rb_cObject, ID_fiber_trace, __VA_ARGS__); \
13
13
  }
@@ -66,22 +66,22 @@ enum {
66
66
  VALUE Fiber_auto_watcher(VALUE self);
67
67
  void Fiber_make_runnable(VALUE fiber, VALUE value);
68
68
 
69
- VALUE Polyphony_switchpoint();
70
-
71
69
  VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue);
72
70
  VALUE LibevAgent_break(VALUE self);
73
71
  VALUE LibevAgent_pending_count(VALUE self);
74
72
  VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write);
75
73
 
74
+ VALUE LibevAgent_ref(VALUE self);
75
+ VALUE LibevAgent_unref(VALUE self);
76
+ int LibevAgent_ref_count(VALUE self);
77
+ void LibevAgent_reset_ref_count(VALUE self);
78
+
76
79
  VALUE Polyphony_snooze(VALUE self);
77
80
 
78
81
  VALUE Polyphony_Queue_push(VALUE self, VALUE value);
79
82
 
80
- VALUE Thread_post_fork(VALUE thread);
81
- VALUE Thread_ref(VALUE thread);
82
83
  VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
83
84
  VALUE Thread_switch_fiber(VALUE thread);
84
- VALUE Thread_unref(VALUE thread);
85
85
 
86
86
  int io_setstrbuf(VALUE *str, long len);
87
87
  void io_set_read_length(VALUE str, long n, int shrinkable);
@@ -4,7 +4,6 @@ void Init_Fiber();
4
4
  void Init_Polyphony();
5
5
  void Init_LibevAgent();
6
6
  void Init_LibevQueue();
7
- void Init_Socket();
8
7
  void Init_Thread();
9
8
  void Init_Tracing();
10
9
 
@@ -16,7 +15,6 @@ void Init_polyphony_ext() {
16
15
  Init_LibevQueue();
17
16
 
18
17
  Init_Fiber();
19
- Init_Socket();
20
18
  Init_Thread();
21
19
 
22
20
  Init_Tracing();
@@ -1,15 +1,11 @@
1
1
  #include "polyphony.h"
2
2
 
3
3
  ID ID_deactivate_all_watchers_post_fork;
4
- ID ID_empty;
5
- ID ID_fiber_ref_count;
6
4
  ID ID_ivar_agent;
7
5
  ID ID_ivar_join_wait_queue;
8
6
  ID ID_ivar_main_fiber;
9
7
  ID ID_ivar_result;
10
8
  ID ID_ivar_terminated;
11
- ID ID_pop;
12
- ID ID_push;
13
9
  ID ID_run_queue;
14
10
  ID ID_runnable_next;
15
11
  ID ID_stop;
@@ -18,34 +14,20 @@ static VALUE Thread_setup_fiber_scheduling(VALUE self) {
18
14
  VALUE queue;
19
15
 
20
16
  rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
21
- rb_ivar_set(self, ID_fiber_ref_count, INT2NUM(0));
22
17
  queue = rb_ary_new();
23
18
  rb_ivar_set(self, ID_run_queue, queue);
24
19
 
25
20
  return self;
26
21
  }
27
22
 
28
- VALUE Thread_ref(VALUE self) {
29
- VALUE count = rb_ivar_get(self, ID_fiber_ref_count);
30
- int new_count = NUM2INT(count) + 1;
31
- rb_ivar_set(self, ID_fiber_ref_count, INT2NUM(new_count));
32
- return self;
33
- }
34
-
35
- VALUE Thread_unref(VALUE self) {
36
- VALUE count = rb_ivar_get(self, ID_fiber_ref_count);
37
- int new_count = NUM2INT(count) - 1;
38
- rb_ivar_set(self, ID_fiber_ref_count, INT2NUM(new_count));
39
- return self;
40
- }
41
-
42
23
  int Thread_fiber_ref_count(VALUE self) {
43
- VALUE count = rb_ivar_get(self, ID_fiber_ref_count);
44
- return NUM2INT(count);
24
+ VALUE agent = rb_ivar_get(self, ID_ivar_agent);
25
+ return NUM2INT(LibevAgent_ref_count(agent));
45
26
  }
46
27
 
47
- void Thread_fiber_reset_ref_count(VALUE self) {
48
- rb_ivar_set(self, ID_fiber_ref_count, INT2NUM(0));
28
+ inline void Thread_fiber_reset_ref_count(VALUE self) {
29
+ VALUE agent = rb_ivar_get(self, ID_ivar_agent);
30
+ LibevAgent_reset_ref_count(agent);
49
31
  }
50
32
 
51
33
  static VALUE SYM_scheduled_fibers;
@@ -140,8 +122,8 @@ VALUE Thread_switch_fiber(VALUE self) {
140
122
  }
141
123
  }
142
124
 
125
+ ref_count = LibevAgent_ref_count(agent);
143
126
  while (1) {
144
- ref_count = Thread_fiber_ref_count(self);
145
127
  next_fiber = rb_ary_shift(queue);
146
128
  if (next_fiber != Qnil) {
147
129
  if (ref_count > 0) {
@@ -165,7 +147,8 @@ VALUE Thread_switch_fiber(VALUE self) {
165
147
  rb_ivar_set(next_fiber, ID_runnable, Qnil);
166
148
  RB_GC_GUARD(next_fiber);
167
149
  RB_GC_GUARD(value);
168
- return rb_funcall(next_fiber, ID_transfer, 1, value);
150
+ return (next_fiber == current_fiber) ?
151
+ value : rb_funcall(next_fiber, ID_transfer, 1, value);
169
152
  }
170
153
 
171
154
  VALUE Thread_reset_fiber_scheduling(VALUE self) {
@@ -175,16 +158,6 @@ VALUE Thread_reset_fiber_scheduling(VALUE self) {
175
158
  return self;
176
159
  }
177
160
 
178
- VALUE Polyphony_switchpoint() {
179
- VALUE ret;
180
- VALUE thread = rb_thread_current();
181
- Thread_ref(thread);
182
- ret = Thread_switch_fiber(thread);
183
- Thread_unref(thread);
184
- RB_GC_GUARD(ret);
185
- return ret;
186
- }
187
-
188
161
  VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_obj) {
189
162
  VALUE agent = rb_ivar_get(self, ID_ivar_agent);
190
163
  if (fiber != Qnil) {
@@ -200,9 +173,6 @@ VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_ob
200
173
  }
201
174
 
202
175
  void Init_Thread() {
203
- rb_define_method(rb_cThread, "fiber_ref", Thread_ref, 0);
204
- rb_define_method(rb_cThread, "fiber_unref", Thread_unref, 0);
205
-
206
176
  rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
207
177
  rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
208
178
  rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
@@ -214,15 +184,11 @@ void Init_Thread() {
214
184
  rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
215
185
 
216
186
  ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
217
- ID_empty = rb_intern("empty?");
218
- ID_fiber_ref_count = rb_intern("fiber_ref_count");
219
187
  ID_ivar_agent = rb_intern("@agent");
220
188
  ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
221
189
  ID_ivar_main_fiber = rb_intern("@main_fiber");
222
190
  ID_ivar_result = rb_intern("@result");
223
191
  ID_ivar_terminated = rb_intern("@terminated");
224
- ID_pop = rb_intern("pop");
225
- ID_push = rb_intern("push");
226
192
  ID_run_queue = rb_intern("run_queue");
227
193
  ID_runnable_next = rb_intern("runnable_next");
228
194
  ID_stop = rb_intern("stop");
@@ -6,6 +6,12 @@ require_relative './polyphony_ext'
6
6
  module Polyphony
7
7
  # Map Queue to Libev queue implementation
8
8
  Queue = LibevQueue
9
+
10
+ # replace core Queue class with our own
11
+ verbose = $VERBOSE
12
+ $VERBOSE = nil
13
+ Object.const_set(:Queue, Polyphony::Queue)
14
+ $VERBOSE = verbose
9
15
  end
10
16
 
11
17
  require_relative './polyphony/extensions/core'
@@ -26,14 +32,17 @@ require_relative './polyphony/event'
26
32
  module Polyphony
27
33
  class << self
28
34
  def wait_for_signal(sig)
35
+ raise "should be reimplemented"
36
+
29
37
  fiber = Fiber.current
30
- Polyphony.ref
38
+ # Polyphony.ref
31
39
  old_trap = trap(sig) do
32
- Polyphony.unref
40
+ # Polyphony.unref
33
41
  fiber.schedule(sig)
34
42
  trap(sig, old_trap)
35
43
  end
36
44
  suspend
45
+
37
46
  end
38
47
 
39
48
  def fork(&block)
@@ -111,7 +120,25 @@ module Polyphony
111
120
  threads.each(&:kill)
112
121
  threads.each(&:join)
113
122
  end
123
+
124
+ attr_accessor :original_pid
125
+
126
+ def install_at_exit_handler
127
+ @original_pid = ::Process.pid
128
+
129
+ # This at_exit handler is needed only when the original process exits. Due to
130
+ # the behaviour of fibers on fork (and especially on exit from forked
131
+ # processes,) we use a separate mechanism to terminate fibers in forked
132
+ # processes (see Polyphony.fork).
133
+ at_exit do
134
+ next unless @original_pid == ::Process.pid
135
+
136
+ Polyphony.terminate_threads
137
+ Fiber.current.shutdown_all_children
138
+ end
139
+ end
114
140
  end
115
141
  end
116
142
 
117
143
  Polyphony.install_terminating_signal_handlers
144
+ Polyphony.install_at_exit_handler
@@ -6,7 +6,7 @@ require 'redis'
6
6
  require 'hiredis/reader'
7
7
 
8
8
  # Polyphony-based Redis driver
9
- class Driver
9
+ class Polyphony::RedisDriver
10
10
  def self.connect(config)
11
11
  raise 'unix sockets not supported' if config[:scheme] == 'unix'
12
12
 
@@ -43,6 +43,7 @@ class Driver
43
43
  end
44
44
 
45
45
  def format_command(args)
46
+ args = args.flatten
46
47
  (+"*#{args.size}\r\n").tap do |s|
47
48
  args.each do |a|
48
49
  a = a.to_s
@@ -63,4 +64,4 @@ class Driver
63
64
  end
64
65
  end
65
66
 
66
- Redis::Connection.drivers << Driver
67
+ Redis::Connection.drivers << Polyphony::RedisDriver
@@ -26,7 +26,7 @@ module Polyphony
26
26
  end
27
27
 
28
28
  def receive
29
- Polyphony.ref
29
+ Thread.current.agent.ref
30
30
  if @payload_queue.empty?
31
31
  @waiting_queue << Fiber.current
32
32
  suspend
@@ -34,7 +34,7 @@ module Polyphony
34
34
  receive_from_queue
35
35
  end
36
36
  ensure
37
- Polyphony.unref
37
+ Thread.current.agent.unref
38
38
  end
39
39
 
40
40
  def receive_from_queue
@@ -15,11 +15,13 @@ module Polyphony
15
15
  end
16
16
  end
17
17
 
18
- def cancel_after(interval, &block)
18
+ def cancel_after(interval, with_exception: Polyphony::Cancel, &block)
19
19
  fiber = ::Fiber.current
20
20
  canceller = spin do
21
21
  sleep interval
22
- fiber.schedule Polyphony::Cancel.new
22
+ exception = with_exception.is_a?(Class) ?
23
+ with_exception.new : RuntimeError.new(with_exception)
24
+ fiber.schedule exception
23
25
  end
24
26
  block ? cancel_after_wrap_block(canceller, &block) : canceller
25
27
  end
@@ -100,10 +102,10 @@ module Polyphony
100
102
  end
101
103
 
102
104
  def sleep_forever
103
- Thread.current.fiber_ref
105
+ Thread.current.agent.ref
104
106
  suspend
105
107
  ensure
106
- Thread.current.fiber_unref
108
+ Thread.current.agent.unref
107
109
  end
108
110
 
109
111
  def throttled_loop(rate, count: nil, &block)
@@ -13,6 +13,7 @@ module Polyphony
13
13
 
14
14
  @stock = []
15
15
  @queue = []
16
+ @acquired_resources = {}
16
17
 
17
18
  @limit = opts[:limit] || 4
18
19
  @size = 0
@@ -23,16 +24,25 @@ module Polyphony
23
24
  end
24
25
 
25
26
  def acquire
26
- Polyphony.ref
27
- resource = wait_for_resource
28
- return unless resource
29
-
30
- yield resource
31
- ensure
32
- Polyphony.unref
33
- release(resource) if resource
27
+ fiber = Fiber.current
28
+ if @acquired_resources[fiber]
29
+ yield @acquired_resources[fiber]
30
+ else
31
+ begin
32
+ Thread.current.agent.ref
33
+ resource = wait_for_resource
34
+ return unless resource
35
+
36
+ @acquired_resources[fiber] = resource
37
+ yield resource
38
+ ensure
39
+ @acquired_resources[fiber] = nil
40
+ Thread.current.agent.unref
41
+ release(resource) if resource
42
+ end
43
+ end
34
44
  end
35
-
45
+
36
46
  def wait_for_resource
37
47
  fiber = Fiber.current
38
48
  @queue << fiber
@@ -8,8 +8,6 @@ require_relative '../core/exceptions'
8
8
 
9
9
  # Exeption overrides
10
10
  class ::Exception
11
- EXIT_EXCEPTION_CLASSES = [::Interrupt, ::SystemExit].freeze
12
-
13
11
  class << self
14
12
  attr_accessor :__disable_sanitized_backtrace__
15
13
  end
@@ -24,7 +22,7 @@ class ::Exception
24
22
 
25
23
  alias_method :orig_backtrace, :backtrace
26
24
  def backtrace
27
- unless @first_backtrace_call || EXIT_EXCEPTION_CLASSES.include?(self.class)
25
+ unless @first_backtrace_call
28
26
  @first_backtrace_call = true
29
27
  return orig_backtrace
30
28
  end
@@ -59,6 +57,12 @@ module ::Process
59
57
  fiber.define_singleton_method(:pid) { pid }
60
58
  fiber
61
59
  end
60
+
61
+ alias_method :orig_daemon, :daemon
62
+ def daemon(*args)
63
+ orig_daemon(*args)
64
+ Polyphony.original_pid = Process.pid
65
+ end
62
66
  end
63
67
  end
64
68
 
@@ -93,6 +97,7 @@ module ::Kernel
93
97
  def gets(*_args)
94
98
  if !ARGV.empty? || @gets_fiber
95
99
  @gets_fiber ||= Fiber.new(&ARGV_GETS_LOOP)
100
+ @gets_fiber.thread = Thread.current
96
101
  result = @gets_fiber.alive? && @gets_fiber.safe_transfer(Fiber.current)
97
102
  return result if result
98
103