| Class | MUES::ObjectStore::TrainMemoryManager |
| In: |
lib/mues/os-extensions/trainmemorymanager.rb
(CVS)
|
| Parent: | MUES::ObjectStore::MemoryManager |
an incremental memory manager scheme, to allow for non-disruptive management of the ObjectStore system. note: this cannot be restart()ed.
| SVNRev | = | %q$Rev: 1206 $ | SVN Revision | |
| SVNId | = | %q$Id: trainmemorymanager.rb 1206 2004-05-09 21:25:11Z deveiant $ | SVN Id | |
| SVNURL | = | %q$URL: svn+ssh://deveiate.org/usr/local/svn/MUES/trunk/lib/mues/os-extensions/trainmemorymanager.rb $ | SVN URL |
| activeObjectReferences | [RW] | the active objects keyed to a list of every registered object that references it. |
| carSize | [RW] | the number of objects allowed per car |
| interval | [RW] | the number of seconds between each manager thread interval |
| trainInterval | [RW] | the number of seconds between each train thread interval. note that this is set actively by the algorithm and any changes enacted by setting it here will be temporary. |
| trainObjectRatio | [RW] | the ratio to maintain between number of mature objects and total number of objects. |
| trainRatioAccuracy | [RW] | the give or take of the trainObjectRatio - how close the ratio can actually be without invoking interval changing. |
| trainSize | [RW] | the number of cars allowed per train before a new one is started. this only applies to the youngest train. |
Create a new TrainMemoryManager object with the specified args.
# File lib/mues/os-extensions/trainmemorymanager.rb, line 108 def initialize( *args ) super( *args ) @interval = @config['interval'] || 50 @trainInterval = @config['trainInterval'] || 30 @trainObjectRatio = @config['trainObjectRatio'] || 0.1 @trainRatioAccuracy = @config['trainRatioAccuracy'] || .05 @trainSize = @config['trainSize'] || 3 @carSize = @config['carSize'] || 2**10 @trainyard = Trainyard::new(self) @trainThread = nil @activeObjectReferences = {} end
Caveat: this assumes that the references between objects does not change after registration. This will not be true for any real system, but since reference tracking is being added simply for the sake of argument, no effort was put into making this realistic. The most correct way to have implemented this would be to have altered Object to be able to know how many links there are to it at any given time, or possibly tied into the hook of whenever any variable is changed, and set the values that way.
# File lib/mues/os-extensions/trainmemorymanager.rb, line 161 def register( *objs ) super( *objs ) @mutex.synchronize( Sync::EX ) { objs.each {|fresh| @activeObjectReferences[fresh] = [] @activeObjects.each {|id,o| @activeObjectReferences[fresh] << o if o.instance_variables.include?(fresh) @activeObjectReferences[o] << fresh if fresh.instance_variables.include?(o) } } } end
# File lib/mues/os-extensions/trainmemorymanager.rb, line 176 def restart(thread) @trainThread.raise Reload @trainThread.join super(thread) end
# File lib/mues/os-extensions/trainmemorymanager.rb, line 182 def shutdown @trainThread.raise Shutdown @trainThread.join super end
add functionality to the start method.
# File lib/mues/os-extensions/trainmemorymanager.rb, line 189 def start( visitor ) $stderr.puts "Starting TrainMemoryManager threads" if $debug super( visitor ) unless @trainThread && @trainThread.alive? @trainThread = Thread.new { Thread.current.abort_on_exception = true begin trainManagingRoutine( visitor ) rescue Reload # :TODO: Log? rescue Shutdown # :TODO: Log? end } end end
checks the train yard for cars in need of deletion. only looks at one car each iteration - the first car on the first train. the train is then "deleted" or not based on the way the references between trains are networked. if it is not deleted, the objects in the car are shuffled off to the newest train that references them, or the newest train if the reference is from outside mature object space, or to the end of the oldest train if not referenced. Caveat: this assumes that once an object is declared mature, that will not have changed by the time it gets deleted.
# File lib/mues/os-extensions/trainmemorymanager.rb, line 289 def checkTrains( visitor ) deleteTheTrain = true @trainyard[0][0].objects.each {|old| newest = nil @activeObjectReferences[old].each {|obj| # find which trains, if any, the reference to our object # comes from. residence = @trainyard.trains.reverse.collect {|t| t.hasObject?(obj) ? t : nil }.compact if residence.empty? # the reference to the mature object wasn't found in # any of the trains, meaning it comes from outside # of mature object space, and this object should be # moved to the end of the newest train. deleteTheTrain = false @mutex.synchronize( Sync::EX ) { @trainyard[0][0].objects.delete(old) } newlyMatured(old) break # no need to check any more references elsif residence[-1] == residence[0] && residence[0] == @trainyard[0] # this reference is on the same train, so do nothing # - will get moved to end of this train or deleted, # but not until the rest of this car is checked. else # otherwise, the reference is known to be in a # train, and the object should be put there. # :TODO: # for best behavior, check every reference, then put # it in with the youngest of those. deleteTheTrain = false @mutex.synchronize( Sync::EX ) { @trainyard[0][0].objects.delete(old) residence[-1].addObj(old) } end } } if deleteTheTrain # no outside references were found, so trash the whole train $stderr.puts "deleting a train of length #{@trainyard[0].length}" if $debug self.reclaim(@trainyard.shift.cars.collect {|c| c.objects }.flatten) else # some outside references were found, meaning the entire # train may not have been checked, so put this car, with all # its remaining objects, onto the end of this train, thus # maintaining any datastructures that span multiple cars. @trainyard.[0].cars.push( @trainyard[0].cars.unshift ) end end
simple - calls startCycle, waits at least @interval time, then starts over. on shutdown, calls saveAllObjects.
# File lib/mues/os-extensions/trainmemorymanager.rb, line 215 def managerThreadRoutine( visitor ) begin while true loop_time = Time.now startCycle( visitor ) until (Time.new - loop_time >= @interval) do Thread.pass end end ensure $stderr.puts "saveAllObjects being called" if $debug saveAllObjects() end end
replace the object(s) with a shallow reference
# File lib/mues/os-extensions/trainmemorymanager.rb, line 345 def reclaim( *objs ) objs.to_a.each {|o| @mutex.synchronize( Sync::EX ) { @objectStore.store(o) o.polymorph(ShallowReference.new( o.objectStoreId, @objectStore )) } } end
Stores all the (non-shallow) objects in the object store.
# File lib/mues/os-extensions/trainmemorymanager.rb, line 246 def saveAllObjects @mutex.synchronixe( Sync::EX ) { @active_objects.each_value {|o| @objectStore.store(o) unless o.shallow? } } @active_objects.clear @activeObjectReferences.clear @trainyard.clear end
The object maturation algorithm. This gets run once a cycle.
# File lib/mues/os-extensions/trainmemorymanager.rb, line 232 def startCycle( visitor ) @mutex.synchronize( Sync::SH ) { @active_objects.each_value {|o| unless o.shallow? # maturity is a boolean - true means the object is mature maturity = o.accept(visitor) @trainyard.newlyMatured(o) if maturity $stderr.puts "object #{o.to_s} found to be mature" if $debug && maturity end } } end
the train thread conroller routine.
# File lib/mues/os-extensions/trainmemorymanager.rb, line 258 def trainManagingRoutine( visitor ) while true loop_time = Time.now checkTrains( visitor ) case @trainyard.objectCount / @active_objects.length when (@trainObjectRatio*(1-@trainRatioAccuracy))..( @trainObjectRatio*(1+@trainRatioAccuracy)) #close enough, leave it alone. when @trainObjectRatio..1.0 #too much trash, go faster @trainInterval *= .9 when 0..@trainObjectRatio #too often, slow down @trainInterval *= 1.1 end until (Time.new - loop_time >= @trainInterval) do Thread.pass end end end