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.

Methods

Classes and Modules

Class MUES::ObjectStore::TrainMemoryManager::MMCar
Class MUES::ObjectStore::TrainMemoryManager::MMTrain
Class MUES::ObjectStore::TrainMemoryManager::TrainError
Class MUES::ObjectStore::TrainMemoryManager::Trainyard

Constants

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

Attributes

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.

Public Class methods

Create a new TrainMemoryManager object with the specified args.

[Source]

# 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

Public Instance methods

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.

[Source]

# 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

[Source]

# File lib/mues/os-extensions/trainmemorymanager.rb, line 176
            def restart(thread)
                @trainThread.raise Reload
                @trainThread.join
                super(thread)
            end

[Source]

# File lib/mues/os-extensions/trainmemorymanager.rb, line 182
            def shutdown 
                @trainThread.raise Shutdown
                @trainThread.join
                super
            end

add functionality to the start method.

[Source]

# 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

Protected Instance methods

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.

[Source]

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

[Source]

# 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

[Source]

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

[Source]

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

[Source]

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

[Source]

# 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

[Validate]