fiber18 1.0.0
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.
- data/.gitignore +2 -0
- data/README +21 -0
- data/Rakefile +8 -0
- data/fiber18.gemspec +19 -0
- data/lib/compat/continuation.rb +0 -0
- data/lib/compat/fiber.rb +0 -0
- data/lib/fiber18.rb +96 -0
- data/test/test_fiber.rb +165 -0
- metadata +62 -0
data/.gitignore
ADDED
data/README
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Poor Man's Fiber (API compatible Thread based Fiber implementation for Ruby 1.8)
|
2
|
+
(c) 2008 Aman Gupta (tmm1)
|
3
|
+
|
4
|
+
== Usage
|
5
|
+
|
6
|
+
f = Fiber.new{ |sym|
|
7
|
+
p(sym)
|
8
|
+
puts 'hi'
|
9
|
+
p(Fiber.yield 1)
|
10
|
+
puts 'bye'
|
11
|
+
:end
|
12
|
+
}
|
13
|
+
p(f.resume :begin)
|
14
|
+
p(f.resume 2)
|
15
|
+
|
16
|
+
:begin
|
17
|
+
hi
|
18
|
+
1
|
19
|
+
2
|
20
|
+
bye
|
21
|
+
:end
|
data/Rakefile
ADDED
data/fiber18.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "fiber18"
|
6
|
+
s.version = "1.0.0"
|
7
|
+
s.authors = [""]
|
8
|
+
s.email = [""]
|
9
|
+
s.homepage = "http://github.com/tmm1/fiber18"
|
10
|
+
s.summary = %q{API compatible Thread based Fiber implementation for Ruby 1.8}
|
11
|
+
s.description = %q{API compatible Thread based Fiber implementation for Ruby 1.8 (including JRuby in 1.8 mode)}
|
12
|
+
|
13
|
+
s.rubyforge_project = "fiber18"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
end
|
File without changes
|
data/lib/compat/fiber.rb
ADDED
File without changes
|
data/lib/fiber18.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# Poor Man's Fiber (API compatible Thread based Fiber implementation for Ruby 1.8)
|
2
|
+
# (c) 2008 Aman Gupta (tmm1)
|
3
|
+
|
4
|
+
unless defined? Fiber
|
5
|
+
$:.unshift File.expand_path(File.dirname(__FILE__)) + '/compat'
|
6
|
+
require 'thread'
|
7
|
+
|
8
|
+
class FiberError < StandardError; end
|
9
|
+
|
10
|
+
class Fiber
|
11
|
+
def initialize
|
12
|
+
raise ArgumentError, 'new Fiber requires a block' unless block_given?
|
13
|
+
|
14
|
+
@yield = Queue.new
|
15
|
+
@resume = Queue.new
|
16
|
+
|
17
|
+
@thread = Thread.new{ @yield.push [yield(*@resume.pop)] }
|
18
|
+
@thread.abort_on_exception = true
|
19
|
+
@thread[:fiber] = self
|
20
|
+
end
|
21
|
+
attr_reader :thread
|
22
|
+
|
23
|
+
def resume *args
|
24
|
+
raise FiberError, 'dead fiber called' unless @thread.alive?
|
25
|
+
@resume.push(args)
|
26
|
+
result = @yield.pop
|
27
|
+
result.size > 1 ? result : result.first
|
28
|
+
end
|
29
|
+
|
30
|
+
def yield *args
|
31
|
+
@yield.push(args)
|
32
|
+
result = @resume.pop
|
33
|
+
result.size > 1 ? result : result.first
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.yield *args
|
37
|
+
if fiber = Thread.current[:fiber]
|
38
|
+
fiber.yield(*args)
|
39
|
+
else
|
40
|
+
raise FiberError, 'not inside a fiber'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.current
|
45
|
+
if Thread.current == Thread.main
|
46
|
+
return Thread.main[:fiber] ||= RootFiber.new
|
47
|
+
end
|
48
|
+
|
49
|
+
Thread.current[:fiber] or raise FiberError, 'not inside a fiber'
|
50
|
+
end
|
51
|
+
|
52
|
+
def inspect
|
53
|
+
"#<#{self.class}:0x#{self.object_id.to_s(16)}>"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class RootFiber < Fiber
|
58
|
+
def initialize
|
59
|
+
# XXX: what is a root fiber anyway?
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.yield *args
|
63
|
+
raise FiberError, "can't yield from root fiber"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
if __FILE__ == $0
|
69
|
+
f = Fiber.new{ |sym|
|
70
|
+
p(sym)
|
71
|
+
puts 'hi'
|
72
|
+
p(Fiber.yield 1)
|
73
|
+
puts 'bye'
|
74
|
+
:end
|
75
|
+
}
|
76
|
+
p(f.resume :begin)
|
77
|
+
p(f.resume 2)
|
78
|
+
end
|
79
|
+
|
80
|
+
__END__
|
81
|
+
|
82
|
+
$ ruby fbr.rb
|
83
|
+
:begin
|
84
|
+
hi
|
85
|
+
1
|
86
|
+
2
|
87
|
+
bye
|
88
|
+
:end
|
89
|
+
|
90
|
+
$ ruby1.9 fbr.rb
|
91
|
+
:begin
|
92
|
+
hi
|
93
|
+
1
|
94
|
+
2
|
95
|
+
bye
|
96
|
+
:end
|
data/test/test_fiber.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'fiber'
|
3
|
+
require 'continuation'
|
4
|
+
|
5
|
+
class TestFiber < Test::Unit::TestCase
|
6
|
+
def test_normal
|
7
|
+
f = Fiber.current
|
8
|
+
assert_equal(:ok2,
|
9
|
+
Fiber.new{|e|
|
10
|
+
assert_equal(:ok1, e)
|
11
|
+
Fiber.yield :ok2
|
12
|
+
}.resume(:ok1)
|
13
|
+
)
|
14
|
+
assert_equal([:a, :b], Fiber.new{|a, b| [a, b]}.resume(:a, :b))
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_term
|
18
|
+
assert_equal(:ok, Fiber.new{:ok}.resume)
|
19
|
+
assert_equal([:a, :b, :c, :d, :e],
|
20
|
+
Fiber.new{
|
21
|
+
Fiber.new{
|
22
|
+
Fiber.new{
|
23
|
+
Fiber.new{
|
24
|
+
[:a]
|
25
|
+
}.resume + [:b]
|
26
|
+
}.resume + [:c]
|
27
|
+
}.resume + [:d]
|
28
|
+
}.resume + [:e])
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_many_fibers
|
32
|
+
max = 10000
|
33
|
+
assert_equal(max, max.times{
|
34
|
+
Fiber.new{}
|
35
|
+
})
|
36
|
+
assert_equal(max,
|
37
|
+
max.times{|i|
|
38
|
+
Fiber.new{
|
39
|
+
}.resume
|
40
|
+
}
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
__END__
|
45
|
+
|
46
|
+
def test_many_fibers_with_threads
|
47
|
+
max = 1000
|
48
|
+
@cnt = 0
|
49
|
+
(1..100).map{|ti|
|
50
|
+
Thread.new{
|
51
|
+
max.times{|i|
|
52
|
+
Fiber.new{
|
53
|
+
@cnt += 1
|
54
|
+
}.resume
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}.each{|t|
|
58
|
+
t.join
|
59
|
+
}
|
60
|
+
assert_equal(:ok, :ok)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_error
|
64
|
+
assert_raise(ArgumentError){
|
65
|
+
Fiber.new # Fiber without block
|
66
|
+
}
|
67
|
+
assert_raise(FiberError){
|
68
|
+
f = Fiber.new{}
|
69
|
+
Thread.new{f.resume}.join # Fiber yielding across thread
|
70
|
+
}
|
71
|
+
assert_raise(FiberError){
|
72
|
+
f = Fiber.new{}
|
73
|
+
f.resume
|
74
|
+
f.resume
|
75
|
+
}
|
76
|
+
assert_raise(RuntimeError){
|
77
|
+
f = Fiber.new{
|
78
|
+
@c = callcc{|c| @c = c}
|
79
|
+
}.resume
|
80
|
+
@c.call # cross fiber callcc
|
81
|
+
}
|
82
|
+
assert_raise(RuntimeError){
|
83
|
+
Fiber.new{
|
84
|
+
raise
|
85
|
+
}.resume
|
86
|
+
}
|
87
|
+
assert_raise(FiberError){
|
88
|
+
Fiber.yield
|
89
|
+
}
|
90
|
+
assert_raise(FiberError){
|
91
|
+
fib = Fiber.new{
|
92
|
+
fib.resume
|
93
|
+
}
|
94
|
+
fib.resume
|
95
|
+
}
|
96
|
+
assert_raise(FiberError){
|
97
|
+
fib = Fiber.new{
|
98
|
+
Fiber.new{
|
99
|
+
fib.resume
|
100
|
+
}.resume
|
101
|
+
}
|
102
|
+
fib.resume
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_return
|
107
|
+
assert_raise(LocalJumpError){
|
108
|
+
Fiber.new do
|
109
|
+
return
|
110
|
+
end.resume
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_throw
|
115
|
+
assert_raise(ArgumentError){
|
116
|
+
Fiber.new do
|
117
|
+
throw :a
|
118
|
+
end.resume
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_transfer
|
123
|
+
ary = []
|
124
|
+
f2 = nil
|
125
|
+
f1 = Fiber.new{
|
126
|
+
ary << f2.transfer(:foo)
|
127
|
+
:ok
|
128
|
+
}
|
129
|
+
f2 = Fiber.new{
|
130
|
+
ary << f1.transfer(:baz)
|
131
|
+
:ng
|
132
|
+
}
|
133
|
+
assert_equal(:ok, f1.transfer)
|
134
|
+
assert_equal([:baz], ary)
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_tls
|
138
|
+
#
|
139
|
+
def tvar(var, val)
|
140
|
+
old = Thread.current[var]
|
141
|
+
begin
|
142
|
+
Thread.current[var] = val
|
143
|
+
yield
|
144
|
+
ensure
|
145
|
+
Thread.current[var] = old
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
fb = Fiber.new {
|
150
|
+
assert_equal(nil, Thread.current[:v]); tvar(:v, :x) {
|
151
|
+
assert_equal(:x, Thread.current[:v]); Fiber.yield
|
152
|
+
assert_equal(:x, Thread.current[:v]); }
|
153
|
+
assert_equal(nil, Thread.current[:v]); Fiber.yield
|
154
|
+
raise # unreachable
|
155
|
+
}
|
156
|
+
|
157
|
+
assert_equal(nil, Thread.current[:v]); tvar(:v,1) {
|
158
|
+
assert_equal(1, Thread.current[:v]); tvar(:v,3) {
|
159
|
+
assert_equal(3, Thread.current[:v]); fb.resume
|
160
|
+
assert_equal(3, Thread.current[:v]); }
|
161
|
+
assert_equal(1, Thread.current[:v]); }
|
162
|
+
assert_equal(nil, Thread.current[:v]); fb.resume
|
163
|
+
assert_equal(nil, Thread.current[:v]);
|
164
|
+
end
|
165
|
+
end
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fiber18
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 1.0.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- ""
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2012-03-07 00:00:00 Z
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: API compatible Thread based Fiber implementation for Ruby 1.8 (including JRuby in 1.8 mode)
|
17
|
+
email:
|
18
|
+
- ""
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files: []
|
24
|
+
|
25
|
+
files:
|
26
|
+
- .gitignore
|
27
|
+
- README
|
28
|
+
- Rakefile
|
29
|
+
- fiber18.gemspec
|
30
|
+
- lib/compat/continuation.rb
|
31
|
+
- lib/compat/fiber.rb
|
32
|
+
- lib/fiber18.rb
|
33
|
+
- test/test_fiber.rb
|
34
|
+
homepage: http://github.com/tmm1/fiber18
|
35
|
+
licenses: []
|
36
|
+
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options: []
|
39
|
+
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: "0"
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
requirements: []
|
55
|
+
|
56
|
+
rubyforge_project: fiber18
|
57
|
+
rubygems_version: 1.8.15
|
58
|
+
signing_key:
|
59
|
+
specification_version: 3
|
60
|
+
summary: API compatible Thread based Fiber implementation for Ruby 1.8
|
61
|
+
test_files: []
|
62
|
+
|