fiber_pool 0.9.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 +4 -0
- data/Gemfile +4 -0
- data/README.md +45 -0
- data/Rakefile +2 -0
- data/fiber_pool.gemspec +23 -0
- data/lib/fiber_pool/fiber18.rb +83 -0
- data/lib/fiber_pool/pool.rb +95 -0
- data/lib/fiber_pool/version.rb +3 -0
- data/lib/fiber_pool.rb +7 -0
- metadata +64 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# What is this ?
|
|
2
|
+
|
|
3
|
+
It is a basic implementation of a fiber pool extracted for my projects,
|
|
4
|
+
I need a pool in many project and I am tired of having to reimplement it
|
|
5
|
+
in every project so here goes my gem !
|
|
6
|
+
|
|
7
|
+
My implementation is based on the neverblock one but I added some changes of
|
|
8
|
+
my own over time.
|
|
9
|
+
|
|
10
|
+
# Features
|
|
11
|
+
|
|
12
|
+
- Constant count of fibers with a queue (I am sure you would have never guessed ;) )
|
|
13
|
+
- should any uncaught error occur in one fiber of the poll then another one
|
|
14
|
+
will be spawned to keep the pool state (you should not let errors uncaught but
|
|
15
|
+
my experience clearly shows me that it can happen and it can trigger really weird bug
|
|
16
|
+
if your poll runs out of fibers)
|
|
17
|
+
|
|
18
|
+
# Usage
|
|
19
|
+
|
|
20
|
+
I use it mainly with EventMachine but it is in no way required, that said it may not be
|
|
21
|
+
of much use without, I did not gave it much thoughts :)
|
|
22
|
+
|
|
23
|
+
# create a pool contained 10 fibers
|
|
24
|
+
pool = FiberPool.new(10)
|
|
25
|
+
|
|
26
|
+
# and spawn a job
|
|
27
|
+
# if a fiber is free it will be executed immediatly
|
|
28
|
+
# otherwise the block is queued
|
|
29
|
+
pool.spawn{ do_something() }
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Warning
|
|
33
|
+
|
|
34
|
+
If you use this You should already be aware of how fibers work, If not go read about them
|
|
35
|
+
and keep in mind that when a Fiber is running nothing else will, they are like threads but
|
|
36
|
+
you are responsible for the scheduling.
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Supported Runtimes
|
|
40
|
+
|
|
41
|
+
- MRI 1.8.6+ (in ruby 1.8 the thread based fiber implementation will be used)
|
|
42
|
+
- Rubinius 1.2.2+ (fiber support is still experimental so be careful what you do this it here)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
data/Rakefile
ADDED
data/fiber_pool.gemspec
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
+
require "fiber_pool/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = "fiber_pool"
|
|
7
|
+
s.version = FiberPool::VERSION
|
|
8
|
+
s.platform = Gem::Platform::RUBY
|
|
9
|
+
s.authors = ["Julien Ammmous"]
|
|
10
|
+
s.email = []
|
|
11
|
+
s.homepage = ""
|
|
12
|
+
s.summary = %q{Fiber Pool Implementation}
|
|
13
|
+
s.description = %q{A General purpose fiber pool}
|
|
14
|
+
|
|
15
|
+
s.rubyforge_project = "fiber_pool"
|
|
16
|
+
|
|
17
|
+
s.files = `git ls-files`.split("\n")
|
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
20
|
+
s.require_paths = ["lib"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
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 alive?
|
|
37
|
+
@thread.alive?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.yield *args
|
|
41
|
+
if fiber = Thread.current[:fiber]
|
|
42
|
+
fiber.yield(*args)
|
|
43
|
+
else
|
|
44
|
+
raise FiberError, 'not inside a fiber'
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.current
|
|
49
|
+
if Thread.current == Thread.main
|
|
50
|
+
return Thread.main[:fiber] ||= RootFiber.new
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
Thread.current[:fiber] or raise FiberError, 'not inside a fiber'
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def inspect
|
|
57
|
+
"#<#{self.class}:0x#{self.object_id.to_s(16)}>"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class RootFiber < Fiber
|
|
62
|
+
def initialize
|
|
63
|
+
# XXX: what is a root fiber anyway?
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.yield *args
|
|
67
|
+
raise FiberError, "can't yield from root fiber"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
if __FILE__ == $0
|
|
73
|
+
f = Fiber.new{ |sym|
|
|
74
|
+
p(sym)
|
|
75
|
+
puts 'hi'
|
|
76
|
+
p(Fiber.yield 1)
|
|
77
|
+
puts 'bye'
|
|
78
|
+
:end
|
|
79
|
+
}
|
|
80
|
+
p(f.resume :begin)
|
|
81
|
+
p(f.resume 2)
|
|
82
|
+
end
|
|
83
|
+
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'fiber'
|
|
3
|
+
if defined?(Rubinius::Fiber)
|
|
4
|
+
Fiber = Rubinius::Fiber
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
rescue LoadError
|
|
8
|
+
require File.expand_path('../fiber18', __FILE__)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class FiberPool
|
|
12
|
+
# Prepare a list of fibers that are able to run different blocks of code
|
|
13
|
+
# every time. Once a fiber is done with its block, it attempts to fetch
|
|
14
|
+
# another one from the queue
|
|
15
|
+
def initialize(count = 100)
|
|
16
|
+
@fibers,@busy_fibers,@queue = [],{},[]
|
|
17
|
+
|
|
18
|
+
count.times do |i|
|
|
19
|
+
add_fiber()
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def add_fiber
|
|
24
|
+
fiber = Fiber.new do |block|
|
|
25
|
+
loop do
|
|
26
|
+
block.call
|
|
27
|
+
unless @queue.empty?
|
|
28
|
+
block = @queue.shift
|
|
29
|
+
else
|
|
30
|
+
@busy_fibers.delete(Fiber.current.object_id)
|
|
31
|
+
@fibers.unshift Fiber.current
|
|
32
|
+
block = Fiber.yield
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
@fibers << fiber
|
|
38
|
+
fiber
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# If there is an available fiber use it, otherwise, leave it to linger
|
|
42
|
+
# in a queue
|
|
43
|
+
def spawn(&block)
|
|
44
|
+
# resurrect dead fibers
|
|
45
|
+
@busy_fibers.values.reject(&:alive?).each do |f|
|
|
46
|
+
@busy_fibers.delete(f.object_id)
|
|
47
|
+
add_fiber()
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
if fiber = @fibers.shift
|
|
51
|
+
@busy_fibers[fiber.object_id] = fiber
|
|
52
|
+
fiber.resume(block)
|
|
53
|
+
else
|
|
54
|
+
@queue << block
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
fiber
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if $0 == __FILE__
|
|
64
|
+
pool = FiberPool.new(2)
|
|
65
|
+
f1 = pool.spawn do
|
|
66
|
+
puts "1"
|
|
67
|
+
puts Fiber.yield
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
begin
|
|
71
|
+
pool.spawn do
|
|
72
|
+
puts "2"
|
|
73
|
+
f1.resume(42)
|
|
74
|
+
raise 'arggggg'
|
|
75
|
+
end
|
|
76
|
+
rescue => err
|
|
77
|
+
p err.inspect
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
begin
|
|
81
|
+
pool.spawn do
|
|
82
|
+
raise 'arggggg 222'
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
rescue => err
|
|
86
|
+
p err.inspect
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
pool.spawn do
|
|
91
|
+
puts "I am alive !!!"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
data/lib/fiber_pool.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: fiber_pool
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
prerelease:
|
|
5
|
+
version: 0.9.0
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Julien Ammmous
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
|
|
13
|
+
date: 2011-04-24 00:00:00 Z
|
|
14
|
+
dependencies: []
|
|
15
|
+
|
|
16
|
+
description: A General purpose fiber pool
|
|
17
|
+
email: []
|
|
18
|
+
|
|
19
|
+
executables: []
|
|
20
|
+
|
|
21
|
+
extensions: []
|
|
22
|
+
|
|
23
|
+
extra_rdoc_files: []
|
|
24
|
+
|
|
25
|
+
files:
|
|
26
|
+
- .gitignore
|
|
27
|
+
- Gemfile
|
|
28
|
+
- README.md
|
|
29
|
+
- Rakefile
|
|
30
|
+
- fiber_pool.gemspec
|
|
31
|
+
- lib/fiber_pool.rb
|
|
32
|
+
- lib/fiber_pool/fiber18.rb
|
|
33
|
+
- lib/fiber_pool/pool.rb
|
|
34
|
+
- lib/fiber_pool/version.rb
|
|
35
|
+
homepage: ""
|
|
36
|
+
licenses: []
|
|
37
|
+
|
|
38
|
+
post_install_message:
|
|
39
|
+
rdoc_options: []
|
|
40
|
+
|
|
41
|
+
require_paths:
|
|
42
|
+
- lib
|
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
44
|
+
none: false
|
|
45
|
+
requirements:
|
|
46
|
+
- - ">="
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: "0"
|
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
50
|
+
none: false
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: "0"
|
|
55
|
+
requirements: []
|
|
56
|
+
|
|
57
|
+
rubyforge_project: fiber_pool
|
|
58
|
+
rubygems_version: 1.7.2
|
|
59
|
+
signing_key:
|
|
60
|
+
specification_version: 3
|
|
61
|
+
summary: Fiber Pool Implementation
|
|
62
|
+
test_files: []
|
|
63
|
+
|
|
64
|
+
has_rdoc:
|