rubyx/lib/parfait/factory.rb
Torsten Ruger 74b790250a use class variables instead of globals
and set page size from new argument
This closes #23 , though ripples will follow
2019-02-08 23:03:08 +02:00

121 lines
4.3 KiB
Ruby

module Parfait
# A factory has the one job of handing out new instances
#
# A factory is for a specific type (currently, may change by size at some point)
#
# get_next_object is the main entry point, all other functions help to get more
# memory and objects as needed
#
# A factory keeps a reserve, and in case the freelist is empty, switches that in _immediately
# This is especially useful for messages, that can then be used even they run out.
#
# The idea (especially for messages) is to call out from the MessageSetup to the
# factory when the next (not current) is nil.
# This is btw just as easy a check, as the next needs to be gotten to swap the list.
class Factory < Object
attr :type , :for_type , :next_object , :reserve , :attribute_name
@page_size = 1024
@reserve_size = 10
class << self; attr_accessor :page_size ; attr :reserve_size end
# initialize for a given type (for_type). The attribute that is used to create the
# list is the first that starts with next_ . "next" itself would have been nice and general
# but is a keyword, so no go.
def initialize(type)
self.for_type = type
self.attribute_name = type.names.find {|name| name.to_s.start_with?("next")}
raise "No next found for #{type.class_name}" unless attribute_name
end
# get the next free object, advancing the list.
# Calls out to get_more if the list is empty.
# This function is not realy used, as it is hard-coded in risc, but the get_more is
# used, as it get's called from risc (or will)
def get_next_object
unless( next_object )
self.next_object = reserve
get_more
end
get_head
end
# this gets the head of the freelist, swaps it out agains the next and returns it
def get_head
nekst = next_object
self.next_object = get_next_for(nekst)
return nekst
end
# get more from system
# and rebuilt the reserve (get_next already instantiates the reserve)
#
def get_more
first_object = get_chain
link = first_object
count = Factory.reserve_size
while(count > 0)
link = get_next_for(link)
count -= 1
end
self.next_object = get_next_for(link)
set_next_for( link , nil )
self.reserve = first_object
self
end
# this initiates the syscall to get more memory.
# it creates objects from the mem and link them into a chain
def get_chain
raise "type is nil" unless self.for_type
first = sys_mem( for_type , Factory.page_size)
chain = first
counter = Factory.page_size
while( counter > 0)
nekst = get_next_raw( chain )
set_next_for(chain, nekst)
chain = nekst
counter -= 1
end
first
end
# get the next_* attribute from the given object.
# the attribute name is determined in initialize
def get_next_for(object)
object.send(attribute_name)
end
# set the next_* attribute of the given object, with the value.
# the attribute name is determined in initialize
def set_next_for(object , value)
object.send("#{attribute_name}=".to_sym , value)
end
# Return the object _after the given one. In memory terms the next object starts
# after the object ends. So this is in fact pointer arithmetic (once done)
# This implementation will be moved to the adapter, as the real thing needs to be coded
# in risc
# This adapter version just return a new object
def get_next_raw( object )
sys_mem( object.get_type , 1)
end
# return more memory from the system.
# Or to be more precise (as that is not really possible), allocate memory
# for Factory.page_size objects, and return the first object.
# ( the object has a type as first member, that type will be the for_type of this factory)
# This implementation will be moved to the adapter, as the real thing needs to be coded
# in risc
# This adapter version just return a new object (by establishing the ruby class
# and using ruby's allocate and set_type)
def sys_mem( type , amount)
r_class = eval( "Parfait::#{type.object_class.name}" )
obj = r_class.allocate
obj.set_type(type)
#puts "Factory #{type.object_class.name} at 0x#{obj.object_id.to_s(16)}"
obj
end
end
end