fiber_recycling 0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 13919e9d5f85666b880cebaca25b077824034b18
4
+ data.tar.gz: cf2cde3a386850c9177d0c04a5d2860d92840205
5
+ SHA512:
6
+ metadata.gz: 19c3d05d76143172cb73db33dba29d04a64de0fef51d87c5327fd0d775c9689555da0c77289267ad3a014fc580ff4895cd50a5674891d074078e885bab57b2f6
7
+ data.tar.gz: 0f0b0289cff08d84e3d306ad4f6547107b3eceb3a5cfdf450ea56d543326e2ce54545f762e545e2ba0ce7ced48bc13416866d9d1172587e678c5cd6623cedf94
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Rob Fors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # Ruby Gem: FiberRecycling
2
+ This gem offers a ducktype 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`.
@@ -0,0 +1,8 @@
1
+ module FiberRecycling
2
+ module DuckTypes
3
+
4
+ Fiber = Fiber
5
+ FiberError = FiberError
6
+
7
+ end
8
+ end
@@ -0,0 +1,51 @@
1
+ module FiberRecycling
2
+ class Fiber
3
+
4
+ def self.current
5
+ Thread.current[:fiber_recycling__fiber] || root
6
+ end
7
+
8
+ def self.root
9
+ unless Thread.current.thread_variable_get(:fiber_recycling__root_fiber)
10
+ Thread.current.thread_variable_set(:fiber_recycling__root_fiber, new(RootFiberBackend.new))
11
+ end
12
+ Thread.current.thread_variable_get(:fiber_recycling__root_fiber)
13
+ end
14
+
15
+ def self.yield(*args)
16
+ current.backend.class.yield(*args)
17
+ end
18
+
19
+ attr_reader :backend
20
+
21
+ def initialize(backend = nil, &block)
22
+ if backend && backend.is_a?(FiberBackend)
23
+ @backend = backend
24
+ else
25
+ raise ArgumentError, 'must pass a block' unless block_given?
26
+ @backend = NormalFiberBackend.new(self, block)
27
+ end
28
+ end
29
+
30
+ def alive?
31
+ @backend.alive?
32
+ end
33
+
34
+ def inspect
35
+ to_s
36
+ end
37
+
38
+ def resume(*args)
39
+ @backend.resume(*args)
40
+ end
41
+
42
+ def to_s
43
+ @backend.to_s
44
+ end
45
+
46
+ def transfer(*args)
47
+ @backend.transfer(*args)
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,8 @@
1
+ module FiberRecycling
2
+
3
+ # this class only exists so we can type check backends
4
+ class FiberBackend
5
+
6
+ end
7
+
8
+ end
@@ -0,0 +1,4 @@
1
+ module FiberRecycling
2
+ class FiberError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,52 @@
1
+ module FiberRecycling
2
+ class NormalFiberBackend < FiberBackend
3
+
4
+ def self.yield(*args)
5
+ @state = 'suspended'
6
+ return_value = RecycledFiber.yield(*args)
7
+ @state = 'resumed'
8
+ return_value
9
+ end
10
+
11
+ def initialize(fiber, block)
12
+ @state = 'created'
13
+ @recycled_fiber = RecycledFiberPool.local.release_recycled_fiber
14
+ @recycled_fiber.run { execute(fiber, block) }
15
+ end
16
+
17
+ def alive?
18
+ @state != 'terminated'
19
+ end
20
+
21
+ def inspect
22
+ to_s
23
+ end
24
+
25
+ def resume(*args)
26
+ raise FiberError, 'dead fiber called' unless alive?
27
+ @recycled_fiber.resume(*args)
28
+ end
29
+
30
+ def to_s
31
+ "#<RecycledFiber::Fiber:#{object_hexid} (#{@state})>"
32
+ end
33
+
34
+ def transfer(*args)
35
+ raise FiberError, 'dead fiber called' unless alive?
36
+ @recycled_fiber.transfer(*args)
37
+ end
38
+
39
+ private
40
+
41
+ def execute(fiber, block)
42
+ args = RecycledFiber.yield
43
+ Thread.current[:fiber_recycling__fiber] = fiber
44
+ return_value = block.call(*args)
45
+ Thread.current[:fiber_recycling__fiber] = nil
46
+ @state = 'terminated'
47
+ RecycledFiberPool.local.absorb_recycled_fiber(@recycled_fiber)
48
+ return_value
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,66 @@
1
+ module FiberRecycling
2
+ class RecycledFiber
3
+
4
+ def self.yield(*args)
5
+ NativeFiber.yield(*args)
6
+ end
7
+
8
+ def initialize
9
+ @native_fiber = NativeFiber.new { execution_loop }
10
+ @state = :initialized
11
+ start
12
+ end
13
+
14
+ def close
15
+ raise 'can not close, currently running a block' unless @state == :waiting_for_instruction
16
+ @native_fiber.resume(:close)
17
+ nil
18
+ end
19
+
20
+ def run(*args, &block)
21
+ raise 'can not run, already running a block' unless @state == :waiting_for_instruction
22
+ @native_fiber.resume(:run)
23
+ @native_fiber.resume(args)
24
+ block_reutrn_value = @native_fiber.resume(block)
25
+ block_reutrn_value
26
+ end
27
+
28
+ def execution_loop
29
+ last_reutrn_value = nil
30
+ loop do
31
+ @state = :waiting_for_instruction
32
+ instruction = NativeFiber.yield(last_reutrn_value)
33
+ case instruction
34
+ when :run
35
+ @state = :executing_block
36
+ args = NativeFiber.yield
37
+ proc = NativeFiber.yield
38
+ last_reutrn_value = proc.call(*args)
39
+ when :close
40
+ @state = :closed
41
+ break
42
+ else
43
+ raise 'invalid instruction'
44
+ end
45
+ end
46
+ end
47
+
48
+ def resume(*args)
49
+ raise 'can not resume, not running a block' unless @state == :executing_block
50
+ @native_fiber.resume(*args)
51
+ end
52
+
53
+ def transfer(*args)
54
+ raise 'can not transfer, not running a block' unless @state == :executing_block
55
+ @native_fiber.transfer(*args)
56
+ end
57
+
58
+ private
59
+
60
+ def start
61
+ @native_fiber.resume
62
+ nil
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,24 @@
1
+ module FiberRecycling
2
+ class RecycledFiberPool < QuackPool
3
+
4
+ def self.local
5
+ unless Thread.current.thread_variable_get(:fiber_recycling__recycled_fiber_pool)
6
+ Thread.current.thread_variable_set(:fiber_recycling__recycled_fiber_pool, new)
7
+ end
8
+ Thread.current.thread_variable_get(:fiber_recycling__recycled_fiber_pool)
9
+ end
10
+
11
+ def initialize
12
+ super(resource_class: RecycledFiber)
13
+ end
14
+
15
+ def release_recycled_fiber
16
+ release_resource
17
+ end
18
+
19
+ def absorb_recycled_fiber(recycled_fiber)
20
+ absorb_resource(recycled_fiber)
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ module FiberRecycling
2
+ class RootFiberBackend < FiberBackend
3
+
4
+ def self.yield(*args)
5
+ raise FiberError, "can't yield from root fiber"
6
+ end
7
+
8
+ def alive?
9
+ NativeFiber.root.alive?
10
+ end
11
+
12
+ def inspect
13
+ NativeFiber.root.inspect
14
+ end
15
+
16
+ def resume(*args)
17
+ NativeFiber.root.resume(*args)
18
+ end
19
+
20
+ def to_s
21
+ "#<RecycledFiber::Fiber:#{object_hexid}>"
22
+ end
23
+
24
+ def transfer(*args)
25
+ NativeFiber.root.transfer(*args)
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ require 'thread'
2
+ require 'fiber'
3
+ require 'quack_pool'
4
+ require 'thread_root_fiber'
5
+
6
+ require 'fiber_recycling/fiber'
7
+ require 'fiber_recycling/fiber_backend'
8
+ require 'fiber_recycling/fiber_error'
9
+ require 'fiber_recycling/duck_types'
10
+ require 'fiber_recycling/normal_fiber_backend'
11
+ require 'fiber_recycling/recycled_fiber'
12
+ require 'fiber_recycling/recycled_fiber_pool'
13
+ require 'fiber_recycling/root_fiber_backend'
14
+
15
+ module FiberRecycling
16
+
17
+ NativeFiber = ::Fiber
18
+
19
+ end
@@ -0,0 +1,138 @@
1
+ require 'fiber_recycling'
2
+
3
+ RSpec.describe FiberRecycling::Fiber do
4
+
5
+ describe "#resume" do
6
+
7
+ context "when called on fiber that does not yield" do
8
+ it "should return last value" do
9
+ fiber = FiberRecycling::Fiber.new { 1 }
10
+ last_value = fiber.resume
11
+ expect(last_value).to eql 1
12
+ end
13
+ end
14
+
15
+ context "when called for the fist time" do
16
+ it "should use argument passed to 'resume' as block parameter" do
17
+ fiber = FiberRecycling::Fiber.new { |a| a }
18
+ expect(fiber.resume(1)).to eql 1
19
+ end
20
+
21
+ it "should use arguments passed to it as block parameters" do
22
+ fiber = FiberRecycling::Fiber.new { |a, b| [a, b] }
23
+ expect(fiber.resume(1,2)).to eql [1, 2]
24
+ end
25
+ end
26
+
27
+ context "when called on fiber that yields" do
28
+ it "should return argument passed to yield and 'yield' should return argument passed to 'resume'" do
29
+ fiber = FiberRecycling::Fiber.new do |a|
30
+ FiberRecycling::Fiber.yield(a)
31
+ end
32
+ expect(fiber.resume(1)).to eql 1
33
+ end
34
+ end
35
+
36
+ context "when called on dead fiber" do
37
+ it "should raise FiberError" do
38
+ fiber = FiberRecycling::Fiber.new {}
39
+ fiber.resume
40
+ expect{ fiber.resume }.to raise_error FiberRecycling::FiberError
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ describe "::current" do
47
+
48
+ context "when not in a fiber" do
49
+ it "should return a fiber" do
50
+ fiber = FiberRecycling::Fiber.current
51
+ expect(fiber).to be_a(FiberRecycling::Fiber)
52
+ end
53
+
54
+ it "should return root fiber" do
55
+ fiber = FiberRecycling::Fiber.current
56
+ expect(fiber).to eql FiberRecycling::Fiber.root
57
+ end
58
+ end
59
+
60
+ context "when in a fiber" do
61
+ it "should return a fiber" do
62
+ fiber = FiberRecycling::Fiber.new do
63
+ FiberRecycling::Fiber.current
64
+ end
65
+ expect(fiber.resume).to be_a(FiberRecycling::Fiber)
66
+ end
67
+
68
+ it "should return current fiber" do
69
+ fiber = FiberRecycling::Fiber.new do
70
+ FiberRecycling::Fiber.current
71
+ end
72
+ expect(fiber.resume).to eql fiber
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ describe "::yield" do
79
+
80
+ context "when called in root fiber" do
81
+ it "should raise FiberError" do
82
+ expect{ FiberRecycling::Fiber.yield }.to raise_error FiberRecycling::FiberError
83
+ end
84
+ end
85
+
86
+ end
87
+
88
+ describe "#alive?" do
89
+
90
+ context "when called on new fiber" do
91
+ it "should return true" do
92
+ fiber = FiberRecycling::Fiber.new do
93
+ FiberRecycling::Fiber.yield
94
+ end
95
+ expect(fiber.alive?).to eql true
96
+ end
97
+ end
98
+
99
+ context "when called on yielded fiber" do
100
+ it "should return true" do
101
+ fiber = FiberRecycling::Fiber.new do
102
+ FiberRecycling::Fiber.yield
103
+ end
104
+ fiber.resume
105
+ expect(fiber.alive?).to eql true
106
+ end
107
+ end
108
+
109
+ context "when called on completed fiber" do
110
+ it "should return false" do
111
+ fiber = FiberRecycling::Fiber.new {}
112
+ fiber.resume
113
+ expect(fiber.alive?).to eql false
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+
120
+ describe "#transfer" do
121
+
122
+ context "when called from within a fiber" do
123
+ it "should resume the callee fiber" do
124
+ fiber1 = FiberRecycling::Fiber.new do
125
+ Fiber.yield(1)
126
+ end
127
+
128
+ fiber2 = Fiber.new do
129
+ fiber1.transfer
130
+ 2
131
+ end
132
+
133
+ expect(fiber2.resume).to eql 1
134
+ end
135
+ end
136
+
137
+ end
138
+ end
@@ -0,0 +1,100 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
16
+ RSpec.configure do |config|
17
+ # rspec-expectations config goes here. You can use an alternate
18
+ # assertion/expectation library such as wrong or the stdlib/minitest
19
+ # assertions if you prefer.
20
+ config.expect_with :rspec do |expectations|
21
+ # This option will default to `true` in RSpec 4. It makes the `description`
22
+ # and `failure_message` of custom matchers include text for helper methods
23
+ # defined using `chain`, e.g.:
24
+ # be_bigger_than(2).and_smaller_than(4).description
25
+ # # => "be bigger than 2 and smaller than 4"
26
+ # ...rather than:
27
+ # # => "be bigger than 2"
28
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
29
+ end
30
+
31
+ # rspec-mocks config goes here. You can use an alternate test double
32
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
33
+ config.mock_with :rspec do |mocks|
34
+ # Prevents you from mocking or stubbing a method that does not exist on
35
+ # a real object. This is generally recommended, and will default to
36
+ # `true` in RSpec 4.
37
+ mocks.verify_partial_doubles = true
38
+ end
39
+
40
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
41
+ # have no way to turn it off -- the option exists only for backwards
42
+ # compatibility in RSpec 3). It causes shared context metadata to be
43
+ # inherited by the metadata hash of host groups and examples, rather than
44
+ # triggering implicit auto-inclusion in groups with matching metadata.
45
+ config.shared_context_metadata_behavior = :apply_to_host_groups
46
+
47
+ # The settings below are suggested to provide a good initial experience
48
+ # with RSpec, but feel free to customize to your heart's content.
49
+ =begin
50
+ # This allows you to limit a spec run to individual examples or groups
51
+ # you care about by tagging them with `:focus` metadata. When nothing
52
+ # is tagged with `:focus`, all examples get run. RSpec also provides
53
+ # aliases for `it`, `describe`, and `context` that include `:focus`
54
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
55
+ config.filter_run_when_matching :focus
56
+
57
+ # Allows RSpec to persist some state between runs in order to support
58
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
59
+ # you configure your source control system to ignore this file.
60
+ config.example_status_persistence_file_path = "spec/examples.txt"
61
+
62
+ # Limits the available syntax to the non-monkey patched syntax that is
63
+ # recommended. For more details, see:
64
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
65
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
66
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
67
+ config.disable_monkey_patching!
68
+
69
+ # This setting enables warnings. It's recommended, but in some cases may
70
+ # be too noisy due to issues in dependencies.
71
+ config.warnings = true
72
+
73
+ # Many RSpec users commonly either run the entire suite or an individual
74
+ # file, and it's useful to allow more verbose output when running an
75
+ # individual spec file.
76
+ if config.files_to_run.one?
77
+ # Use the documentation formatter for detailed output,
78
+ # unless a formatter has already been configured
79
+ # (e.g. via a command-line flag).
80
+ config.default_formatter = "doc"
81
+ end
82
+
83
+ # Print the 10 slowest examples and example groups at the
84
+ # end of the spec run, to help surface which specs are running
85
+ # particularly slow.
86
+ config.profile_examples = 10
87
+
88
+ # Run specs in random order to surface order dependencies. If you find an
89
+ # order dependency and want to debug it, you can fix the order by providing
90
+ # the seed, which is printed after each run.
91
+ # --seed 1234
92
+ config.order = :random
93
+
94
+ # Seed global randomization in this process using the `--seed` CLI option.
95
+ # Setting this allows you to use `--seed` to deterministically reproduce
96
+ # test failures related to randomization by passing the same `--seed` value
97
+ # as the one that triggered the failure.
98
+ Kernel.srand config.seed
99
+ =end
100
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fiber_recycling
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Rob Fors
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-04-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thread_root_fiber
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ description: Duck type for Fiber that will reuse old native Fibers after they have
28
+ finished executing their block. Reusing old Fibers will offer a small performance
29
+ gain.
30
+ email: mail@robfors.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - LICENSE
36
+ - README.md
37
+ - lib/fiber_recycling.rb
38
+ - lib/fiber_recycling/duck_types.rb
39
+ - lib/fiber_recycling/fiber.rb
40
+ - lib/fiber_recycling/fiber_backend.rb
41
+ - lib/fiber_recycling/fiber_error.rb
42
+ - lib/fiber_recycling/normal_fiber_backend.rb
43
+ - lib/fiber_recycling/recycled_fiber.rb
44
+ - lib/fiber_recycling/recycled_fiber_pool.rb
45
+ - lib/fiber_recycling/root_fiber_backend.rb
46
+ - spec/fiber_spec.rb
47
+ - spec/spec_helper.rb
48
+ homepage: https://github.com/robfors/fiber_recycling
49
+ licenses:
50
+ - MIT
51
+ metadata: {}
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 2.4.8
69
+ signing_key:
70
+ specification_version: 4
71
+ summary: Recycles old Fibers for performance
72
+ test_files: []