2015-12-16 00:07:31 -05:00
|
|
|
# frozen_string_literal: false
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
require 'test/unit'
|
2007-08-24 22:03:44 -04:00
|
|
|
require 'fiber'
|
2014-11-28 19:36:33 -05:00
|
|
|
EnvUtil.suppress_warning {require 'continuation'}
|
2013-12-14 15:46:44 -05:00
|
|
|
require 'tmpdir'
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
|
|
|
|
class TestFiber < Test::Unit::TestCase
|
|
|
|
def test_normal
|
|
|
|
assert_equal(:ok2,
|
2007-06-02 03:48:29 -04:00
|
|
|
Fiber.new{|e|
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
assert_equal(:ok1, e)
|
2007-06-02 03:48:29 -04:00
|
|
|
Fiber.yield :ok2
|
2007-08-21 14:51:39 -04:00
|
|
|
}.resume(:ok1)
|
2007-06-02 03:48:29 -04:00
|
|
|
)
|
2007-08-21 14:51:39 -04:00
|
|
|
assert_equal([:a, :b], Fiber.new{|a, b| [a, b]}.resume(:a, :b))
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
end
|
|
|
|
|
2008-11-30 22:00:48 -05:00
|
|
|
def test_argument
|
|
|
|
assert_equal(4, Fiber.new {|i=4| i}.resume)
|
|
|
|
end
|
|
|
|
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
def test_term
|
2007-08-21 14:51:39 -04:00
|
|
|
assert_equal(:ok, Fiber.new{:ok}.resume)
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
assert_equal([:a, :b, :c, :d, :e],
|
|
|
|
Fiber.new{
|
|
|
|
Fiber.new{
|
|
|
|
Fiber.new{
|
|
|
|
Fiber.new{
|
|
|
|
[:a]
|
2007-08-21 14:51:39 -04:00
|
|
|
}.resume + [:b]
|
|
|
|
}.resume + [:c]
|
|
|
|
}.resume + [:d]
|
|
|
|
}.resume + [:e])
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_many_fibers
|
2021-01-14 01:36:45 -05:00
|
|
|
skip 'This is unstable on GitHub Actions --jit-wait. TODO: debug it' if defined?(RubyVM::JIT) && RubyVM::JIT.enabled?
|
2021-06-25 18:17:26 -04:00
|
|
|
max = 1000
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
assert_equal(max, max.times{
|
|
|
|
Fiber.new{}
|
|
|
|
})
|
2013-08-19 04:44:50 -04:00
|
|
|
GC.start # force collect created fibers
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
assert_equal(max,
|
|
|
|
max.times{|i|
|
|
|
|
Fiber.new{
|
2007-08-21 14:51:39 -04:00
|
|
|
}.resume
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
}
|
|
|
|
)
|
2013-08-19 04:44:50 -04:00
|
|
|
GC.start # force collect created fibers
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
end
|
|
|
|
|
2007-06-06 14:19:42 -04:00
|
|
|
def test_many_fibers_with_threads
|
2021-03-22 23:20:44 -04:00
|
|
|
assert_normal_exit <<-SRC, timeout: (/solaris/i =~ RUBY_PLATFORM ? 300 : 60)
|
2012-10-29 04:52:59 -04:00
|
|
|
max = 1000
|
|
|
|
@cnt = 0
|
|
|
|
(1..100).map{|ti|
|
|
|
|
Thread.new{
|
|
|
|
max.times{|i|
|
|
|
|
Fiber.new{
|
|
|
|
@cnt += 1
|
|
|
|
}.resume
|
|
|
|
}
|
2007-06-06 14:19:42 -04:00
|
|
|
}
|
2012-10-29 04:52:59 -04:00
|
|
|
}.each{|t|
|
|
|
|
t.join
|
2007-06-06 14:19:42 -04:00
|
|
|
}
|
2013-06-20 00:08:49 -04:00
|
|
|
SRC
|
2007-06-06 14:19:42 -04:00
|
|
|
end
|
|
|
|
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
def test_error
|
|
|
|
assert_raise(ArgumentError){
|
|
|
|
Fiber.new # Fiber without block
|
|
|
|
}
|
2017-12-12 13:44:49 -05:00
|
|
|
f = Fiber.new{}
|
|
|
|
Thread.new{
|
|
|
|
assert_raise(FiberError){ # Fiber yielding across thread
|
|
|
|
f.resume
|
|
|
|
}
|
|
|
|
}.join
|
2007-06-02 03:48:29 -04:00
|
|
|
assert_raise(FiberError){
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
f = Fiber.new{}
|
2007-08-21 14:51:39 -04:00
|
|
|
f.resume
|
|
|
|
f.resume
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
}
|
2007-06-14 23:20:13 -04:00
|
|
|
assert_raise(RuntimeError){
|
2013-01-16 00:30:20 -05:00
|
|
|
Fiber.new{
|
2007-06-14 23:20:13 -04:00
|
|
|
@c = callcc{|c| @c = c}
|
2007-08-21 14:51:39 -04:00
|
|
|
}.resume
|
2007-06-14 23:20:13 -04:00
|
|
|
@c.call # cross fiber callcc
|
|
|
|
}
|
2007-08-21 14:51:39 -04:00
|
|
|
assert_raise(RuntimeError){
|
|
|
|
Fiber.new{
|
|
|
|
raise
|
|
|
|
}.resume
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
}
|
2007-08-21 14:51:39 -04:00
|
|
|
assert_raise(FiberError){
|
|
|
|
Fiber.yield
|
|
|
|
}
|
|
|
|
assert_raise(FiberError){
|
|
|
|
fib = Fiber.new{
|
|
|
|
fib.resume
|
|
|
|
}
|
|
|
|
fib.resume
|
|
|
|
}
|
|
|
|
assert_raise(FiberError){
|
|
|
|
fib = Fiber.new{
|
|
|
|
Fiber.new{
|
|
|
|
fib.resume
|
|
|
|
}.resume
|
|
|
|
}
|
|
|
|
fib.resume
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
}
|
2018-12-28 08:03:09 -05:00
|
|
|
assert_raise(FiberError){
|
|
|
|
fib = Fiber.new{}
|
|
|
|
fib.raise "raise in unborn fiber"
|
|
|
|
}
|
|
|
|
assert_raise(FiberError){
|
|
|
|
fib = Fiber.new{}
|
|
|
|
fib.resume
|
|
|
|
fib.raise "raise in dead fiber"
|
|
|
|
}
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
end
|
2007-06-05 13:55:07 -04:00
|
|
|
|
|
|
|
def test_return
|
|
|
|
assert_raise(LocalJumpError){
|
|
|
|
Fiber.new do
|
|
|
|
return
|
2007-08-21 14:51:39 -04:00
|
|
|
end.resume
|
2007-06-05 13:55:07 -04:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_throw
|
2014-11-15 02:28:08 -05:00
|
|
|
assert_raise(UncaughtThrowError){
|
2007-06-05 13:55:07 -04:00
|
|
|
Fiber.new do
|
|
|
|
throw :a
|
2007-08-21 14:51:39 -04:00
|
|
|
end.resume
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2018-12-28 08:03:09 -05:00
|
|
|
def test_raise
|
|
|
|
assert_raise(ZeroDivisionError){
|
|
|
|
Fiber.new do
|
|
|
|
1/0
|
|
|
|
end.resume
|
|
|
|
}
|
|
|
|
assert_raise(RuntimeError){
|
|
|
|
fib = Fiber.new{ Fiber.yield }
|
2018-12-28 08:03:14 -05:00
|
|
|
fib.resume
|
2018-12-28 08:03:09 -05:00
|
|
|
fib.raise "raise and propagate"
|
|
|
|
}
|
|
|
|
assert_nothing_raised{
|
|
|
|
fib = Fiber.new do
|
|
|
|
begin
|
|
|
|
Fiber.yield
|
|
|
|
rescue
|
|
|
|
end
|
|
|
|
end
|
|
|
|
fib.resume
|
|
|
|
fib.raise "rescue in fiber"
|
|
|
|
}
|
|
|
|
fib = Fiber.new do
|
|
|
|
begin
|
|
|
|
Fiber.yield
|
|
|
|
rescue
|
|
|
|
Fiber.yield :ok
|
|
|
|
end
|
|
|
|
end
|
|
|
|
fib.resume
|
|
|
|
assert_equal(:ok, fib.raise)
|
|
|
|
end
|
|
|
|
|
2020-11-17 19:23:51 -05:00
|
|
|
def test_raise_transferring_fiber
|
|
|
|
root = Fiber.current
|
|
|
|
fib = Fiber.new { root.transfer }
|
|
|
|
fib.transfer
|
|
|
|
assert_raise(RuntimeError){
|
|
|
|
fib.raise "can raise with transfer: true"
|
|
|
|
}
|
|
|
|
assert_not_predicate(fib, :alive?)
|
|
|
|
end
|
|
|
|
|
2007-08-21 14:51:39 -04:00
|
|
|
def test_transfer
|
|
|
|
ary = []
|
|
|
|
f2 = nil
|
2007-08-24 22:03:44 -04:00
|
|
|
f1 = Fiber.new{
|
2007-08-21 14:51:39 -04:00
|
|
|
ary << f2.transfer(:foo)
|
|
|
|
:ok
|
|
|
|
}
|
2007-08-24 22:03:44 -04:00
|
|
|
f2 = Fiber.new{
|
2007-08-21 14:51:39 -04:00
|
|
|
ary << f1.transfer(:baz)
|
|
|
|
:ng
|
2007-06-05 13:55:07 -04:00
|
|
|
}
|
2007-08-21 14:51:39 -04:00
|
|
|
assert_equal(:ok, f1.transfer)
|
|
|
|
assert_equal([:baz], ary)
|
2007-06-05 13:55:07 -04:00
|
|
|
end
|
2007-09-28 15:04:45 -04:00
|
|
|
|
relax Fiber#transfer's restriction
Using Fiber#transfer with Fiber#resume for a same Fiber is
limited (once Fiber#transfer is called for a fiber, the fiber
can not be resumed more). This restriction was introduced to
protect the resume/yield chain, but we realized that it is too much
to protect the chain. Instead of the current restriction, we
introduce some other protections.
(1) can not transfer to the resuming fiber.
(2) can not transfer to the yielding fiber.
(3) can not resume transferred fiber.
(4) can not yield from not-resumed fiber.
[Bug #17221]
Also at the end of a transferred fiber, it had continued on root fiber.
However, if the root fiber resumed a fiber (and that fiber can resumed
another fiber), this behavior also breaks the resume/yield chain.
So at the end of a transferred fiber, switch to the edge of resume
chain from root fiber.
For example, root fiber resumed f1 and f1 resumed f2, transferred to
f3 and f3 terminated, then continue from the fiber f2 (it was continued
from root fiber without this patch).
2020-10-07 12:15:32 -04:00
|
|
|
def test_terminate_transferred_fiber
|
|
|
|
log = []
|
|
|
|
fa1 = fa2 = fb1 = r1 = nil
|
|
|
|
|
|
|
|
fa1 = Fiber.new{
|
|
|
|
fa2 = Fiber.new{
|
|
|
|
log << :fa2_terminate
|
|
|
|
}
|
|
|
|
fa2.resume
|
|
|
|
log << :fa1_terminate
|
|
|
|
}
|
|
|
|
fb1 = Fiber.new{
|
|
|
|
fa1.transfer
|
|
|
|
log << :fb1_terminate
|
|
|
|
}
|
|
|
|
|
|
|
|
r1 = Fiber.new{
|
|
|
|
fb1.transfer
|
|
|
|
log << :r1_terminate
|
|
|
|
}
|
|
|
|
|
|
|
|
r1.resume
|
|
|
|
log << :root_terminate
|
|
|
|
|
|
|
|
assert_equal [:fa2_terminate, :fa1_terminate, :r1_terminate, :root_terminate], log
|
|
|
|
end
|
|
|
|
|
2007-09-28 15:04:45 -04:00
|
|
|
def test_tls
|
2009-03-05 22:56:38 -05:00
|
|
|
#
|
2007-09-28 15:04:45 -04:00
|
|
|
def tvar(var, val)
|
|
|
|
old = Thread.current[var]
|
|
|
|
begin
|
|
|
|
Thread.current[var] = val
|
|
|
|
yield
|
|
|
|
ensure
|
|
|
|
Thread.current[var] = old
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
fb = Fiber.new {
|
|
|
|
assert_equal(nil, Thread.current[:v]); tvar(:v, :x) {
|
|
|
|
assert_equal(:x, Thread.current[:v]); Fiber.yield
|
|
|
|
assert_equal(:x, Thread.current[:v]); }
|
|
|
|
assert_equal(nil, Thread.current[:v]); Fiber.yield
|
|
|
|
raise # unreachable
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_equal(nil, Thread.current[:v]); tvar(:v,1) {
|
|
|
|
assert_equal(1, Thread.current[:v]); tvar(:v,3) {
|
|
|
|
assert_equal(3, Thread.current[:v]); fb.resume
|
|
|
|
assert_equal(3, Thread.current[:v]); }
|
|
|
|
assert_equal(1, Thread.current[:v]); }
|
|
|
|
assert_equal(nil, Thread.current[:v]); fb.resume
|
|
|
|
assert_equal(nil, Thread.current[:v]);
|
|
|
|
end
|
2009-02-13 01:04:25 -05:00
|
|
|
|
|
|
|
def test_alive
|
|
|
|
fib = Fiber.new{Fiber.yield}
|
|
|
|
assert_equal(true, fib.alive?)
|
|
|
|
fib.resume
|
|
|
|
assert_equal(true, fib.alive?)
|
|
|
|
fib.resume
|
|
|
|
assert_equal(false, fib.alive?)
|
|
|
|
end
|
2009-05-31 22:21:31 -04:00
|
|
|
|
|
|
|
def test_resume_self
|
2019-10-24 02:03:26 -04:00
|
|
|
f = Fiber.new {f.resume}
|
|
|
|
assert_raise(FiberError, '[ruby-core:23651]') {f.transfer}
|
2009-05-31 22:21:31 -04:00
|
|
|
end
|
2010-05-09 13:32:45 -04:00
|
|
|
|
|
|
|
def test_fiber_transfer_segv
|
|
|
|
assert_normal_exit %q{
|
|
|
|
require 'fiber'
|
|
|
|
f2 = nil
|
|
|
|
f1 = Fiber.new{ f2.resume }
|
|
|
|
f2 = Fiber.new{ f1.resume }
|
|
|
|
f1.transfer
|
2010-05-09 14:23:32 -04:00
|
|
|
}, '[ruby-dev:40833]'
|
2011-11-20 16:17:57 -05:00
|
|
|
assert_normal_exit %q{
|
|
|
|
require 'fiber'
|
|
|
|
Fiber.new{}.resume
|
2012-12-19 16:28:44 -05:00
|
|
|
1.times{Fiber.current.transfer}
|
2011-11-20 16:17:57 -05:00
|
|
|
}
|
2010-05-09 13:32:45 -04:00
|
|
|
end
|
2010-11-03 13:08:25 -04:00
|
|
|
|
|
|
|
def test_resume_root_fiber
|
2017-12-12 13:44:49 -05:00
|
|
|
Thread.new do
|
|
|
|
assert_raise(FiberError) do
|
2010-11-03 13:08:25 -04:00
|
|
|
Fiber.current.resume
|
2017-12-12 13:44:49 -05:00
|
|
|
end
|
|
|
|
end.join
|
2010-11-03 13:08:25 -04:00
|
|
|
end
|
2011-05-15 08:41:40 -04:00
|
|
|
|
|
|
|
def test_gc_root_fiber
|
|
|
|
bug4612 = '[ruby-core:35891]'
|
|
|
|
|
|
|
|
assert_normal_exit %q{
|
|
|
|
require 'fiber'
|
|
|
|
GC.stress = true
|
|
|
|
Thread.start{ Fiber.current; nil }.join
|
|
|
|
GC.start
|
|
|
|
}, bug4612
|
|
|
|
end
|
2011-09-03 11:54:44 -04:00
|
|
|
|
2017-09-08 19:51:14 -04:00
|
|
|
def test_mark_fiber
|
|
|
|
bug13875 = '[ruby-core:82681]'
|
|
|
|
|
|
|
|
assert_normal_exit %q{
|
|
|
|
GC.stress = true
|
|
|
|
up = 1.upto(10)
|
|
|
|
down = 10.downto(1)
|
|
|
|
up.zip(down) {|a, b| a + b == 11 or fail 'oops'}
|
|
|
|
}, bug13875
|
|
|
|
end
|
|
|
|
|
2011-09-03 11:54:44 -04:00
|
|
|
def test_no_valid_cfp
|
|
|
|
bug5083 = '[ruby-dev:44208]'
|
2014-03-04 08:55:24 -05:00
|
|
|
assert_equal([], Fiber.new(&Module.method(:nesting)).resume, bug5083)
|
|
|
|
assert_instance_of(Class, Fiber.new(&Class.new.method(:undef_method)).resume(:to_s), bug5083)
|
2011-09-03 11:54:44 -04:00
|
|
|
end
|
2011-11-09 05:02:22 -05:00
|
|
|
|
relax Fiber#transfer's restriction
Using Fiber#transfer with Fiber#resume for a same Fiber is
limited (once Fiber#transfer is called for a fiber, the fiber
can not be resumed more). This restriction was introduced to
protect the resume/yield chain, but we realized that it is too much
to protect the chain. Instead of the current restriction, we
introduce some other protections.
(1) can not transfer to the resuming fiber.
(2) can not transfer to the yielding fiber.
(3) can not resume transferred fiber.
(4) can not yield from not-resumed fiber.
[Bug #17221]
Also at the end of a transferred fiber, it had continued on root fiber.
However, if the root fiber resumed a fiber (and that fiber can resumed
another fiber), this behavior also breaks the resume/yield chain.
So at the end of a transferred fiber, switch to the edge of resume
chain from root fiber.
For example, root fiber resumed f1 and f1 resumed f2, transferred to
f3 and f3 terminated, then continue from the fiber f2 (it was continued
from root fiber without this patch).
2020-10-07 12:15:32 -04:00
|
|
|
def test_prohibit_transfer_to_resuming_fiber
|
|
|
|
root_fiber = Fiber.current
|
|
|
|
|
2011-11-09 05:02:22 -05:00
|
|
|
assert_raise(FiberError){
|
relax Fiber#transfer's restriction
Using Fiber#transfer with Fiber#resume for a same Fiber is
limited (once Fiber#transfer is called for a fiber, the fiber
can not be resumed more). This restriction was introduced to
protect the resume/yield chain, but we realized that it is too much
to protect the chain. Instead of the current restriction, we
introduce some other protections.
(1) can not transfer to the resuming fiber.
(2) can not transfer to the yielding fiber.
(3) can not resume transferred fiber.
(4) can not yield from not-resumed fiber.
[Bug #17221]
Also at the end of a transferred fiber, it had continued on root fiber.
However, if the root fiber resumed a fiber (and that fiber can resumed
another fiber), this behavior also breaks the resume/yield chain.
So at the end of a transferred fiber, switch to the edge of resume
chain from root fiber.
For example, root fiber resumed f1 and f1 resumed f2, transferred to
f3 and f3 terminated, then continue from the fiber f2 (it was continued
from root fiber without this patch).
2020-10-07 12:15:32 -04:00
|
|
|
fiber = Fiber.new{ root_fiber.transfer }
|
|
|
|
fiber.resume
|
|
|
|
}
|
|
|
|
|
|
|
|
fa1 = Fiber.new{
|
2020-10-13 00:40:44 -04:00
|
|
|
_fa2 = Fiber.new{ root_fiber.transfer }
|
relax Fiber#transfer's restriction
Using Fiber#transfer with Fiber#resume for a same Fiber is
limited (once Fiber#transfer is called for a fiber, the fiber
can not be resumed more). This restriction was introduced to
protect the resume/yield chain, but we realized that it is too much
to protect the chain. Instead of the current restriction, we
introduce some other protections.
(1) can not transfer to the resuming fiber.
(2) can not transfer to the yielding fiber.
(3) can not resume transferred fiber.
(4) can not yield from not-resumed fiber.
[Bug #17221]
Also at the end of a transferred fiber, it had continued on root fiber.
However, if the root fiber resumed a fiber (and that fiber can resumed
another fiber), this behavior also breaks the resume/yield chain.
So at the end of a transferred fiber, switch to the edge of resume
chain from root fiber.
For example, root fiber resumed f1 and f1 resumed f2, transferred to
f3 and f3 terminated, then continue from the fiber f2 (it was continued
from root fiber without this patch).
2020-10-07 12:15:32 -04:00
|
|
|
}
|
|
|
|
fb1 = Fiber.new{
|
2020-10-13 00:40:44 -04:00
|
|
|
_fb2 = Fiber.new{ root_fiber.transfer }
|
2011-11-09 05:02:22 -05:00
|
|
|
}
|
relax Fiber#transfer's restriction
Using Fiber#transfer with Fiber#resume for a same Fiber is
limited (once Fiber#transfer is called for a fiber, the fiber
can not be resumed more). This restriction was introduced to
protect the resume/yield chain, but we realized that it is too much
to protect the chain. Instead of the current restriction, we
introduce some other protections.
(1) can not transfer to the resuming fiber.
(2) can not transfer to the yielding fiber.
(3) can not resume transferred fiber.
(4) can not yield from not-resumed fiber.
[Bug #17221]
Also at the end of a transferred fiber, it had continued on root fiber.
However, if the root fiber resumed a fiber (and that fiber can resumed
another fiber), this behavior also breaks the resume/yield chain.
So at the end of a transferred fiber, switch to the edge of resume
chain from root fiber.
For example, root fiber resumed f1 and f1 resumed f2, transferred to
f3 and f3 terminated, then continue from the fiber f2 (it was continued
from root fiber without this patch).
2020-10-07 12:15:32 -04:00
|
|
|
fa1.transfer
|
|
|
|
fb1.transfer
|
|
|
|
|
2011-11-09 05:02:22 -05:00
|
|
|
assert_raise(FiberError){
|
relax Fiber#transfer's restriction
Using Fiber#transfer with Fiber#resume for a same Fiber is
limited (once Fiber#transfer is called for a fiber, the fiber
can not be resumed more). This restriction was introduced to
protect the resume/yield chain, but we realized that it is too much
to protect the chain. Instead of the current restriction, we
introduce some other protections.
(1) can not transfer to the resuming fiber.
(2) can not transfer to the yielding fiber.
(3) can not resume transferred fiber.
(4) can not yield from not-resumed fiber.
[Bug #17221]
Also at the end of a transferred fiber, it had continued on root fiber.
However, if the root fiber resumed a fiber (and that fiber can resumed
another fiber), this behavior also breaks the resume/yield chain.
So at the end of a transferred fiber, switch to the edge of resume
chain from root fiber.
For example, root fiber resumed f1 and f1 resumed f2, transferred to
f3 and f3 terminated, then continue from the fiber f2 (it was continued
from root fiber without this patch).
2020-10-07 12:15:32 -04:00
|
|
|
fa1.transfer
|
|
|
|
}
|
|
|
|
assert_raise(FiberError){
|
|
|
|
fb1.transfer
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_prohibit_transfer_to_yielding_fiber
|
|
|
|
f1 = f2 = f3 = nil
|
|
|
|
|
|
|
|
f1 = Fiber.new{
|
|
|
|
f2 = Fiber.new{
|
|
|
|
f3 = Fiber.new{
|
|
|
|
p f3: Fiber.yield
|
|
|
|
}
|
|
|
|
f3.resume
|
2011-11-09 05:02:22 -05:00
|
|
|
}
|
relax Fiber#transfer's restriction
Using Fiber#transfer with Fiber#resume for a same Fiber is
limited (once Fiber#transfer is called for a fiber, the fiber
can not be resumed more). This restriction was introduced to
protect the resume/yield chain, but we realized that it is too much
to protect the chain. Instead of the current restriction, we
introduce some other protections.
(1) can not transfer to the resuming fiber.
(2) can not transfer to the yielding fiber.
(3) can not resume transferred fiber.
(4) can not yield from not-resumed fiber.
[Bug #17221]
Also at the end of a transferred fiber, it had continued on root fiber.
However, if the root fiber resumed a fiber (and that fiber can resumed
another fiber), this behavior also breaks the resume/yield chain.
So at the end of a transferred fiber, switch to the edge of resume
chain from root fiber.
For example, root fiber resumed f1 and f1 resumed f2, transferred to
f3 and f3 terminated, then continue from the fiber f2 (it was continued
from root fiber without this patch).
2020-10-07 12:15:32 -04:00
|
|
|
f2.resume
|
2011-11-09 05:02:22 -05:00
|
|
|
}
|
relax Fiber#transfer's restriction
Using Fiber#transfer with Fiber#resume for a same Fiber is
limited (once Fiber#transfer is called for a fiber, the fiber
can not be resumed more). This restriction was introduced to
protect the resume/yield chain, but we realized that it is too much
to protect the chain. Instead of the current restriction, we
introduce some other protections.
(1) can not transfer to the resuming fiber.
(2) can not transfer to the yielding fiber.
(3) can not resume transferred fiber.
(4) can not yield from not-resumed fiber.
[Bug #17221]
Also at the end of a transferred fiber, it had continued on root fiber.
However, if the root fiber resumed a fiber (and that fiber can resumed
another fiber), this behavior also breaks the resume/yield chain.
So at the end of a transferred fiber, switch to the edge of resume
chain from root fiber.
For example, root fiber resumed f1 and f1 resumed f2, transferred to
f3 and f3 terminated, then continue from the fiber f2 (it was continued
from root fiber without this patch).
2020-10-07 12:15:32 -04:00
|
|
|
f1.resume
|
|
|
|
|
|
|
|
assert_raise(FiberError){ f3.transfer 10 }
|
2011-11-09 05:02:22 -05:00
|
|
|
end
|
2012-02-15 09:00:11 -05:00
|
|
|
|
relax Fiber#transfer's restriction
Using Fiber#transfer with Fiber#resume for a same Fiber is
limited (once Fiber#transfer is called for a fiber, the fiber
can not be resumed more). This restriction was introduced to
protect the resume/yield chain, but we realized that it is too much
to protect the chain. Instead of the current restriction, we
introduce some other protections.
(1) can not transfer to the resuming fiber.
(2) can not transfer to the yielding fiber.
(3) can not resume transferred fiber.
(4) can not yield from not-resumed fiber.
[Bug #17221]
Also at the end of a transferred fiber, it had continued on root fiber.
However, if the root fiber resumed a fiber (and that fiber can resumed
another fiber), this behavior also breaks the resume/yield chain.
So at the end of a transferred fiber, switch to the edge of resume
chain from root fiber.
For example, root fiber resumed f1 and f1 resumed f2, transferred to
f3 and f3 terminated, then continue from the fiber f2 (it was continued
from root fiber without this patch).
2020-10-07 12:15:32 -04:00
|
|
|
def test_prohibit_resume_to_transferring_fiber
|
|
|
|
root_fiber = Fiber.current
|
|
|
|
|
|
|
|
assert_raise(FiberError){
|
|
|
|
Fiber.new{
|
|
|
|
root_fiber.resume
|
|
|
|
}.transfer
|
|
|
|
}
|
|
|
|
|
|
|
|
f1 = f2 = nil
|
|
|
|
f1 = Fiber.new do
|
|
|
|
f2.transfer
|
|
|
|
end
|
|
|
|
f2 = Fiber.new do
|
|
|
|
f1.resume # attempt to resume transferring fiber
|
|
|
|
end
|
|
|
|
|
|
|
|
assert_raise(FiberError){
|
|
|
|
f1.transfer
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2012-02-15 09:00:11 -05:00
|
|
|
def test_fork_from_fiber
|
2018-09-12 16:49:24 -04:00
|
|
|
skip 'fork not supported' unless Process.respond_to?(:fork)
|
|
|
|
pid = nil
|
2012-02-15 09:00:11 -05:00
|
|
|
bug5700 = '[ruby-core:41456]'
|
|
|
|
assert_nothing_raised(bug5700) do
|
2018-08-29 04:04:09 -04:00
|
|
|
Fiber.new do
|
|
|
|
pid = fork do
|
2018-09-12 16:49:24 -04:00
|
|
|
xpid = nil
|
|
|
|
Fiber.new {
|
|
|
|
xpid = fork do
|
|
|
|
# enough to trigger GC on old root fiber
|
2021-06-25 18:17:26 -04:00
|
|
|
count = 1000
|
2019-09-02 23:27:53 -04:00
|
|
|
count.times do
|
2018-09-12 16:49:24 -04:00
|
|
|
Fiber.new {}.transfer
|
|
|
|
Fiber.new { Fiber.yield }
|
|
|
|
end
|
|
|
|
exit!(0)
|
|
|
|
end
|
|
|
|
}.transfer
|
|
|
|
_, status = Process.waitpid2(xpid)
|
|
|
|
exit!(status.success?)
|
2018-08-29 04:04:09 -04:00
|
|
|
end
|
|
|
|
end.resume
|
2012-02-15 09:00:11 -05:00
|
|
|
end
|
|
|
|
pid, status = Process.waitpid2(pid)
|
|
|
|
assert_equal(0, status.exitstatus, bug5700)
|
|
|
|
assert_equal(false, status.signaled?, bug5700)
|
|
|
|
end
|
2012-12-15 10:46:07 -05:00
|
|
|
|
|
|
|
def test_exit_in_fiber
|
|
|
|
bug5993 = '[ruby-dev:45218]'
|
|
|
|
assert_nothing_raised(bug5993) do
|
2012-12-22 01:45:28 -05:00
|
|
|
Thread.new{ Fiber.new{ Thread.exit }.resume; raise "unreachable" }.join
|
2012-12-15 10:46:07 -05:00
|
|
|
end
|
|
|
|
end
|
2012-12-17 23:30:44 -05:00
|
|
|
|
|
|
|
def test_fatal_in_fiber
|
|
|
|
assert_in_out_err(["-r-test-/fatal/rb_fatal", "-e", <<-EOS], "", [], /ok/)
|
|
|
|
Fiber.new{
|
2021-01-08 05:07:16 -05:00
|
|
|
Bug.rb_fatal "ok"
|
2012-12-17 23:30:44 -05:00
|
|
|
}.resume
|
|
|
|
puts :ng # unreachable.
|
|
|
|
EOS
|
|
|
|
end
|
* cont.c: support Fiber. Check test/ruby/test_fiber.rb for detail.
Fiber is known as "Micro Thread", "Coroutine", and other terms.
At this time, only Fiber#pass is supported to change context.
I want to know more suitable method name/API for Fiber (... do you
know more suitable class name instead of Fiber?) as "suspend/resume",
"call", "yield", "start/kick/stop/restart", ....
* eval.c, eval_intern.h, thread.c, yarvcore.c, yarvcore.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12395 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-05-27 15:12:43 -04:00
|
|
|
|
2013-01-29 23:17:59 -05:00
|
|
|
def test_separate_lastmatch
|
|
|
|
bug7678 = '[ruby-core:51331]'
|
|
|
|
/a/ =~ "a"
|
|
|
|
m1 = $~
|
|
|
|
m2 = nil
|
|
|
|
Fiber.new do
|
|
|
|
/b/ =~ "b"
|
|
|
|
m2 = $~
|
|
|
|
end.resume
|
|
|
|
assert_equal("b", m2[0])
|
|
|
|
assert_equal(m1, $~, bug7678)
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_separate_lastline
|
|
|
|
bug7678 = '[ruby-core:51331]'
|
|
|
|
$_ = s1 = "outer"
|
|
|
|
s2 = nil
|
|
|
|
Fiber.new do
|
|
|
|
s2 = "inner"
|
|
|
|
end.resume
|
|
|
|
assert_equal("inner", s2)
|
|
|
|
assert_equal(s1, $_, bug7678)
|
|
|
|
end
|
2017-03-14 02:52:44 -04:00
|
|
|
|
|
|
|
def test_new_symbol_proc
|
|
|
|
bug = '[ruby-core:80147] [Bug #13313]'
|
|
|
|
assert_ruby_status([], "#{<<-"begin;"}\n#{<<-'end;'}", bug)
|
|
|
|
begin;
|
|
|
|
exit("1" == Fiber.new(&:to_s).resume(1))
|
|
|
|
end;
|
|
|
|
end
|
2017-08-09 22:58:36 -04:00
|
|
|
|
|
|
|
def test_to_s
|
|
|
|
f = Fiber.new do
|
|
|
|
assert_match(/resumed/, f.to_s)
|
|
|
|
Fiber.yield
|
|
|
|
end
|
|
|
|
assert_match(/created/, f.to_s)
|
|
|
|
f.resume
|
|
|
|
assert_match(/suspended/, f.to_s)
|
|
|
|
f.resume
|
|
|
|
assert_match(/terminated/, f.to_s)
|
2017-09-01 21:47:43 -04:00
|
|
|
assert_match(/resumed/, Fiber.current.to_s)
|
2017-08-09 22:58:36 -04:00
|
|
|
end
|
2018-04-03 06:21:47 -04:00
|
|
|
|
2018-04-03 12:54:27 -04:00
|
|
|
def test_create_fiber_in_new_thread
|
2018-04-03 06:21:47 -04:00
|
|
|
ret = Thread.new{
|
|
|
|
Thread.new{
|
|
|
|
Fiber.new{Fiber.yield :ok}.resume
|
2018-04-03 12:54:27 -04:00
|
|
|
}.value
|
|
|
|
}.value
|
2018-04-03 12:50:17 -04:00
|
|
|
assert_equal :ok, ret, '[Bug #14642]'
|
2018-04-03 06:21:47 -04:00
|
|
|
end
|
2018-11-30 22:49:52 -05:00
|
|
|
|
|
|
|
def test_machine_stack_gc
|
|
|
|
assert_normal_exit <<-RUBY, '[Bug #14561]', timeout: 10
|
|
|
|
enum = Enumerator.new { |y| y << 1 }
|
|
|
|
thread = Thread.new { enum.peek }
|
|
|
|
thread.join
|
|
|
|
sleep 5 # pause until thread cache wait time runs out. Native thread exits.
|
|
|
|
GC.start
|
|
|
|
RUBY
|
|
|
|
end
|
2012-12-19 17:29:18 -05:00
|
|
|
end
|