Skip to content

Commit f5ddf1e

Browse files
authored
Merge pull request #3674 from rubinius/fibers
WIP Rewrite Fibers to use pthreads
2 parents 2f04021 + b429232 commit f5ddf1e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1169
-1777
lines changed

configure

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ class Configure
7272
@libc = nil
7373
@x86_32 = false
7474
@x86_64 = false
75-
@fibers = false
7675
@dtrace = false
7776
@dtrace_const = false
7877
@have_lchmod = false
@@ -458,7 +457,6 @@ class Configure
458457
feature "vendor-zlib", false
459458
feature "vendor-libsodium", true
460459
feature "alloc-tracking", false
461-
feature "fibers", true
462460
feature "dtrace", false
463461
feature "rpath", true
464462

@@ -1198,10 +1196,6 @@ int main() { return tgetnum(""); }
11981196
@defines << "RBX_ALLOC_TRACKING"
11991197
end
12001198

1201-
if @features["fibers"].value
1202-
@fibers = true if @x86_32 or @x86_64
1203-
end
1204-
12051199
if @features["dtrace"].value and has_dtrace
12061200
@defines << "HAVE_DTRACE"
12071201
end
@@ -1583,7 +1577,6 @@ int main() { return tgetnum(""); }
15831577
:x86_64 => @x86_64,
15841578
:dtrace => @dtrace,
15851579
:dtrace_const => @dtrace_const,
1586-
:fibers => @fibers,
15871580
:debug_build => @debug_build,
15881581
:sourcedir => @sourcedir,
15891582
:stagingdir => @stagingdir,
@@ -1690,10 +1683,6 @@ int main() { return tgetnum(""); }
16901683
end
16911684
end
16921685

1693-
if @fibers
1694-
f.puts "#define RBX_FIBER_ENABLED 1"
1695-
end
1696-
16971686
f.puts "#define RBX_DTRACE_CONST #{@dtrace_const ? "const" : ""}"
16981687

16991688
write_have_defines f

core/enumerator.rb

Lines changed: 13 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -98,19 +98,7 @@ def each_with_index
9898
def next
9999
return @lookahead.shift unless @lookahead.empty?
100100

101-
unless @generator
102-
# Allow #to_generator to return nil, indicating it has none for
103-
# this method.
104-
if @object.respond_to? :to_generator
105-
@generator = @object.to_generator(@iter)
106-
end
107-
108-
if !@generator and gen = FiberGenerator
109-
@generator = gen.new(self)
110-
else
111-
@generator = ThreadGenerator.new(self, @object, @iter, @args)
112-
end
113-
end
101+
@generator ||= Iterator.new self
114102

115103
begin
116104
return @generator.next if @generator.next?
@@ -198,6 +186,7 @@ def <<(*args)
198186

199187
class Generator
200188
include Enumerable
189+
201190
def initialize(&block)
202191
raise LocalJumpError, "Expected a block to be given" unless block_given?
203192

@@ -447,123 +436,41 @@ def zip(*lists)
447436
end
448437
end
449438

450-
if Rubinius::Fiber::ENABLED
451-
class FiberGenerator
452-
STACK_SIZE = 1_048_576
453-
454-
attr_reader :result
439+
class Iterator
440+
STACK_SIZE = 1_048_576
455441

456-
def initialize(obj)
457-
@object = obj
458-
rewind
459-
end
460-
461-
def next?
462-
!@done
463-
end
464-
465-
def next
466-
reset unless @fiber
467-
468-
val = @fiber.resume
469-
470-
raise StopIteration, "iteration has ended" if @done
471-
472-
return val
473-
end
474-
475-
def rewind
476-
@fiber = nil
477-
@done = false
478-
end
479-
480-
def reset
481-
@done = false
482-
@fiber = Rubinius::Fiber.new stack_size: STACK_SIZE do
483-
obj = @object
484-
@result = obj.each do |*val|
485-
Rubinius::Fiber.yield *val
486-
end
487-
@done = true
488-
end
489-
end
490-
end
491-
else
492-
FiberGenerator = nil
493-
end
494-
495-
class ThreadGenerator
496442
attr_reader :result
497443

498-
def initialize(enum, obj, meth, args)
444+
def initialize(obj)
499445
@object = obj
500-
@method = meth
501-
@args = args
502-
503-
ObjectSpace.define_finalizer(enum, method(:kill))
504-
505446
rewind
506447
end
507448

508-
# Used to cleanup the background thread when the enumerator
509-
# is GC'd.
510-
def kill(obj_id)
511-
if @thread
512-
@thread.kill
513-
end
514-
end
515-
516449
def next?
517-
if @done
518-
@thread.join if @thread
519-
@thread = nil
520-
return false
521-
end
522-
523-
true
450+
!@done
524451
end
525452

526453
def next
527-
reset unless @thread
454+
reset unless @fiber
528455

529-
@hold_channel << nil
530-
vals = @channel.receive
456+
val = @fiber.resume
531457

532458
raise StopIteration, "iteration has ended" if @done
533459

534-
# return *[1] == [1], unfortunately.
535-
return vals.size == 1 ? vals.first : vals
460+
return val
536461
end
537462

538463
def rewind
539-
if @thread
540-
@thread.kill
541-
end
542-
464+
@fiber = nil
543465
@done = false
544-
@thread = nil
545466
end
546467

547468
def reset
548469
@done = false
549-
@channel = Rubinius::Channel.new
550-
@hold_channel = Rubinius::Channel.new
551-
552-
@thread = Thread.new do
553-
@result = @object.__send__(@method, *@args) do |*vals|
554-
@hold_channel.receive
555-
@channel << vals
556-
end
557-
558-
# Hold to indicate done to avoid race conditions setting
559-
# the ivar.
560-
@hold_channel.receive
470+
@fiber = Fiber.new stack_size: STACK_SIZE do
471+
obj = @object
472+
@result = obj.each { |*val| Fiber.yield *val }
561473
@done = true
562-
563-
# Put an extra value in the channel, so that main
564-
# thread doesn't accidentally block if it doesn't
565-
# detect @done in time.
566-
@channel << nil
567474
end
568475
end
569476
end

core/fiber.rb

Lines changed: 52 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,63 @@
1-
module Rubinius
2-
class Fiber
3-
attr_reader :stack_size
4-
attr_reader :thread_name
5-
attr_reader :fiber_id
6-
attr_reader :source
1+
class Fiber
2+
attr_reader :stack_size
3+
attr_reader :thread_name
4+
attr_reader :fiber_id
5+
attr_reader :source
6+
7+
def self.new(**kw, &block)
8+
if block.nil?
9+
raise ArgumentError, "Fiber.new requires a block"
10+
end
711

8-
def self.new(**kw, &block)
9-
if block.nil?
10-
raise ArgumentError, "Fiber.new requires a block"
11-
end
12+
stack_size = Rubinius::Type.try_convert kw[:stack_size], Fixnum, :to_int
1213

13-
stack_size = Rubinius::Type.try_convert kw[:stack_size], Fixnum, :to_int
14+
Rubinius.invoke_primitive :fiber_new, stack_size, block, self
15+
end
1416

15-
Rubinius.invoke_primitive :fiber_new, stack_size, block, self
16-
end
17+
def self.current
18+
Rubinius.primitive :fiber_s_current
19+
raise PrimitiveFailure, "Fiber.current primitive failed"
20+
end
1721

18-
def self.current
19-
Rubinius.primitive :fiber_s_current
20-
raise PrimitiveFailure, "Rubinius::Fiber.current failed"
21-
end
22+
def self.yield(*args)
23+
Rubinius.primitive :fiber_s_yield
24+
raise PrimitiveFailure, "Fiber.yield primitive failed"
25+
end
2226

23-
def self.yield(*args)
24-
Rubinius.primitive :fiber_s_yield
25-
raise PrimitiveFailure, "Rubinius::Fiber.yield failed"
26-
end
27+
def self.list
28+
Rubinius.primitive :fiber_s_list
29+
raise PrimitiveFailure, "Fiber.list primitive failed"
30+
end
2731

28-
def resume(*args)
29-
Rubinius.primitive :fiber_resume
30-
raise PrimitiveFailure, "Rubinius::Fiber#resume failed"
31-
end
32+
def self.main
33+
Rubinius.primitive :fiber_s_main
34+
raise PrimitiveFailure, "Fiber.main primitive failed"
35+
end
3236

33-
def transfer(*args)
34-
Rubinius.primitive :fiber_transfer
35-
raise PrimitiveFailure, "Rubinius::Fiber#transfer failed"
36-
end
37+
def status
38+
Rubinius.primitive :fiber_status
39+
raise PrimitiveFailure, "Fiber#status primitive failed"
40+
end
3741

38-
def alive?
39-
!@dead
40-
end
42+
def resume(*args)
43+
Rubinius.primitive :fiber_resume
44+
raise PrimitiveFailure, "Fiber#resume primitive failed"
45+
end
4146

42-
def inspect
43-
str = "#<#{self.class}:0x#{object_id.to_s(16)} thread_name=#{@thread_name} fiber_id=#{@fiber_id} status=#{alive? ? "alive" : "dead"}"
44-
str << " source=#{@source}" if @source
45-
str << ">"
46-
end
47+
def transfer(*args)
48+
Rubinius.primitive :fiber_transfer
49+
raise PrimitiveFailure, "Fiber#transfer primitive failed"
4750
end
51+
52+
def alive?
53+
status != "dead"
54+
end
55+
56+
def inspect
57+
str = "#<#{self.class}:0x#{object_id.to_s(16)} thread_name=#{@thread_name} fiber_id=#{@fiber_id} status=#{status}"
58+
str << " source=#{@source}" if @source
59+
str << ">"
60+
end
61+
62+
alias_method :to_s, :inspect
4863
end

core/thread.rb

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ def self.stop
8484
nil
8585
end
8686

87+
def fiber_list
88+
Rubinius.primitive :thread_fiber_list
89+
Kernel.raise PrimitiveFailure, "Thread.fiber_list primitive failed"
90+
end
91+
8792
def wakeup
8893
Rubinius.primitive :thread_wakeup
8994
Kernel.raise ThreadError, "Thread#wakeup primitive failed, thread may be dead"
@@ -224,40 +229,45 @@ def raise(exc=undefined, msg=nil, trace=nil)
224229
end
225230
end
226231

227-
def [](key)
228-
locals_aref(Rubinius::Type.coerce_to_symbol(key))
232+
def thread_variable_get(key)
233+
key = Rubinius::Type.coerce_to_symbol key
234+
Rubinius.invoke_primitive :thread_variable_get, self, key
229235
end
230236

231-
def locals_aref(key)
232-
Rubinius.primitive :thread_locals_aref
233-
raise PrimitiveFailure, "Thread#locals_aref primitive failed"
237+
def thread_variable_set(key, value)
238+
key = Rubinius::Type.coerce_to_symbol key
239+
Rubinius.invoke_primitive :thread_variable_set, self, key, value
234240
end
235-
private :locals_aref
236241

237-
def []=(key, value)
238-
locals_store(Rubinius::Type.coerce_to_symbol(key), value)
242+
def thread_variable?(key)
243+
key = Rubinius::Type.coerce_to_symbol key
244+
Rubinius.invoke_primitive :thread_variable_key_p, self, key
239245
end
240246

241-
def locals_store(key, value)
242-
Rubinius.primitive :thread_locals_store
243-
raise PrimitiveFailure, "Thread#locals_store primitive failed"
247+
def thread_variables
248+
Rubinius.primitive :thread_variables
249+
raise PrimitiveFailure, "Thread#thread_variables primitive failed"
244250
end
245-
private :locals_store
246251

247-
def keys
248-
Rubinius.primitive :thread_locals_keys
249-
raise PrimitiveFailure, "Thread#keys primitive failed"
252+
def [](key)
253+
key = Rubinius::Type.coerce_to_symbol key
254+
Rubinius.invoke_primitive :thread_fiber_variable_get, self, key
255+
end
256+
257+
def []=(key, value)
258+
key = Rubinius::Type.coerce_to_symbol key
259+
Rubinius.invoke_primitive :thread_fiber_variable_set, self, key, value
250260
end
251261

252262
def key?(key)
253-
locals_key?(Rubinius::Type.coerce_to_symbol(key))
263+
key = Rubinius::Type.coerce_to_symbol key
264+
Rubinius.invoke_primitive :thread_fiber_variable_key_p, self, key
254265
end
255266

256-
def locals_key?(key)
257-
Rubinius.primitive :thread_locals_has_key
258-
raise PrimitiveFailure, "Thread#locals_key? primitive failed"
267+
def keys
268+
Rubinius.primitive :thread_fiber_variables
269+
raise PrimitiveFailure, "Thread#keys primitive failed"
259270
end
260-
private :locals_key?
261271

262272
# Register another Thread object +thr+ as the Thread where the debugger
263273
# is running. When the current thread hits a breakpoint, it uses this

core/zed.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,8 +1062,6 @@ module Errno
10621062
FFI = Rubinius::FFI
10631063
end
10641064

1065-
Fiber = Rubinius::Fiber
1066-
10671065
class File < IO
10681066
# these will be necessary when we run on Windows
10691067
DOSISH = false # !!(RUBY_PLATFORM =~ /mswin/)

0 commit comments

Comments
 (0)