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 Railz::FrameworkSpawner need this kind of functionality. SpawnManager maintains a collection of Railz::FrameworkSpawner and Railz::ApplicationSpawner objects, while Railz::FrameworkSpawner maintains a collection of Railz::ApplicationSpawner objects.
This class is thread-safe as long as the specified thread-safety rules are followed.
- check_idle_servers!
- cleanup
- clear
- delete
- each
- each_pair
- empty?
- has_key?
- lookup_or_add
- new
- register_activity
- synchronize
[R] | next_cleaning_time |
[ show source ]
# File lib/phusion_passenger/abstract_server_collection.rb, line 44 44: def initialize 45: @collection = {} 46: @lock = Mutex.new 47: @cleanup_lock = Mutex.new 48: @cond = ConditionVariable.new 49: @done = false 50: 51: # The next time the cleaner thread should check for idle servers. 52: # The value may be nil, in which case the value will be calculated 53: # at the end of the #synchronized block. 54: # 55: # Invariant: 56: # if value is not nil: 57: # There exists an s in @collection with s.next_cleaning_time == value. 58: # for all s in @collection: 59: # if eligable_for_cleanup?(s): 60: # s.next_cleaning_time <= value 61: @next_cleaning_time = Time.now + 60 * 60 62: @next_cleaning_time_changed = false 63: 64: @cleaner_thread = Thread.new do 65: begin 66: @lock.synchronize do 67: cleaner_thread_main 68: end 69: rescue Exception => e 70: print_exception(self.class.to_s, e) 71: end 72: end 73: end
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.
[ show source ]
# File lib/phusion_passenger/abstract_server_collection.rb, line 198 198: def check_idle_servers! 199: @lock.synchronize do 200: @next_cleaning_time = Time.now - 60 * 60 201: @cond.signal 202: end 203: end
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.
[ show source ]
# File lib/phusion_passenger/abstract_server_collection.rb, line 246 246: def cleanup 247: @cleanup_lock.synchronize do 248: return if @done 249: @lock.synchronize do 250: @done = true 251: @cond.signal 252: end 253: @cleaner_thread.join 254: clear 255: end 256: end
Delete all AbstractServers from the collection. Each AbstractServer will be stopped, if necessary.
Precondition: this method must be called within a #synchronize block.
[ show source ]
# File lib/phusion_passenger/abstract_server_collection.rb, line 228 228: def clear 229: @collection.each_value do |server| 230: if server.started? 231: server.stop 232: end 233: end 234: @collection.clear 235: @next_cleaning_time = nil 236: end
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.
[ show source ]
# File lib/phusion_passenger/abstract_server_collection.rb, line 162 162: def delete(key) 163: raise ArgumentError, "cleanup() has already been called." if @done 164: server = @collection[key] 165: if server 166: if server.started? 167: server.stop 168: end 169: @collection.delete(key) 170: if server.next_cleaning_time == @next_cleaning_time 171: @next_cleaning_time = nil 172: end 173: end 174: end
Iterate over all AbstractServer objects.
Precondition: this method must be called within a #synchronize block.
[ show source ]
# File lib/phusion_passenger/abstract_server_collection.rb, line 208 208: def each 209: each_pair do |key, server| 210: yield server 211: end 212: end
Iterate over all keys and associated AbstractServer objects.
Precondition: this method must be called within a #synchronize block.
[ show source ]
# File lib/phusion_passenger/abstract_server_collection.rb, line 217 217: def each_pair 218: raise ArgumentError, "cleanup() has already been called." if @done 219: @collection.each_pair do |key, server| 220: yield(key, server) 221: end 222: end
Checks whether the collection is empty.
Precondition: this method must be called within a #synchronize block.
[ show source ]
# File lib/phusion_passenger/abstract_server_collection.rb, line 152 152: def empty? 153: return @collection.empty? 154: end
Checks whether there‘s an AbstractServer object associated with the given key.
Precondition: this method must be called within a #synchronize block.
[ show source ]
# File lib/phusion_passenger/abstract_server_collection.rb, line 145 145: def has_key?(key) 146: return @collection.has_key?(key) 147: end
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.
[ show source ]
# File lib/phusion_passenger/abstract_server_collection.rb, line 119 119: def lookup_or_add(key) 120: raise ArgumentError, "cleanup() has already been called." if @done 121: server = @collection[key] 122: if server 123: register_activity(server) 124: return server 125: else 126: server = yield 127: if !server.respond_to?(:start) 128: raise TypeError, "The block didn't return a valid AbstractServer object." 129: end 130: if eligable_for_cleanup?(server) 131: server.next_cleaning_time = Time.now + server.max_idle_time 132: if @next_cleaning_time && server.next_cleaning_time < @next_cleaning_time 133: @next_cleaning_time = server.next_cleaning_time 134: @next_cleaning_time_changed = true 135: end 136: end 137: @collection[key] = server 138: return server 139: end 140: end
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.
[ show source ]
# File lib/phusion_passenger/abstract_server_collection.rb, line 185 185: def register_activity(server) 186: if eligable_for_cleanup?(server) 187: if server.next_cleaning_time == @next_cleaning_time 188: @next_cleaning_time = nil 189: end 190: server.next_cleaning_time = Time.now + server.max_idle_time 191: end 192: end
Acquire the lock for this AbstractServerCollection object, and run the code within the block. The entire block will be a single atomic operation.
[ show source ]
# File lib/phusion_passenger/abstract_server_collection.rb, line 78 78: def synchronize 79: @lock.synchronize do 80: yield 81: if @next_cleaning_time.nil? 82: @collection.each_value do |server| 83: if @next_cleaning_time.nil? || 84: (eligable_for_cleanup?(server) && 85: server.next_cleaning_time < @next_cleaning_time 86: ) 87: @next_cleaning_time = server.next_cleaning_time 88: end 89: end 90: if @next_cleaning_time.nil? 91: # There are no servers in the collection with an idle timeout. 92: @next_cleaning_time = Time.now + 60 * 60 93: end 94: @next_cleaning_time_changed = true 95: end 96: if @next_cleaning_time_changed 97: @next_cleaning_time_changed = false 98: @cond.signal 99: end 100: end 101: end