Class: PhusionPassenger::AbstractServerCollection
- Includes:
- Utils
- Defined in:
- lib/phusion_passenger/abstract_server_collection.rb
Overview
This class maintains a collection of AbstractServer objects. One can add new AbstractServer objects, or look up existing ones via a key. AbstractServerCollection also automatically takes care of cleaning up AbstractServers that have been idle for too long.
This class exists because both SpawnManager and ClassicRails::FrameworkSpawner need this kind of functionality. SpawnManager maintains a collection of ClassicRails::FrameworkSpawner and ClassicRails::ApplicationSpawner objects, while ClassicRails::FrameworkSpawner maintains a collection of ClassicRails::ApplicationSpawner objects.
This class is thread-safe as long as the specified thread-safety rules are followed.
Instance Attribute Summary collapse
-
#next_cleaning_time ⇒ Object
readonly
Returns the value of attribute next_cleaning_time.
Instance Method Summary collapse
-
#check_idle_servers! ⇒ Object
Tell the cleaner thread to check the collection as soon as possible, instead of sleeping until the next scheduled cleaning time.
-
#cleanup ⇒ Object
Cleanup all resources used by this AbstractServerCollection.
-
#clear ⇒ Object
Delete all AbstractServers from the collection.
-
#delete(key) ⇒ Object
Deletes from the collection the AbstractServer that’s associated with the given key.
-
#each ⇒ Object
Iterate over all AbstractServer objects.
-
#each_pair ⇒ Object
Iterate over all keys and associated AbstractServer objects.
-
#empty? ⇒ Boolean
Checks whether the collection is empty.
-
#has_key?(key) ⇒ Boolean
Checks whether there’s an AbstractServer object associated with the given key.
-
#initialize ⇒ AbstractServerCollection
constructor
A new instance of AbstractServerCollection.
-
#lookup_or_add(key) ⇒ Object
Lookup and returns an AbstractServer with the given key.
-
#register_activity(server) ⇒ Object
Notify this AbstractServerCollection that
server
has performed an activity. -
#synchronize ⇒ Object
Acquire the lock for this AbstractServerCollection object, and run the code within the block.
Methods included from Utils
Constructor Details
#initialize ⇒ AbstractServerCollection
Returns a new instance of AbstractServerCollection.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/phusion_passenger/abstract_server_collection.rb', line 44 def initialize @collection = {} @lock = Mutex.new @cleanup_lock = Mutex.new @cond = ConditionVariable.new @done = false # The next time the cleaner thread should check for idle servers. # The value may be nil, in which case the value will be calculated # at the end of the #synchronized block. # # Invariant: # if value is not nil: # There exists an s in @collection with s.next_cleaning_time == value. # for all s in @collection: # if eligable_for_cleanup?(s): # s.next_cleaning_time <= value @next_cleaning_time = Time.now + 60 * 60 @next_cleaning_time_changed = false @cleaner_thread = Thread.new do begin @lock.synchronize do cleaner_thread_main end rescue Exception => e print_exception(self.class.to_s, e) end end end |
Instance Attribute Details
#next_cleaning_time ⇒ Object (readonly)
Returns the value of attribute next_cleaning_time.
40 41 42 |
# File 'lib/phusion_passenger/abstract_server_collection.rb', line 40 def next_cleaning_time @next_cleaning_time end |
Instance Method Details
#check_idle_servers! ⇒ Object
Tell the cleaner thread to check the collection as soon as possible, instead of sleeping until the next scheduled cleaning time.
Precondition: this method must NOT be called within a #synchronize block.
208 209 210 211 212 213 214 |
# File 'lib/phusion_passenger/abstract_server_collection.rb', line 208 def check_idle_servers! must_not_be_in_synchronize_block @lock.synchronize do @next_cleaning_time = Time.now - 60 * 60 @cond.signal end end |
#cleanup ⇒ Object
Cleanup all resources used by this AbstractServerCollection. All AbstractServers from the collection will be deleted. Each AbstractServer will be stopped, if necessary. The background thread which removes idle AbstractServers will be stopped.
After calling this method, this AbstractServerCollection object will become unusable.
Precondition: this method must NOT be called within a #synchronize block.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/phusion_passenger/abstract_server_collection.rb', line 260 def cleanup must_not_be_in_synchronize_block @cleanup_lock.synchronize do return if @done @lock.synchronize do @done = true @cond.signal end @cleaner_thread.join synchronize do clear end end end |
#clear ⇒ Object
Delete all AbstractServers from the collection. Each AbstractServer will be stopped, if necessary.
Precondition: this method must be called within a #synchronize block.
241 242 243 244 245 246 247 248 249 250 |
# File 'lib/phusion_passenger/abstract_server_collection.rb', line 241 def clear must_be_in_synchronize_block @collection.each_value do |server| if server.started? server.stop end end @collection.clear @next_cleaning_time = nil end |
#delete(key) ⇒ Object
Deletes from the collection the AbstractServer that’s associated with the given key. If no such AbstractServer exists, nothing will happen.
If the AbstractServer is started, then it will be stopped before deletion.
Precondition: this method must be called within a #synchronize block.
170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/phusion_passenger/abstract_server_collection.rb', line 170 def delete(key) raise ArgumentError, "cleanup() has already been called." if @done must_be_in_synchronize_block server = @collection[key] if server if server.started? server.stop end @collection.delete(key) if server.next_cleaning_time == @next_cleaning_time @next_cleaning_time = nil end end end |
#each ⇒ Object
Iterate over all AbstractServer objects.
Precondition: this method must be called within a #synchronize block.
219 220 221 222 223 224 |
# File 'lib/phusion_passenger/abstract_server_collection.rb', line 219 def each must_be_in_synchronize_block each_pair do |key, server| yield server end end |
#each_pair ⇒ Object
Iterate over all keys and associated AbstractServer objects.
Precondition: this method must be called within a #synchronize block.
229 230 231 232 233 234 235 |
# File 'lib/phusion_passenger/abstract_server_collection.rb', line 229 def each_pair raise ArgumentError, "cleanup() has already been called." if @done must_be_in_synchronize_block @collection.each_pair do |key, server| yield(key, server) end end |
#empty? ⇒ Boolean
Checks whether the collection is empty.
Precondition: this method must be called within a #synchronize block.
159 160 161 162 |
# File 'lib/phusion_passenger/abstract_server_collection.rb', line 159 def empty? must_be_in_synchronize_block return @collection.empty? end |
#has_key?(key) ⇒ Boolean
Checks whether there’s an AbstractServer object associated with the given key.
Precondition: this method must be called within a #synchronize block.
151 152 153 154 |
# File 'lib/phusion_passenger/abstract_server_collection.rb', line 151 def has_key?(key) must_be_in_synchronize_block return @collection.has_key?(key) end |
#lookup_or_add(key) ⇒ Object
Lookup and returns an AbstractServer with the given key.
If there is no AbstractSerer associated with the given key, then the given block will be called. That block must return an AbstractServer object. Then, that object will be stored in the collection, and returned.
The block must set the ‘max_idle_time’ attribute on the AbstractServer. AbstractServerCollection’s idle cleaning interval will be adapted to accomodate with this. Changing the value outside this block is not guaranteed to have any effect on the idle cleaning interval. A max_idle_time value of nil or 0 means the AbstractServer will never be idle cleaned.
If the block raises an exception, then the collection will not be modified, and the exception will be propagated.
Precondition: this method must be called within a #synchronize block.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/phusion_passenger/abstract_server_collection.rb', line 124 def lookup_or_add(key) raise ArgumentError, "cleanup() has already been called." if @done must_be_in_synchronize_block server = @collection[key] if server register_activity(server) return server else server = yield if !server.respond_to?(:start) raise TypeError, "The block didn't return a valid AbstractServer object." end if eligable_for_cleanup?(server) server.next_cleaning_time = Time.now + server.max_idle_time if @next_cleaning_time && server.next_cleaning_time < @next_cleaning_time @next_cleaning_time = server.next_cleaning_time @next_cleaning_time_changed = true end end @collection[key] = server return server end end |
#register_activity(server) ⇒ Object
Notify this AbstractServerCollection that server
has performed an activity. This AbstractServerCollection will update the idle information associated with server
accordingly.
lookup_or_add already automatically updates idle information, so you only need to call this method if the time at which the server has performed an activity is not close to the time at which lookup_or_add had been called.
Precondition: this method must be called within a #synchronize block.
194 195 196 197 198 199 200 201 202 |
# File 'lib/phusion_passenger/abstract_server_collection.rb', line 194 def register_activity(server) must_be_in_synchronize_block if eligable_for_cleanup?(server) if server.next_cleaning_time == @next_cleaning_time @next_cleaning_time = nil end server.next_cleaning_time = Time.now + server.max_idle_time end end |
#synchronize ⇒ Object
Acquire the lock for this AbstractServerCollection object, and run the code within the block. The entire block will be a single atomic operation.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/phusion_passenger/abstract_server_collection.rb', line 78 def synchronize @lock.synchronize do @in_synchronize_block = true begin yield ensure if @next_cleaning_time.nil? @collection.each_value do |server| if @next_cleaning_time.nil? || (eligable_for_cleanup?(server) && server.next_cleaning_time < @next_cleaning_time ) @next_cleaning_time = server.next_cleaning_time end end if @next_cleaning_time.nil? # There are no servers in the collection with an idle timeout. @next_cleaning_time = Time.now + 60 * 60 end @next_cleaning_time_changed = true end if @next_cleaning_time_changed @next_cleaning_time_changed = false @cond.signal end @in_synchronize_block = false end end end |