innertube 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/.gitignore +26 -0
- data/Gemfile +1 -0
- data/LICENSE +16 -0
- data/README.md +55 -0
- data/innertube.gemspec +18 -0
- data/lib/innertube.rb +188 -0
- data/lib/innertube/version.rb +3 -0
- metadata +54 -0
data/.gitignore
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## MAC OS
|
|
2
|
+
.DS_Store
|
|
3
|
+
|
|
4
|
+
## TEXTMATE
|
|
5
|
+
*.tmproj
|
|
6
|
+
tmtags
|
|
7
|
+
|
|
8
|
+
## EMACS
|
|
9
|
+
*~
|
|
10
|
+
\#*
|
|
11
|
+
.\#*
|
|
12
|
+
|
|
13
|
+
## VIM
|
|
14
|
+
*.swp
|
|
15
|
+
|
|
16
|
+
## PROJECT::GENERAL
|
|
17
|
+
coverage
|
|
18
|
+
rdoc
|
|
19
|
+
pkg
|
|
20
|
+
|
|
21
|
+
## PROJECT::SPECIFIC
|
|
22
|
+
.bundle
|
|
23
|
+
Gemfile.lock
|
|
24
|
+
**/bin
|
|
25
|
+
*.rbc
|
|
26
|
+
.rvmrc
|
data/Gemfile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
source "http://rubygems.org"
|
data/LICENSE
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Copyright 2011-2012 Sean Cribbs, Kyle Kingsbury and Basho Technologies, Inc.
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
|
14
|
+
|
|
15
|
+
All of the files in this project are under the project-wide license
|
|
16
|
+
unless they are otherwise marked.
|
data/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Innertube
|
|
2
|
+
|
|
3
|
+
Innertube is a thread-safe, re-entrant resource pool, extracted from
|
|
4
|
+
the [Riak Ruby Client](/basho/riak-ruby-client), where it was used to
|
|
5
|
+
pool connections to [Riak](/basho/riak). It is free to use and modify,
|
|
6
|
+
licensed under the Apache 2.0 License.
|
|
7
|
+
|
|
8
|
+
## Example
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
# -------------------------------------------------------
|
|
12
|
+
# Basics
|
|
13
|
+
# -------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
# Create a pool with open/close callables
|
|
16
|
+
pool = Innertube::Pool.new(proc { Connection.new },
|
|
17
|
+
proc {|c| c.disconnect })
|
|
18
|
+
|
|
19
|
+
# Grab a connection from the pool, returns the same value
|
|
20
|
+
# as the block
|
|
21
|
+
pool.take {|conn| conn.ping } # => true
|
|
22
|
+
|
|
23
|
+
# Raise the BadResource exception if the resource is no
|
|
24
|
+
# longer good
|
|
25
|
+
pool.take do |conn|
|
|
26
|
+
raise Innertube::Pool::BadResource unless conn.connected?
|
|
27
|
+
conn.ping
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Innertube helps your code be re-entrant! Take more resources
|
|
31
|
+
# while you have one checked out.
|
|
32
|
+
pool.take do |conn|
|
|
33
|
+
conn.stream_tweets do |tweet|
|
|
34
|
+
pool.take {|conn2| conn2.increment :tweets }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# -------------------------------------------------------
|
|
39
|
+
# Iterations: These are slow because they have guarantees
|
|
40
|
+
# about visiting all current elements of the pool.
|
|
41
|
+
# -------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
# Do something with every connection in the pool
|
|
44
|
+
pool.each {|conn| puts conn.get_stats }
|
|
45
|
+
|
|
46
|
+
# Expunge some expired connections from the pool
|
|
47
|
+
pool.delete_if {|conn| conn.idle_time > 5 }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Credits
|
|
51
|
+
|
|
52
|
+
The pool was originally implemented by [Kyle Kingsbury](/aphyr) and
|
|
53
|
+
extracted by [Sean Cribbs](/seancribbs), when bugged about it by
|
|
54
|
+
[Pat Allan](/freelancing-god) at
|
|
55
|
+
[EuRuKo 2012](http://www.euruko2012.org/).
|
data/innertube.gemspec
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
2
|
+
require 'innertube/version'
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |gem|
|
|
5
|
+
gem.name = 'innertube'
|
|
6
|
+
gem.version = Innertube::VERSION
|
|
7
|
+
gem.summary = "A thread-safe resource pool, originally borne in riak-client (Ripple)."
|
|
8
|
+
gem.description = "Because everyone needs their own pool library."
|
|
9
|
+
gem.email = [ "sean@basho.com", "aphyr@aphyr.com" ]
|
|
10
|
+
gem.homepage = "http://github.com/basho/innertube"
|
|
11
|
+
gem.authors = ["Sean Cribbs", "Kyle Kingsbury"]
|
|
12
|
+
|
|
13
|
+
# Files
|
|
14
|
+
ignores = File.read(".gitignore").split(/\r?\n/).reject{ |f| f =~ /^(#.+|\s*)$/ }.map {|f| Dir[f] }.flatten
|
|
15
|
+
gem.files = (Dir['**/*','.gitignore'] - ignores).reject {|f| !File.file?(f) }
|
|
16
|
+
# gem.test_files = (Dir['spec/**/*','.gitignore'] - ignores).reject {|f| !File.file?(f) }
|
|
17
|
+
gem.require_paths = ['lib']
|
|
18
|
+
end
|
data/lib/innertube.rb
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
|
|
3
|
+
# Innertube is a re-entrant thread-safe resource pool that was
|
|
4
|
+
# extracted from the Riak Ruby Client
|
|
5
|
+
# (https://github.com/basho/riak-ruby-client).
|
|
6
|
+
# @see Pool
|
|
7
|
+
module Innertube
|
|
8
|
+
# A re-entrant thread-safe resource pool that generates new resources on
|
|
9
|
+
# demand.
|
|
10
|
+
# @private
|
|
11
|
+
class Pool
|
|
12
|
+
# Raised when a taken element should be deleted from the pool.
|
|
13
|
+
class BadResource < RuntimeError; end
|
|
14
|
+
|
|
15
|
+
# An element of the pool. Comprises an object with an owning
|
|
16
|
+
# thread. Not usually needed by user code, and should not be
|
|
17
|
+
# modified outside the {Pool}'s lock.
|
|
18
|
+
class Element
|
|
19
|
+
attr_reader :object, :owner
|
|
20
|
+
|
|
21
|
+
# Creates a pool element
|
|
22
|
+
# @param [Object] object the resource to wrap into the pool element
|
|
23
|
+
def initialize(object)
|
|
24
|
+
@object = object
|
|
25
|
+
@owner = nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Claims this element of the pool for the current Thread.
|
|
29
|
+
# Do not call this manually, it is only used from inside the pool.
|
|
30
|
+
def lock
|
|
31
|
+
self.owner = Thread.current
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [true,false] Is this element locked/claimed?
|
|
35
|
+
def locked?
|
|
36
|
+
!unlocked?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Releases this element of the pool from the current Thread.
|
|
40
|
+
def unlock
|
|
41
|
+
self.owner = nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return [true,false] Is this element available for use?
|
|
45
|
+
def unlocked?
|
|
46
|
+
owner.nil?
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Creates a new resource pool.
|
|
51
|
+
# @param [Proc, #call] open a callable which allocates a new object for the
|
|
52
|
+
# pool
|
|
53
|
+
# @param [Proc, #call] close a callable which is called with an
|
|
54
|
+
# object before it is freed.
|
|
55
|
+
def initialize(open, close)
|
|
56
|
+
@open = open
|
|
57
|
+
@close = close
|
|
58
|
+
@lock = Mutex.new
|
|
59
|
+
@iterator = Mutex.new
|
|
60
|
+
@element_released = ConditionVariable.new
|
|
61
|
+
@pool = Set.new
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# On each element of the pool, calls close(element) and removes it.
|
|
65
|
+
# @private
|
|
66
|
+
def clear
|
|
67
|
+
each_element do |e|
|
|
68
|
+
delete_element e
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
alias :close :clear
|
|
72
|
+
|
|
73
|
+
# Deletes an element of the pool. Calls the close callback on its object.
|
|
74
|
+
# Not intended for external use.
|
|
75
|
+
# @param [Element] e the element to remove from the pool
|
|
76
|
+
def delete_element(e)
|
|
77
|
+
@close.call(e.object)
|
|
78
|
+
@lock.synchronize do
|
|
79
|
+
@pool.delete e
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
private :delete_element
|
|
83
|
+
|
|
84
|
+
# Locks each element in turn and closes/deletes elements for which the
|
|
85
|
+
# object passes the block.
|
|
86
|
+
# @yield [object] a block that should determine whether an element
|
|
87
|
+
# should be deleted from the pool
|
|
88
|
+
# @yieldparam [Object] object the resource
|
|
89
|
+
def delete_if
|
|
90
|
+
raise ArgumentError, "block required" unless block_given?
|
|
91
|
+
|
|
92
|
+
each_element do |e|
|
|
93
|
+
if yield e.object
|
|
94
|
+
delete_element e
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Acquire an element of the pool. Yields the object. If all
|
|
100
|
+
# elements are claimed, it will create another one.
|
|
101
|
+
# @yield [resource] a block that will perform some action with the
|
|
102
|
+
# element of the pool
|
|
103
|
+
# @yieldparam [Object] resource a resource managed by the pool.
|
|
104
|
+
# Locked for the duration of the block
|
|
105
|
+
# @param [Proc, #call] :filter a callable which receives objects and has
|
|
106
|
+
# the opportunity to reject each in turn.
|
|
107
|
+
# @param [Object] :default if no resources are available, use this object
|
|
108
|
+
# instead of calling #open.
|
|
109
|
+
# @private
|
|
110
|
+
def take(opts = {})
|
|
111
|
+
raise ArgumentError, "block required" unless block_given?
|
|
112
|
+
|
|
113
|
+
result = nil
|
|
114
|
+
element = nil
|
|
115
|
+
opts[:filter] ||= proc {|_| true }
|
|
116
|
+
@lock.synchronize do
|
|
117
|
+
element = pool.find { |e| e.unlocked? && opts[:filter].call(e.object) }
|
|
118
|
+
unless element
|
|
119
|
+
# No objects were acceptable
|
|
120
|
+
resource = opts[:default] || @open.call
|
|
121
|
+
element = Element.new(resource)
|
|
122
|
+
@pool << element
|
|
123
|
+
end
|
|
124
|
+
element.lock
|
|
125
|
+
end
|
|
126
|
+
begin
|
|
127
|
+
result = yield element.object
|
|
128
|
+
rescue BadResource
|
|
129
|
+
delete_element element
|
|
130
|
+
raise
|
|
131
|
+
ensure
|
|
132
|
+
# Unlock
|
|
133
|
+
if element
|
|
134
|
+
element.unlock
|
|
135
|
+
@element_released.signal
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
result
|
|
139
|
+
end
|
|
140
|
+
alias >> take
|
|
141
|
+
|
|
142
|
+
# Iterate over a snapshot of the pool. Yielded objects are locked
|
|
143
|
+
# for the duration of the block. This may block the current thread
|
|
144
|
+
# until elements in the snapshot are released by other threads.
|
|
145
|
+
# @yield [element] a block that will do something with each
|
|
146
|
+
# element in the pool
|
|
147
|
+
# @yieldparam [Element] element the current element in the
|
|
148
|
+
# iteration
|
|
149
|
+
def each_element
|
|
150
|
+
targets = @pool.to_a
|
|
151
|
+
unlocked = []
|
|
152
|
+
|
|
153
|
+
@iterator.synchronize do
|
|
154
|
+
until targets.empty?
|
|
155
|
+
@lock.synchronize do
|
|
156
|
+
unlocked, targets = targets.partition {|e| e.unlocked? }
|
|
157
|
+
unlocked.each {|e| e.lock }
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
unlocked.each do |e|
|
|
161
|
+
begin
|
|
162
|
+
yield e
|
|
163
|
+
ensure
|
|
164
|
+
e.unlock
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
@element_released.wait(@iterator) unless targets.empty?
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# As each_element, but yields objects, not wrapper elements.
|
|
173
|
+
# @yield [resource] a block that will do something with each
|
|
174
|
+
# resource in the pool
|
|
175
|
+
# @yieldparam [Object] resource the current resource in the
|
|
176
|
+
# iteration
|
|
177
|
+
def each
|
|
178
|
+
each_element do |e|
|
|
179
|
+
yield e.object
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# @return [Integer] the number of the resources in the pool
|
|
184
|
+
def size
|
|
185
|
+
@lock.synchronize { @pool.size }
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: innertube
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Sean Cribbs
|
|
9
|
+
- Kyle Kingsbury
|
|
10
|
+
autorequire:
|
|
11
|
+
bindir: bin
|
|
12
|
+
cert_chain: []
|
|
13
|
+
date: 2012-06-01 00:00:00.000000000 Z
|
|
14
|
+
dependencies: []
|
|
15
|
+
description: Because everyone needs their own pool library.
|
|
16
|
+
email:
|
|
17
|
+
- sean@basho.com
|
|
18
|
+
- aphyr@aphyr.com
|
|
19
|
+
executables: []
|
|
20
|
+
extensions: []
|
|
21
|
+
extra_rdoc_files: []
|
|
22
|
+
files:
|
|
23
|
+
- Gemfile
|
|
24
|
+
- innertube.gemspec
|
|
25
|
+
- lib/innertube/version.rb
|
|
26
|
+
- lib/innertube.rb
|
|
27
|
+
- LICENSE
|
|
28
|
+
- README.md
|
|
29
|
+
- .gitignore
|
|
30
|
+
homepage: http://github.com/basho/innertube
|
|
31
|
+
licenses: []
|
|
32
|
+
post_install_message:
|
|
33
|
+
rdoc_options: []
|
|
34
|
+
require_paths:
|
|
35
|
+
- lib
|
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
37
|
+
none: false
|
|
38
|
+
requirements:
|
|
39
|
+
- - ! '>='
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '0'
|
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
43
|
+
none: false
|
|
44
|
+
requirements:
|
|
45
|
+
- - ! '>='
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
requirements: []
|
|
49
|
+
rubyforge_project:
|
|
50
|
+
rubygems_version: 1.8.23
|
|
51
|
+
signing_key:
|
|
52
|
+
specification_version: 3
|
|
53
|
+
summary: A thread-safe resource pool, originally borne in riak-client (Ripple).
|
|
54
|
+
test_files: []
|