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 39 39: def initialize 40: @collection = {} 41: @lock = Mutex.new 42: @cleanup_lock = Mutex.new 43: @cond = ConditionVariable.new 44: @done = false 45: 46: # The next time the cleaner thread should check for idle servers. 47: # The value may be nil, in which case the value will be calculated 48: # at the end of the #synchronized block. 49: # 50: # Invariant: 51: # if value is not nil: 52: # There exists an s in @collection with s.next_cleaning_time == value. 53: # for all s in @collection: 54: # if eligable_for_cleanup?(s): 55: # s.next_cleaning_time <= value 56: @next_cleaning_time = Time.now + 60 * 60 57: @next_cleaning_time_changed = false 58: 59: @cleaner_thread = Thread.new do 60: begin 61: @lock.synchronize do 62: cleaner_thread_main 63: end 64: rescue Exception => e 65: print_exception(self.class.to_s, e) 66: end 67: end 68: 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 193 193: def check_idle_servers! 194: @lock.synchronize do 195: @next_cleaning_time = Time.now - 60 * 60 196: @cond.signal 197: end 198: 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 241 241: def cleanup 242: @cleanup_lock.synchronize do 243: return if @done 244: @lock.synchronize do 245: @done = true 246: @cond.signal 247: end 248: @cleaner_thread.join 249: clear 250: end 251: 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 223 223: def clear 224: @collection.each_value do |server| 225: if server.started? 226: server.stop 227: end 228: end 229: @collection.clear 230: @next_cleaning_time = nil 231: 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 157 157: def delete(key) 158: raise ArgumentError, "cleanup() has already been called." if @done 159: server = @collection[key] 160: if server 161: if server.started? 162: server.stop 163: end 164: @collection.delete(key) 165: if server.next_cleaning_time == @next_cleaning_time 166: @next_cleaning_time = nil 167: end 168: end 169: 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 203 203: def each 204: each_pair do |key, server| 205: yield server 206: end 207: 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 212 212: def each_pair 213: raise ArgumentError, "cleanup() has already been called." if @done 214: @collection.each_pair do |key, server| 215: yield(key, server) 216: end 217: 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 147 147: def empty? 148: return @collection.empty? 149: 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 140 140: def has_key?(key) 141: return @collection.has_key?(key) 142: 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 114 114: def lookup_or_add(key) 115: raise ArgumentError, "cleanup() has already been called." if @done 116: server = @collection[key] 117: if server 118: register_activity(server) 119: return server 120: else 121: server = yield 122: if !server.respond_to?(:start) 123: raise TypeError, "The block didn't return a valid AbstractServer object." 124: end 125: if eligable_for_cleanup?(server) 126: server.next_cleaning_time = Time.now + server.max_idle_time 127: if @next_cleaning_time && server.next_cleaning_time < @next_cleaning_time 128: @next_cleaning_time = server.next_cleaning_time 129: @next_cleaning_time_changed = true 130: end 131: end 132: @collection[key] = server 133: return server 134: end 135: 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 180 180: def register_activity(server) 181: if eligable_for_cleanup?(server) 182: if server.next_cleaning_time == @next_cleaning_time 183: @next_cleaning_time = nil 184: end 185: server.next_cleaning_time = Time.now + server.max_idle_time 186: end 187: 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 73 73: def synchronize 74: @lock.synchronize do 75: yield 76: if @next_cleaning_time.nil? 77: @collection.each_value do |server| 78: if @next_cleaning_time.nil? || 79: (eligable_for_cleanup?(server) && 80: server.next_cleaning_time < @next_cleaning_time 81: ) 82: @next_cleaning_time = server.next_cleaning_time 83: end 84: end 85: if @next_cleaning_time.nil? 86: # There are no servers in the collection with an idle timeout. 87: @next_cleaning_time = Time.now + 60 * 60 88: end 89: @next_cleaning_time_changed = true 90: end 91: if @next_cleaning_time_changed 92: @next_cleaning_time_changed = false 93: @cond.signal 94: end 95: end 96: end