Class | PhusionPassenger::AbstractServerCollection |
In: |
lib/phusion_passenger/abstract_server_collection.rb
|
Parent: | Object |
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.
next_cleaning_time | [R] |
# 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.
# File lib/phusion_passenger/abstract_server_collection.rb, line 208 208: def check_idle_servers! 209: must_not_be_in_synchronize_block 210: @lock.synchronize do 211: @next_cleaning_time = Time.now - 60 * 60 212: @cond.signal 213: end 214: 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.
# File lib/phusion_passenger/abstract_server_collection.rb, line 260 260: def cleanup 261: must_not_be_in_synchronize_block 262: @cleanup_lock.synchronize do 263: return if @done 264: @lock.synchronize do 265: @done = true 266: @cond.signal 267: end 268: @cleaner_thread.join 269: synchronize do 270: clear 271: end 272: end 273: end
Delete all AbstractServers from the collection. Each AbstractServer will be stopped, if necessary.
Precondition: this method must be called within a #synchronize block.
# File lib/phusion_passenger/abstract_server_collection.rb, line 241 241: def clear 242: must_be_in_synchronize_block 243: @collection.each_value do |server| 244: if server.started? 245: server.stop 246: end 247: end 248: @collection.clear 249: @next_cleaning_time = nil 250: 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.
# File lib/phusion_passenger/abstract_server_collection.rb, line 170 170: def delete(key) 171: raise ArgumentError, "cleanup() has already been called." if @done 172: must_be_in_synchronize_block 173: server = @collection[key] 174: if server 175: if server.started? 176: server.stop 177: end 178: @collection.delete(key) 179: if server.next_cleaning_time == @next_cleaning_time 180: @next_cleaning_time = nil 181: end 182: end 183: end
Iterate over all AbstractServer objects.
Precondition: this method must be called within a #synchronize block.
# File lib/phusion_passenger/abstract_server_collection.rb, line 219 219: def each 220: must_be_in_synchronize_block 221: each_pair do |key, server| 222: yield server 223: end 224: end
Iterate over all keys and associated AbstractServer objects.
Precondition: this method must be called within a #synchronize block.
# File lib/phusion_passenger/abstract_server_collection.rb, line 229 229: def each_pair 230: raise ArgumentError, "cleanup() has already been called." if @done 231: must_be_in_synchronize_block 232: @collection.each_pair do |key, server| 233: yield(key, server) 234: end 235: end
Checks whether the collection is empty.
Precondition: this method must be called within a #synchronize block.
# File lib/phusion_passenger/abstract_server_collection.rb, line 159 159: def empty? 160: must_be_in_synchronize_block 161: return @collection.empty? 162: end
Checks whether there‘s an AbstractServer object associated with the given key.
Precondition: this method must be called within a #synchronize block.
# File lib/phusion_passenger/abstract_server_collection.rb, line 151 151: def has_key?(key) 152: must_be_in_synchronize_block 153: return @collection.has_key?(key) 154: 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.
# File lib/phusion_passenger/abstract_server_collection.rb, line 124 124: def lookup_or_add(key) 125: raise ArgumentError, "cleanup() has already been called." if @done 126: must_be_in_synchronize_block 127: server = @collection[key] 128: if server 129: register_activity(server) 130: return server 131: else 132: server = yield 133: if !server.respond_to?(:start) 134: raise TypeError, "The block didn't return a valid AbstractServer object." 135: end 136: if eligable_for_cleanup?(server) 137: server.next_cleaning_time = Time.now + server.max_idle_time 138: if @next_cleaning_time && server.next_cleaning_time < @next_cleaning_time 139: @next_cleaning_time = server.next_cleaning_time 140: @next_cleaning_time_changed = true 141: end 142: end 143: @collection[key] = server 144: return server 145: end 146: 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.
# File lib/phusion_passenger/abstract_server_collection.rb, line 194 194: def register_activity(server) 195: must_be_in_synchronize_block 196: if eligable_for_cleanup?(server) 197: if server.next_cleaning_time == @next_cleaning_time 198: @next_cleaning_time = nil 199: end 200: server.next_cleaning_time = Time.now + server.max_idle_time 201: end 202: end
Acquire the lock for this AbstractServerCollection object, and run the code within the block. The entire block will be a single atomic operation.
# File lib/phusion_passenger/abstract_server_collection.rb, line 78 78: def synchronize 79: @lock.synchronize do 80: @in_synchronize_block = true 81: begin 82: yield 83: ensure 84: if @next_cleaning_time.nil? 85: @collection.each_value do |server| 86: if @next_cleaning_time.nil? || 87: (eligable_for_cleanup?(server) && 88: server.next_cleaning_time < @next_cleaning_time 89: ) 90: @next_cleaning_time = server.next_cleaning_time 91: end 92: end 93: if @next_cleaning_time.nil? 94: # There are no servers in the collection with an idle timeout. 95: @next_cleaning_time = Time.now + 60 * 60 96: end 97: @next_cleaning_time_changed = true 98: end 99: if @next_cleaning_time_changed 100: @next_cleaning_time_changed = false 101: @cond.signal 102: end 103: @in_synchronize_block = false 104: end 105: end 106: end