fiber_recycling 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +55 -2
- data/lib/fiber_recycling/duck_types.rb +1 -1
- data/lib/fiber_recycling/fiber.rb +8 -0
- data/lib/fiber_recycling/normal_fiber_backend.rb +3 -0
- data/lib/fiber_recycling/root_fiber_backend.rb +6 -0
- data/lib/fiber_recycling/thread_extensions.rb +21 -0
- data/lib/fiber_recycling.rb +2 -0
- data/lib/thread.rb +4 -0
- data/spec/thread_spec.rb +85 -0
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 630db3827d3b2a636f707e11f6595dc987627718
|
4
|
+
data.tar.gz: 33a112a7bdfbfc4e08daf71e45bf5ca2e35bed44
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39a32ddb2a25f409eb60350a1dcdc1b56f6e70a92e12ea2fd1f8358989f10b8a663f2bdf325ea848d274c9187e350da3ef5a860b03e66166095fc3ae62c89529
|
7
|
+
data.tar.gz: 8eee84fc7780c79be520918267a1a3992fcaf06b2bedcda4662624c6e157d7c756d14d1be21860c5f1ddafcd728e21004cdf12fb20483995a2048157e1f3d52c
|
data/README.md
CHANGED
@@ -1,2 +1,55 @@
|
|
1
|
-
#
|
2
|
-
This
|
1
|
+
# FiberRecycling
|
2
|
+
This Ruby Gem offers a duck type for `Fiber` that will reuse old native fibers for a small performance gain. It will work as a drop in replacment for Ruby's natve `Fiber`, offering a small performance gain.
|
3
|
+
|
4
|
+
# Install
|
5
|
+
`gem install fiber_recycling`
|
6
|
+
|
7
|
+
Then simply `require 'fiber_recycling'` in your project.
|
8
|
+
|
9
|
+
# Example
|
10
|
+
`FiberRecycling::Fiber` can be used just like `::Fiber`.
|
11
|
+
```ruby
|
12
|
+
fiber = FiberRecycling::Fiber.new do |a, b|
|
13
|
+
c = FiberRecycling::Fiber.yield(a)
|
14
|
+
d = FiberRecycling::Fiber.yield(c + 1)
|
15
|
+
d
|
16
|
+
end
|
17
|
+
|
18
|
+
fiber.resume(1, 2) # => 1
|
19
|
+
fiber.resume(3) # => 4
|
20
|
+
fiber.resume(5) # => 5
|
21
|
+
```
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
fiber = FiberRecycling::Fiber.new do
|
25
|
+
FiberRecycling::Fiber.yield
|
26
|
+
end
|
27
|
+
|
28
|
+
fiber.resume # => nil
|
29
|
+
fiber.resume # => nil
|
30
|
+
fiber.resume # => FiberRecycling::FiberError, dead fiber called
|
31
|
+
```
|
32
|
+
|
33
|
+
To integrate *FiberRecycling* into an existing project that relies on `Fiber` you can `include` `FiberRecycling::DuckTypes`.
|
34
|
+
```ruby
|
35
|
+
module A
|
36
|
+
include FiberRecycling::DuckTypes
|
37
|
+
|
38
|
+
def self.a
|
39
|
+
fiber = Fiber.new do
|
40
|
+
Fiber.yield
|
41
|
+
end
|
42
|
+
|
43
|
+
fiber # => FiberRecycling::Fiber
|
44
|
+
fiber.resume # => nil
|
45
|
+
fiber.resume # => nil
|
46
|
+
fiber.resume # => FiberRecycling::FiberError, dead fiber resumed
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
A.a
|
52
|
+
```
|
53
|
+
|
54
|
+
# How It Works
|
55
|
+
As fibers can only be resumed in the thread that they were created, a native fiber pool will be created on demand for each thread that a pool is needed. The native fibers in each pool also only be created on demand. When a new `FiberRecycling::Fiber` is created, it will create a new native fiber if none are available in thread's pool. The native fiber will only be returned to the pool when the `FiberRecycling::Fiber` has completed executing it's block (is in a dead state). As each pool is shared globally, possibly being accessed by multiple code bases that have no knowledge of each other, it is impossible to allow a code base to set a maximum pool size limit. One project may want a maximum size of 5 native fibers per thread while another may want 10, there is no way to satisfy both requests. As such *FiberRecycling* will put no constraint on the number of native fibers that can be created. Therefore, it is important to keep in mind that *FiberRecycling* will create as many native fibers as is simoustanly demanded and will not delete them until the thread gets garbage collected (if you are creating fibers in the main thread, this will obviously never happen). It is up to the developer of the code base to ensure that only a reasonable amount of `FiberRecycling::Fiber` will ever be alive simoustanly.
|
@@ -12,6 +12,10 @@ module FiberRecycling
|
|
12
12
|
Thread.current.thread_variable_get(:fiber_recycling__root_fiber)
|
13
13
|
end
|
14
14
|
|
15
|
+
def self.root?
|
16
|
+
current == root
|
17
|
+
end
|
18
|
+
|
15
19
|
def self.yield(*args)
|
16
20
|
current.backend.class.yield(*args)
|
17
21
|
end
|
@@ -47,5 +51,9 @@ module FiberRecycling
|
|
47
51
|
@backend.transfer(*args)
|
48
52
|
end
|
49
53
|
|
54
|
+
def variables
|
55
|
+
@backend.variables
|
56
|
+
end
|
57
|
+
|
50
58
|
end
|
51
59
|
end
|
@@ -8,8 +8,11 @@ module FiberRecycling
|
|
8
8
|
return_value
|
9
9
|
end
|
10
10
|
|
11
|
+
attr_reader :variables
|
12
|
+
|
11
13
|
def initialize(fiber, block)
|
12
14
|
@state = 'created'
|
15
|
+
@variables = {}
|
13
16
|
@recycled_fiber = RecycledFiberPool.local.release_recycled_fiber
|
14
17
|
@recycled_fiber.run { execute(fiber, block) }
|
15
18
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module FiberRecycling
|
2
|
+
module ThreadExtensions
|
3
|
+
|
4
|
+
def [](key)
|
5
|
+
if key == :fiber_recycling__fiber || Fiber.root?
|
6
|
+
super
|
7
|
+
else
|
8
|
+
Fiber.current.variables[key]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def []=(key, value)
|
13
|
+
if key == :fiber_recycling__fiber || Fiber.root?
|
14
|
+
super
|
15
|
+
else
|
16
|
+
Fiber.current.variables[key] = value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/fiber_recycling.rb
CHANGED
@@ -11,6 +11,8 @@ require 'fiber_recycling/normal_fiber_backend'
|
|
11
11
|
require 'fiber_recycling/recycled_fiber'
|
12
12
|
require 'fiber_recycling/recycled_fiber_pool'
|
13
13
|
require 'fiber_recycling/root_fiber_backend'
|
14
|
+
require 'fiber_recycling/thread_extensions'
|
15
|
+
require_relative 'thread'
|
14
16
|
|
15
17
|
module FiberRecycling
|
16
18
|
end
|
data/lib/thread.rb
ADDED
data/spec/thread_spec.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'fiber_recycling'
|
2
|
+
|
3
|
+
RSpec.describe Thread do
|
4
|
+
|
5
|
+
context "when calling native method" do
|
6
|
+
it "should work" do
|
7
|
+
expect(Thread.current.alive?).to eql true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context "when in root FiberRecycling::Fiber" do
|
12
|
+
|
13
|
+
context "when setting a variable" do
|
14
|
+
it "should persist" do
|
15
|
+
Thread.current[:a] = 1
|
16
|
+
expect(Thread.current[:a]).to eql 1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "when setting a variable" do
|
21
|
+
it "should not exist in FiberRecycling::Fiber" do
|
22
|
+
Thread.current[:a] = 1
|
23
|
+
fiber = FiberRecycling::Fiber.new do
|
24
|
+
Thread.current[:a]
|
25
|
+
end
|
26
|
+
expect(fiber.resume).to eql nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "when setting a variable" do
|
31
|
+
it "should should persist a new FiberRecycling::Fiber" do
|
32
|
+
Thread.current[:a] = 1
|
33
|
+
fiber = FiberRecycling::Fiber.new do
|
34
|
+
Thread.current[:a] = 2
|
35
|
+
end
|
36
|
+
fiber.resume
|
37
|
+
expect(Thread.current[:a]).to eql 1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when in non root FiberRecycling::Fiber" do
|
44
|
+
|
45
|
+
context "when getting a variable" do
|
46
|
+
it "should not exist outside FiberRecycling::Fiber" do
|
47
|
+
Thread.current[:a] = nil
|
48
|
+
fiber = FiberRecycling::Fiber.new do
|
49
|
+
Thread.current[:a] = 1
|
50
|
+
end
|
51
|
+
fiber.resume
|
52
|
+
expect(Thread.current[:a]).to eql nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "when setting a variable" do
|
57
|
+
it "should persist a yield" do
|
58
|
+
Thread.current[:a] = nil
|
59
|
+
fiber = FiberRecycling::Fiber.new do
|
60
|
+
Thread.current[:a] = 1
|
61
|
+
FiberRecycling::Fiber.yield
|
62
|
+
Thread.current[:a]
|
63
|
+
end
|
64
|
+
fiber.resume
|
65
|
+
Thread.current[:a] = 2
|
66
|
+
expect(fiber.resume).to eql 1
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when setting a variable" do
|
71
|
+
it "should not leak to next fiber" do
|
72
|
+
Thread.current[:a] = nil
|
73
|
+
fiber1 = FiberRecycling::Fiber.new do
|
74
|
+
Thread.current[:a] = 1
|
75
|
+
end
|
76
|
+
fiber1.resume
|
77
|
+
fiber2 = FiberRecycling::Fiber.new do
|
78
|
+
Thread.current[:a]
|
79
|
+
end
|
80
|
+
expect(fiber2.resume).to eql nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fiber_recycling
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob Fors
|
@@ -43,9 +43,12 @@ files:
|
|
43
43
|
- lib/fiber_recycling/recycled_fiber.rb
|
44
44
|
- lib/fiber_recycling/recycled_fiber_pool.rb
|
45
45
|
- lib/fiber_recycling/root_fiber_backend.rb
|
46
|
+
- lib/fiber_recycling/thread_extensions.rb
|
47
|
+
- lib/thread.rb
|
46
48
|
- spec/duck_types_spec.rb
|
47
49
|
- spec/fiber_spec.rb
|
48
50
|
- spec/spec_helper.rb
|
51
|
+
- spec/thread_spec.rb
|
49
52
|
homepage: https://github.com/robfors/fiber_recycling
|
50
53
|
licenses:
|
51
54
|
- MIT
|