mame-threadfiber 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/History.txt +3 -0
- data/Manifest.txt +6 -0
- data/README.txt +36 -0
- data/Rakefile +12 -0
- data/lib/threadfiber.rb +94 -0
- data/test/test_threadfiber.rb +168 -0
- metadata +70 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
= threadfiber
|
|
2
|
+
|
|
3
|
+
* http://github.com/mame/threadfiber/tree/master
|
|
4
|
+
|
|
5
|
+
== DESCRIPTION:
|
|
6
|
+
|
|
7
|
+
ThreadFiber is an implementation of fiber using threads.
|
|
8
|
+
|
|
9
|
+
== FEATURES/PROBLEMS:
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
== SYNOPSIS:
|
|
13
|
+
|
|
14
|
+
require "threadfiber"
|
|
15
|
+
|
|
16
|
+
f = ThreadFiber.new do
|
|
17
|
+
p :foo
|
|
18
|
+
Fiber.yield
|
|
19
|
+
p :bar
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
f.resume #=> :foo
|
|
23
|
+
f.resume #=> :bar
|
|
24
|
+
|
|
25
|
+
== REQUIREMENTS:
|
|
26
|
+
|
|
27
|
+
None
|
|
28
|
+
|
|
29
|
+
== INSTALL:
|
|
30
|
+
|
|
31
|
+
* gem install mame-threadfiber
|
|
32
|
+
|
|
33
|
+
== LICENSE:
|
|
34
|
+
|
|
35
|
+
Copyright:: Yusuke Endoh <mame@tsg.ne.jp>
|
|
36
|
+
License:: Ruby's
|
data/Rakefile
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# -*- ruby -*-
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'hoe'
|
|
5
|
+
require './lib/threadfiber.rb'
|
|
6
|
+
|
|
7
|
+
Hoe.new('threadfiber', ThreadFiber::VERSION) do |p|
|
|
8
|
+
p.rubyforge_name = 'threadfiberx' # if different than lowercase project name
|
|
9
|
+
p.developer('Yusuke Endoh', 'mame@tsg.ne.jp')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# vim: syntax=Ruby
|
data/lib/threadfiber.rb
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require "thread"
|
|
2
|
+
|
|
3
|
+
class ThreadFiber
|
|
4
|
+
VERSION = "1.0.0"
|
|
5
|
+
|
|
6
|
+
@@fibers = {}
|
|
7
|
+
|
|
8
|
+
def initialize(&block)
|
|
9
|
+
unless block_given?
|
|
10
|
+
raise(ArgumentError, "tried to create Fiber without a block")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
@resume_mutex = Mutex.new
|
|
14
|
+
@switch_mutex = Mutex.new
|
|
15
|
+
@switch_cond = ConditionVariable.new
|
|
16
|
+
@arg, @exception, @dead = nil, nil, false
|
|
17
|
+
@switch_mutex.synchronize do
|
|
18
|
+
th = Thread.new do
|
|
19
|
+
arg, exc = call_block(switch, &block)
|
|
20
|
+
@switch_mutex.synchronize do
|
|
21
|
+
@arg, @exception, @dead = arg, exc, true
|
|
22
|
+
@switch_cond.signal
|
|
23
|
+
end
|
|
24
|
+
@@fibers.delete(Thread.current)
|
|
25
|
+
end
|
|
26
|
+
@@fibers[th] = self
|
|
27
|
+
@switch_cond.wait(@switch_mutex)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def call_block(arg, &block)
|
|
32
|
+
[yield(arg), nil]
|
|
33
|
+
rescue ThreadError
|
|
34
|
+
case $!.message
|
|
35
|
+
when "return can't jump across threads"
|
|
36
|
+
[nil, LocalJumpError.new("unexpected return")]
|
|
37
|
+
when /\Auncaught throw/
|
|
38
|
+
[nil, ArgumentError.new($!.message)]
|
|
39
|
+
else
|
|
40
|
+
[nil, $!]
|
|
41
|
+
end
|
|
42
|
+
rescue Exception
|
|
43
|
+
[nil, $!]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def switch(*args)
|
|
47
|
+
@switch_mutex.synchronize do
|
|
48
|
+
raise(FiberError, "dead fiber called") if @dead
|
|
49
|
+
@arg = args.size <= 1 ? args.first : args
|
|
50
|
+
@switch_cond.signal
|
|
51
|
+
@switch_cond.wait(@switch_mutex)
|
|
52
|
+
raise @exception if @exception
|
|
53
|
+
@arg
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def resume(*args)
|
|
58
|
+
if @resume_mutex.try_lock
|
|
59
|
+
begin
|
|
60
|
+
switch(*args)
|
|
61
|
+
ensure
|
|
62
|
+
@resume_mutex.unlock
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
raise(FiberError, "double resume")
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def resume_wait(*args)
|
|
70
|
+
@resume_mutex.synchronize { switch(*args) }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.yield(*args)
|
|
74
|
+
fib = current
|
|
75
|
+
raise(FiberError, "can't yield from root fiber") unless fib
|
|
76
|
+
fib.switch(*args)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.current
|
|
80
|
+
@@fibers[Thread.current]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def self.deploy
|
|
84
|
+
Object.instance_eval do
|
|
85
|
+
remove_const(:Fiber) if const_defined?(:Fiber)
|
|
86
|
+
const_set(:Fiber, ThreadFiber)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
unless defined?(FiberError)
|
|
92
|
+
class FiberError < StandardError
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
require 'test/unit'
|
|
2
|
+
require 'threadfiber'
|
|
3
|
+
begin
|
|
4
|
+
require 'continuation'
|
|
5
|
+
rescue LoadError
|
|
6
|
+
end
|
|
7
|
+
ThreadFiber.deploy
|
|
8
|
+
|
|
9
|
+
class TestFiber < Test::Unit::TestCase
|
|
10
|
+
def test_normal
|
|
11
|
+
f = Fiber.current
|
|
12
|
+
assert_equal(:ok2,
|
|
13
|
+
Fiber.new{|e|
|
|
14
|
+
assert_equal(:ok1, e)
|
|
15
|
+
Fiber.yield :ok2
|
|
16
|
+
}.resume(:ok1)
|
|
17
|
+
)
|
|
18
|
+
assert_equal([:a, :b], Fiber.new{|a, b| [a, b]}.resume(:a, :b))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_term
|
|
22
|
+
assert_equal(:ok, Fiber.new{:ok}.resume)
|
|
23
|
+
assert_equal([:a, :b, :c, :d, :e],
|
|
24
|
+
Fiber.new{
|
|
25
|
+
Fiber.new{
|
|
26
|
+
Fiber.new{
|
|
27
|
+
Fiber.new{
|
|
28
|
+
[:a]
|
|
29
|
+
}.resume + [:b]
|
|
30
|
+
}.resume + [:c]
|
|
31
|
+
}.resume + [:d]
|
|
32
|
+
}.resume + [:e])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_many_fibers
|
|
36
|
+
max = 100
|
|
37
|
+
assert_equal(max, max.times{
|
|
38
|
+
Fiber.new{}
|
|
39
|
+
})
|
|
40
|
+
assert_equal(max,
|
|
41
|
+
max.times{|i|
|
|
42
|
+
Fiber.new{
|
|
43
|
+
}.resume
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_many_fibers_with_threads
|
|
49
|
+
max = 10
|
|
50
|
+
@cnt = 0
|
|
51
|
+
(1..10).map{|ti|
|
|
52
|
+
Thread.new{
|
|
53
|
+
max.times{|i|
|
|
54
|
+
Fiber.new{
|
|
55
|
+
@cnt += 1
|
|
56
|
+
}.resume
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}.each{|t|
|
|
60
|
+
t.join
|
|
61
|
+
}
|
|
62
|
+
assert_equal(:ok, :ok)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_error
|
|
66
|
+
assert_raise(ArgumentError){
|
|
67
|
+
Fiber.new # Fiber without block
|
|
68
|
+
}
|
|
69
|
+
# assert_raise(FiberError){
|
|
70
|
+
# f = Fiber.new{}
|
|
71
|
+
# Thread.new{f.resume}.join # Fiber yielding across thread
|
|
72
|
+
# }
|
|
73
|
+
assert_raise(FiberError){
|
|
74
|
+
f = Fiber.new{}
|
|
75
|
+
f.resume
|
|
76
|
+
f.resume
|
|
77
|
+
}
|
|
78
|
+
assert_raise(RuntimeError){
|
|
79
|
+
f = Fiber.new{
|
|
80
|
+
@c = callcc{|c| @c = c}
|
|
81
|
+
}.resume
|
|
82
|
+
@c.call # cross fiber callcc
|
|
83
|
+
}
|
|
84
|
+
assert_raise(RuntimeError){
|
|
85
|
+
Fiber.new{
|
|
86
|
+
raise
|
|
87
|
+
}.resume
|
|
88
|
+
}
|
|
89
|
+
assert_raise(FiberError){
|
|
90
|
+
Fiber.yield
|
|
91
|
+
}
|
|
92
|
+
assert_raise(FiberError){
|
|
93
|
+
fib = Fiber.new{
|
|
94
|
+
fib.resume
|
|
95
|
+
}
|
|
96
|
+
fib.resume
|
|
97
|
+
}
|
|
98
|
+
assert_raise(FiberError){
|
|
99
|
+
fib = Fiber.new{
|
|
100
|
+
Fiber.new{
|
|
101
|
+
fib.resume
|
|
102
|
+
}.resume
|
|
103
|
+
}
|
|
104
|
+
fib.resume
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def test_return
|
|
109
|
+
assert_raise(LocalJumpError){
|
|
110
|
+
Fiber.new do
|
|
111
|
+
return
|
|
112
|
+
end.resume
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def test_throw
|
|
117
|
+
assert_raise(ArgumentError){
|
|
118
|
+
Fiber.new do
|
|
119
|
+
throw :a
|
|
120
|
+
end.resume
|
|
121
|
+
}
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# def test_transfer
|
|
125
|
+
# ary = []
|
|
126
|
+
# f2 = nil
|
|
127
|
+
# f1 = Fiber.new{
|
|
128
|
+
# ary << f2.transfer(:foo)
|
|
129
|
+
# :ok
|
|
130
|
+
# }
|
|
131
|
+
# f2 = Fiber.new{
|
|
132
|
+
# ary << f1.transfer(:baz)
|
|
133
|
+
# :ng
|
|
134
|
+
# }
|
|
135
|
+
# assert_equal(:ok, f1.transfer)
|
|
136
|
+
# assert_equal([:baz], ary)
|
|
137
|
+
# end
|
|
138
|
+
|
|
139
|
+
def test_tls
|
|
140
|
+
#
|
|
141
|
+
def tvar(var, val)
|
|
142
|
+
old = Thread.current[var]
|
|
143
|
+
begin
|
|
144
|
+
Thread.current[var] = val
|
|
145
|
+
yield
|
|
146
|
+
ensure
|
|
147
|
+
Thread.current[var] = old
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
fb = Fiber.new {
|
|
152
|
+
assert_equal(nil, Thread.current[:v]); tvar(:v, :x) {
|
|
153
|
+
assert_equal(:x, Thread.current[:v]); Fiber.yield
|
|
154
|
+
assert_equal(:x, Thread.current[:v]); }
|
|
155
|
+
assert_equal(nil, Thread.current[:v]); Fiber.yield
|
|
156
|
+
raise # unreachable
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
assert_equal(nil, Thread.current[:v]); tvar(:v,1) {
|
|
160
|
+
assert_equal(1, Thread.current[:v]); tvar(:v,3) {
|
|
161
|
+
assert_equal(3, Thread.current[:v]); fb.resume
|
|
162
|
+
assert_equal(3, Thread.current[:v]); }
|
|
163
|
+
assert_equal(1, Thread.current[:v]); }
|
|
164
|
+
assert_equal(nil, Thread.current[:v]); fb.resume
|
|
165
|
+
assert_equal(nil, Thread.current[:v]);
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
metadata
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: mame-threadfiber
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Yusuke Endoh
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2008-08-24 00:00:00 -07:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: hoe
|
|
17
|
+
version_requirement:
|
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
19
|
+
requirements:
|
|
20
|
+
- - ">="
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: 1.7.0
|
|
23
|
+
version:
|
|
24
|
+
description: ThreadFiber is an implementation of fiber using threads.
|
|
25
|
+
email:
|
|
26
|
+
- mame@tsg.ne.jp
|
|
27
|
+
executables: []
|
|
28
|
+
|
|
29
|
+
extensions: []
|
|
30
|
+
|
|
31
|
+
extra_rdoc_files:
|
|
32
|
+
- History.txt
|
|
33
|
+
- Manifest.txt
|
|
34
|
+
- README.txt
|
|
35
|
+
files:
|
|
36
|
+
- History.txt
|
|
37
|
+
- Manifest.txt
|
|
38
|
+
- README.txt
|
|
39
|
+
- Rakefile
|
|
40
|
+
- lib/threadfiber.rb
|
|
41
|
+
- test/test_threadfiber.rb
|
|
42
|
+
has_rdoc: true
|
|
43
|
+
homepage: http://github.com/mame/threadfiber/tree/master
|
|
44
|
+
post_install_message:
|
|
45
|
+
rdoc_options:
|
|
46
|
+
- --main
|
|
47
|
+
- README.txt
|
|
48
|
+
require_paths:
|
|
49
|
+
- lib
|
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: "0"
|
|
55
|
+
version:
|
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: "0"
|
|
61
|
+
version:
|
|
62
|
+
requirements: []
|
|
63
|
+
|
|
64
|
+
rubyforge_project: threadfiberx
|
|
65
|
+
rubygems_version: 1.2.0
|
|
66
|
+
signing_key:
|
|
67
|
+
specification_version: 2
|
|
68
|
+
summary: ThreadFiber is an implementation of fiber using threads.
|
|
69
|
+
test_files:
|
|
70
|
+
- test/test_threadfiber.rb
|