innertube 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.
@@ -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/).
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Innertube
2
+ VERSION = "1.0.0"
3
+ 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: []