strand 0.1.0 → 0.2.0.rc0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/CHANGELOG +13 -0
- data/Gemfile +3 -15
- data/LICENSE.txt +1 -0
- data/README.rdoc +12 -19
- data/Rakefile +10 -57
- data/lib/strand.rb +151 -126
- data/lib/strand/atc.rb +1 -1
- data/lib/strand/em/condition_variable.rb +57 -0
- data/lib/strand/em/mutex.rb +63 -0
- data/lib/strand/em/queue.rb +84 -0
- data/lib/strand/em/thread.rb +305 -0
- data/lib/strand/monitor.rb +193 -0
- data/lib/strand/version.rb +3 -0
- data/spec/spec_helper.rb +9 -5
- data/spec/strand/alive.rb +62 -0
- data/spec/strand/condition_variable.rb +10 -0
- data/spec/strand/condition_variable/broadcast.rb +61 -0
- data/spec/strand/condition_variable/signal.rb +62 -0
- data/spec/strand/condition_variable/wait.rb +20 -0
- data/spec/strand/current.rb +15 -0
- data/spec/strand/exit.rb +148 -0
- data/spec/strand/join.rb +60 -0
- data/spec/strand/local_storage.rb +98 -0
- data/spec/strand/mutex.rb +244 -0
- data/spec/strand/pass.rb +9 -0
- data/spec/strand/queue.rb +124 -0
- data/spec/strand/raise.rb +142 -0
- data/spec/strand/run.rb +5 -0
- data/spec/strand/shared.rb +14 -0
- data/spec/strand/sleep.rb +51 -0
- data/spec/strand/status.rb +44 -0
- data/spec/strand/stop.rb +58 -0
- data/spec/strand/strand.rb +32 -0
- data/spec/strand/value.rb +39 -0
- data/spec/strand/wakeup.rb +60 -0
- data/spec/strand_spec.rb +51 -0
- data/spec/support/fixtures.rb +305 -0
- data/spec/support/scratch.rb +17 -0
- data/spec/thread_spec.rb +20 -0
- data/strand.gemspec +23 -0
- metadata +72 -58
- data/Gemfile.lock +0 -40
- data/lib/strand/condition_variable.rb +0 -78
- data/spec/condition_variable_spec.rb +0 -82
- data/test/helper.rb +0 -30
- data/test/test_strand.rb +0 -121
data/spec/thread_spec.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'strand/shared'
|
3
|
+
|
4
|
+
describe ::Thread do
|
5
|
+
|
6
|
+
let (:strand_type) { ::Thread }
|
7
|
+
let (:strand_exception) { ThreadError }
|
8
|
+
|
9
|
+
around(:each) do |example|
|
10
|
+
ScratchPad.clear
|
11
|
+
example.run
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should not be running in event machine" do
|
15
|
+
Strand.event_machine?.should be_false
|
16
|
+
end
|
17
|
+
|
18
|
+
include_examples Strand
|
19
|
+
end
|
20
|
+
|
data/strand.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'strand/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "strand"
|
8
|
+
gem.version = Strand::VERSION
|
9
|
+
gem.authors = ["Grant Gardner","Christopher J. Bottaro"]
|
10
|
+
gem.email = ["grant@lastweekend.com.au", "cjbottaro@alumni.cs.utexas.edu"]
|
11
|
+
gem.description = %q{Get thread-like behavior from fibers using EventMachine.}
|
12
|
+
gem.summary = %q{Make fibers behave like threads using EventMachine}
|
13
|
+
gem.homepage = "http://rubygems.org/gems/strand"
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
gem.test_files = `git ls-files -- {test,spec}/*`.split($/)
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
gem.licenses = %q{MIT}
|
18
|
+
|
19
|
+
gem.add_development_dependency 'rspec'
|
20
|
+
gem.add_development_dependency 'em-spec'
|
21
|
+
gem.add_development_dependency 'rr'
|
22
|
+
gem.add_development_dependency 'eventmachine', '~> 0.12.0'
|
23
|
+
end
|
metadata
CHANGED
@@ -1,63 +1,52 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strand
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.0.rc0
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
|
+
- Grant Gardner
|
8
9
|
- Christopher J. Bottaro
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date:
|
13
|
+
date: 2012-12-13 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
16
|
-
requirement:
|
16
|
+
name: rspec
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
17
18
|
none: false
|
18
19
|
requirements:
|
19
|
-
- -
|
20
|
+
- - ! '>='
|
20
21
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
22
|
+
version: '0'
|
22
23
|
type: :development
|
23
24
|
prerelease: false
|
24
|
-
version_requirements:
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: em-spec
|
27
|
-
requirement: &70284397100280 !ruby/object:Gem::Requirement
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
26
|
none: false
|
29
27
|
requirements:
|
30
28
|
- - ! '>='
|
31
29
|
- !ruby/object:Gem::Version
|
32
30
|
version: '0'
|
33
|
-
type: :development
|
34
|
-
prerelease: false
|
35
|
-
version_requirements: *70284397100280
|
36
31
|
- !ruby/object:Gem::Dependency
|
37
|
-
name:
|
38
|
-
requirement:
|
32
|
+
name: em-spec
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
39
34
|
none: false
|
40
35
|
requirements:
|
41
|
-
- -
|
36
|
+
- - ! '>='
|
42
37
|
- !ruby/object:Gem::Version
|
43
|
-
version: 0
|
38
|
+
version: '0'
|
44
39
|
type: :development
|
45
40
|
prerelease: false
|
46
|
-
version_requirements:
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: jeweler
|
49
|
-
requirement: &70284397153900 !ruby/object:Gem::Requirement
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
42
|
none: false
|
51
43
|
requirements:
|
52
|
-
- -
|
44
|
+
- - ! '>='
|
53
45
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
55
|
-
type: :development
|
56
|
-
prerelease: false
|
57
|
-
version_requirements: *70284397153900
|
46
|
+
version: '0'
|
58
47
|
- !ruby/object:Gem::Dependency
|
59
|
-
name:
|
60
|
-
requirement:
|
48
|
+
name: rr
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
61
50
|
none: false
|
62
51
|
requirements:
|
63
52
|
- - ! '>='
|
@@ -65,53 +54,81 @@ dependencies:
|
|
65
54
|
version: '0'
|
66
55
|
type: :development
|
67
56
|
prerelease: false
|
68
|
-
version_requirements:
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rr
|
71
|
-
requirement: &70284397150240 !ruby/object:Gem::Requirement
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
58
|
none: false
|
73
59
|
requirements:
|
74
60
|
- - ! '>='
|
75
61
|
- !ruby/object:Gem::Version
|
76
62
|
version: '0'
|
77
|
-
type: :development
|
78
|
-
prerelease: false
|
79
|
-
version_requirements: *70284397150240
|
80
63
|
- !ruby/object:Gem::Dependency
|
81
|
-
name:
|
82
|
-
requirement:
|
64
|
+
name: eventmachine
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
83
66
|
none: false
|
84
67
|
requirements:
|
85
|
-
- -
|
68
|
+
- - ~>
|
86
69
|
- !ruby/object:Gem::Version
|
87
|
-
version:
|
70
|
+
version: 0.12.0
|
88
71
|
type: :development
|
89
72
|
prerelease: false
|
90
|
-
version_requirements:
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ~>
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 0.12.0
|
91
79
|
description: Get thread-like behavior from fibers using EventMachine.
|
92
|
-
email:
|
80
|
+
email:
|
81
|
+
- grant@lastweekend.com.au
|
82
|
+
- cjbottaro@alumni.cs.utexas.edu
|
93
83
|
executables: []
|
94
84
|
extensions: []
|
95
|
-
extra_rdoc_files:
|
96
|
-
- LICENSE.txt
|
97
|
-
- README.rdoc
|
85
|
+
extra_rdoc_files: []
|
98
86
|
files:
|
99
87
|
- .document
|
88
|
+
- .gitignore
|
100
89
|
- .rspec
|
90
|
+
- CHANGELOG
|
101
91
|
- Gemfile
|
102
|
-
- Gemfile.lock
|
103
92
|
- LICENSE.txt
|
104
93
|
- README.rdoc
|
105
94
|
- Rakefile
|
106
95
|
- VERSION
|
107
96
|
- lib/strand.rb
|
108
97
|
- lib/strand/atc.rb
|
109
|
-
- lib/strand/condition_variable.rb
|
110
|
-
-
|
98
|
+
- lib/strand/em/condition_variable.rb
|
99
|
+
- lib/strand/em/mutex.rb
|
100
|
+
- lib/strand/em/queue.rb
|
101
|
+
- lib/strand/em/thread.rb
|
102
|
+
- lib/strand/monitor.rb
|
103
|
+
- lib/strand/version.rb
|
111
104
|
- spec/spec_helper.rb
|
112
|
-
-
|
113
|
-
-
|
114
|
-
|
105
|
+
- spec/strand/alive.rb
|
106
|
+
- spec/strand/condition_variable.rb
|
107
|
+
- spec/strand/condition_variable/broadcast.rb
|
108
|
+
- spec/strand/condition_variable/signal.rb
|
109
|
+
- spec/strand/condition_variable/wait.rb
|
110
|
+
- spec/strand/current.rb
|
111
|
+
- spec/strand/exit.rb
|
112
|
+
- spec/strand/join.rb
|
113
|
+
- spec/strand/local_storage.rb
|
114
|
+
- spec/strand/mutex.rb
|
115
|
+
- spec/strand/pass.rb
|
116
|
+
- spec/strand/queue.rb
|
117
|
+
- spec/strand/raise.rb
|
118
|
+
- spec/strand/run.rb
|
119
|
+
- spec/strand/shared.rb
|
120
|
+
- spec/strand/sleep.rb
|
121
|
+
- spec/strand/status.rb
|
122
|
+
- spec/strand/stop.rb
|
123
|
+
- spec/strand/strand.rb
|
124
|
+
- spec/strand/value.rb
|
125
|
+
- spec/strand/wakeup.rb
|
126
|
+
- spec/strand_spec.rb
|
127
|
+
- spec/support/fixtures.rb
|
128
|
+
- spec/support/scratch.rb
|
129
|
+
- spec/thread_spec.rb
|
130
|
+
- strand.gemspec
|
131
|
+
homepage: http://rubygems.org/gems/strand
|
115
132
|
licenses:
|
116
133
|
- MIT
|
117
134
|
post_install_message:
|
@@ -124,19 +141,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
124
141
|
- - ! '>='
|
125
142
|
- !ruby/object:Gem::Version
|
126
143
|
version: '0'
|
127
|
-
segments:
|
128
|
-
- 0
|
129
|
-
hash: -933947513672593859
|
130
144
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
145
|
none: false
|
132
146
|
requirements:
|
133
|
-
- - ! '
|
147
|
+
- - ! '>'
|
134
148
|
- !ruby/object:Gem::Version
|
135
|
-
version:
|
149
|
+
version: 1.3.1
|
136
150
|
requirements: []
|
137
151
|
rubyforge_project:
|
138
|
-
rubygems_version: 1.8.
|
152
|
+
rubygems_version: 1.8.24
|
139
153
|
signing_key:
|
140
154
|
specification_version: 3
|
141
|
-
summary: Make fibers behave like threads using EventMachine
|
155
|
+
summary: Make fibers behave like threads using EventMachine
|
142
156
|
test_files: []
|
data/Gemfile.lock
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
GEM
|
2
|
-
remote: http://rubygems.org/
|
3
|
-
specs:
|
4
|
-
bacon (1.1.0)
|
5
|
-
diff-lcs (1.1.2)
|
6
|
-
em-spec (0.2.5)
|
7
|
-
bacon
|
8
|
-
eventmachine
|
9
|
-
rspec (~> 2.6.0)
|
10
|
-
test-unit
|
11
|
-
eventmachine (0.12.10)
|
12
|
-
git (1.2.5)
|
13
|
-
jeweler (1.6.4)
|
14
|
-
bundler (~> 1.0)
|
15
|
-
git (>= 1.2.5)
|
16
|
-
rake
|
17
|
-
rake (0.9.2)
|
18
|
-
rcov (0.9.9)
|
19
|
-
rr (1.0.3)
|
20
|
-
rspec (2.6.0)
|
21
|
-
rspec-core (~> 2.6.0)
|
22
|
-
rspec-expectations (~> 2.6.0)
|
23
|
-
rspec-mocks (~> 2.6.0)
|
24
|
-
rspec-core (2.6.4)
|
25
|
-
rspec-expectations (2.6.0)
|
26
|
-
diff-lcs (~> 1.1.2)
|
27
|
-
rspec-mocks (2.6.0)
|
28
|
-
test-unit (2.3.2)
|
29
|
-
|
30
|
-
PLATFORMS
|
31
|
-
ruby
|
32
|
-
|
33
|
-
DEPENDENCIES
|
34
|
-
bundler (~> 1.0.0)
|
35
|
-
em-spec
|
36
|
-
eventmachine (~> 0.12.0)
|
37
|
-
jeweler (~> 1.6.4)
|
38
|
-
rcov
|
39
|
-
rr
|
40
|
-
rspec
|
@@ -1,78 +0,0 @@
|
|
1
|
-
class Strand
|
2
|
-
# Provides for Strands (Fibers) what Ruby's ConditionVariable provides for Threads.
|
3
|
-
class ConditionVariable
|
4
|
-
|
5
|
-
# Create a new condition variable.
|
6
|
-
def initialize
|
7
|
-
@waiters = []
|
8
|
-
end
|
9
|
-
|
10
|
-
# Wait until signaled. Returns true upon returning.
|
11
|
-
# x = nil
|
12
|
-
# cond = Strand::ConditionVariable.new
|
13
|
-
# Strand.new{ cond.wait; x = 1; cond.signal }
|
14
|
-
# puts x # => nil
|
15
|
-
# cond.signal
|
16
|
-
# cond.wait # => true
|
17
|
-
# puts x # => 1
|
18
|
-
# If timeout is a number, then returns false if timed out or true if signaled.
|
19
|
-
# x = nil
|
20
|
-
# cond = Strand::ConditionVariable.new
|
21
|
-
# Strand.new{ cond.wait; x = 1; cond.signal }
|
22
|
-
# puts x # => nil
|
23
|
-
# cond.wait(0.01) # => false
|
24
|
-
# puts x # => nil
|
25
|
-
def wait(timeout = nil)
|
26
|
-
# Get the fiber that called us.
|
27
|
-
fiber = Fiber.current
|
28
|
-
|
29
|
-
# Add the fiber to the list of waiters.
|
30
|
-
@waiters << fiber
|
31
|
-
|
32
|
-
# Setup the timer if they specified a timeout
|
33
|
-
timer = EM::Timer.new(timeout){ fiber.resume(:timeout) } if timeout
|
34
|
-
|
35
|
-
# Wait for signal or timeout.
|
36
|
-
if Fiber.yield == :timeout
|
37
|
-
# Timeout occurred.
|
38
|
-
|
39
|
-
# Remove from list of waiters.
|
40
|
-
@waiters.delete(fiber)
|
41
|
-
|
42
|
-
false
|
43
|
-
else
|
44
|
-
# Ok we were signaled.
|
45
|
-
|
46
|
-
# Cancel the timer if there is one.
|
47
|
-
timer.cancel if timer
|
48
|
-
|
49
|
-
true
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
# Asynchronously resume a fiber waiting on this condition variable.
|
55
|
-
# The waiter is not resumed immediately, but on the next tick of EM's reactor loop.
|
56
|
-
# cond = Strand::ConditionVariable.new
|
57
|
-
# Strand.new{ puts 1; cond.wait; puts 2 }
|
58
|
-
# puts 3
|
59
|
-
# cond.signal
|
60
|
-
# puts 4
|
61
|
-
# # output is...
|
62
|
-
# 1
|
63
|
-
# 3
|
64
|
-
# 4
|
65
|
-
# 2
|
66
|
-
def signal
|
67
|
-
# If there are no waiters, do nothing.
|
68
|
-
return if @waiters.empty?
|
69
|
-
|
70
|
-
# Find a waiter to wake up.
|
71
|
-
waiter = @waiters.shift
|
72
|
-
|
73
|
-
# Resume it on next tick.
|
74
|
-
EM.next_tick{ waiter.resume }
|
75
|
-
end
|
76
|
-
|
77
|
-
end
|
78
|
-
end
|
@@ -1,82 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Strand::ConditionVariable do
|
4
|
-
include EM::SpecHelper
|
5
|
-
|
6
|
-
context "calling #wait" do
|
7
|
-
before(:each) do
|
8
|
-
@cond = described_class.new
|
9
|
-
@atc = Atc.new :timeout => 0.01
|
10
|
-
end
|
11
|
-
it "should block until signaled" do
|
12
|
-
em do
|
13
|
-
Strand.new{ @cond.wait; @atc.signal(1) }
|
14
|
-
@atc.wait(1).should be_false
|
15
|
-
@cond.signal
|
16
|
-
@atc.wait(1).should be_true
|
17
|
-
done
|
18
|
-
end
|
19
|
-
end
|
20
|
-
it "should block until timed out" do
|
21
|
-
em do
|
22
|
-
Strand.new{ @cond.wait(0.02); @atc.signal(1) }
|
23
|
-
@atc.wait(1).should be_false
|
24
|
-
@atc.wait(1).should be_true
|
25
|
-
done
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
context "calling #signal" do
|
31
|
-
context "with no waiters" do
|
32
|
-
before(:all){ @cond = described_class.new }
|
33
|
-
it "should not do anything" do
|
34
|
-
@cond.signal.should be_nil
|
35
|
-
end
|
36
|
-
end
|
37
|
-
context "with a single waiter" do
|
38
|
-
before(:all) do
|
39
|
-
@cond = described_class.new
|
40
|
-
@atc = Atc.new :timeout => 0.01
|
41
|
-
Strand.new{ @cond.wait; @atc.signal(1) }
|
42
|
-
end
|
43
|
-
it "should wake up the waiter" do
|
44
|
-
em do
|
45
|
-
@atc.wait(1).should be_false
|
46
|
-
@cond.signal
|
47
|
-
@atc.wait(1).should be_true
|
48
|
-
done
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
context "with multiple waiters" do
|
53
|
-
before(:all) do
|
54
|
-
@cond = described_class.new
|
55
|
-
@atc = Atc.new :timeout => 0.01
|
56
|
-
Strand.new{ @cond.wait; @atc.signal(1) }
|
57
|
-
Strand.new{ @cond.wait; @atc.signal(2) }
|
58
|
-
end
|
59
|
-
it "should wake up the first waiter" do
|
60
|
-
em do
|
61
|
-
@atc.wait(1).should be_false
|
62
|
-
@atc.wait(2).should be_false
|
63
|
-
@cond.signal
|
64
|
-
@atc.wait(1).should be_true
|
65
|
-
@atc.wait(2).should be_false
|
66
|
-
done
|
67
|
-
end
|
68
|
-
end
|
69
|
-
it "then wake up the second waiter" do
|
70
|
-
em do
|
71
|
-
@atc.wait(1).should be_true
|
72
|
-
@atc.wait(2).should be_false
|
73
|
-
@cond.signal
|
74
|
-
@atc.wait(1).should be_true
|
75
|
-
@atc.wait(2).should be_true
|
76
|
-
done
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|