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.
@@ -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: []