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 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.

### Methods

check_idle_servers! cleanup clear delete each each_pair empty? has_key? lookup_or_add new register_activity synchronize

### Included Modules

Utils

### Attributes

next_cleaning_time | [R] |

### Public Class methods

new()

[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

### Public Instance methods

check_idle_servers!()

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.

[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()

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.

[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

clear()

Delete all AbstractServers from the collection. Each AbstractServer will be stopped, if necessary.

Precondition: this method must be called within a #synchronize block.

[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

delete(key)

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.

[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

each() {|server| ...}

Iterate over all AbstractServer objects.

Precondition: this method must be called within a #synchronize block.

[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

each_pair() {|key, server| ...}

Iterate over all keys and associated AbstractServer objects.

Precondition: this method must be called within a #synchronize block.

[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

empty?()

Checks whether the collection is empty.

Precondition: this method must be called within a #synchronize block.

[Source]

         # File lib/phusion_passenger/abstract_server_collection.rb, line 147
    147:         def empty?
    148:                 return @collection.empty?
    149:         end

has_key?(key)

Checks whether there‘s an AbstractServer object associated with the given key.

Precondition: this method must be called within a #synchronize block.

[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_or_add(key) {|| ...}

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.

[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

register_activity(server)

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.

[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

synchronize() {|| ...}

Acquire the lock for this AbstractServerCollection object, and run the code within the block. The entire block will be a single atomic operation.

[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

[Validate]