start with #14 by implementing factory

page was maybe a too low level name
pages may be the unit of the syscall, but after that objects desolve (maybe later to be added on from different pages)
Factory has the job of handing out a new instance of a type
it keeps a freelist for that and a reserve
This commit is contained in:
Torsten Ruger 2018-08-23 19:55:06 +03:00
parent 9687d6611f
commit d396da16e3
9 changed files with 172 additions and 20 deletions

View File

@ -3,6 +3,7 @@ module Parfait
end end
require_relative "parfait/object" require_relative "parfait/object"
require_relative "parfait/factory"
require_relative "parfait/data_object" require_relative "parfait/data_object"
require_relative "parfait/integer" require_relative "parfait/integer"
require_relative "parfait/behaviour" require_relative "parfait/behaviour"

117
lib/parfait/factory.rb Normal file
View File

@ -0,0 +1,117 @@
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 , :last_object , :reserve , :attribute_name
PAGE_SIZE = 1024
RESERVE = 10
# 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
chain = get_chain
link = chain
count = RESERVE
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 = chain
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 , PAGE_SIZE)
chain = first
counter = 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 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)
obj
end
end
end

View File

@ -1,15 +0,0 @@
# A Page (from the traditional memory page) represents a collection of
# objects in a physically form. Ie the page holds the memory or data, that
# the objects are made up of.
# Pages have a total size, but more importantly an object size.
# All objects of a Page are same sized, and multiples of the smallest
# object. The smallest object is usually a cache line, 16 bytes or
# an exponent of two larger.
module Parfait
class Page < Object
attr :type
end
end

View File

@ -160,6 +160,8 @@ module Parfait
NamedList: {}, NamedList: {},
NilClass: {}, NilClass: {},
Object: {}, Object: {},
Factory: { for_type: :Type , next_object: :Object , last_object: :Object ,
reserve: :Object , attribute_name: :Word },
ReturnAddress: {next_integer: :ReturnAddress}, ReturnAddress: {next_integer: :ReturnAddress},
Space: {classes: :Dictionary , types: :Dictionary , Space: {classes: :Dictionary , types: :Dictionary ,
next_message: :Message , messages: :Message , next_message: :Message , messages: :Message ,

View File

@ -0,0 +1,47 @@
require_relative "helper"
module Parfait
class TestPage < ParfaitTest
def setup
super
@factory = Factory.new Parfait.object_space.get_type_by_class_name(:Integer)
end
def test_ok
assert @factory
end
def test_name_ok
assert @factory.attribute_name.to_s.start_with?("next")
end
def test_get_next
assert_nil @factory.get_next_for( Integer.new(1))
end
def test_no_next
assert_nil @factory.next_object
assert_nil @factory.last_object
assert_nil @factory.reserve
end
def test_get_next_object
assert_equal Parfait::Integer , @factory.get_next_object.class
end
def test_chain_length
count = 0
start = @factory.get_next_object
while( start )
start = start.next_integer
count += 1
end
assert_equal 1024 - 10 , count
end
def test_reserve_length
count = 0
start = @factory.get_next_object
start = @factory.reserve
while( start )
start = start.next_integer
count += 1
end
assert_equal 11 , count
end
end
end

View File

@ -5,7 +5,7 @@ module Parfait
def classes def classes
[:BinaryCode,:Block,:CacheEntry,:Callable,:CallableMethod,:Class, [:BinaryCode,:Block,:CacheEntry,:Callable,:CallableMethod,:Class,
:DataObject,:Data4,:Data8,:Data16,:Dictionary,:Integer,:FalseClass, :DataObject,:Data4,:Data8,:Data16,:Dictionary,:Factory, :Integer,:FalseClass,
:List,:Message,:NamedList,:NilClass,:Object,:ReturnAddress, :List,:Message,:NamedList,:NilClass,:Object,:ReturnAddress,
:Space,:TrueClass,:Type,:VoolMethod,:Word] :Space,:TrueClass,:Type,:VoolMethod,:Word]
end end

View File

@ -45,7 +45,7 @@ module Risc
ret = main_ticks(64) ret = main_ticks(64)
assert_equal FunctionReturn , ret.class assert_equal FunctionReturn , ret.class
assert_equal :r1 , ret.register.symbol assert_equal :r1 , ret.register.symbol
assert_equal 26472 , @interpreter.get_register(ret.register) assert_equal 27080 , @interpreter.get_register(ret.register)
end end
end end
end end

View File

@ -54,7 +54,7 @@ module Risc
end end
def test_pc1 def test_pc1
@interpreter.tick @interpreter.tick
assert_equal 26136 , @interpreter.pc assert_equal 26680 , @interpreter.pc
end end
def test_tick2 def test_tick2
@interpreter.tick @interpreter.tick
@ -68,7 +68,7 @@ module Risc
def test_pc2 def test_pc2
@interpreter.tick @interpreter.tick
@interpreter.tick @interpreter.tick
assert_equal 26140 , @interpreter.pc assert_equal 26684 , @interpreter.pc
end end
def test_tick_14_jump def test_tick_14_jump
14.times {@interpreter.tick} 14.times {@interpreter.tick}

View File

@ -25,7 +25,7 @@ module Risc
assert_equal 0 , Position.get(@linker.cpu_init).at assert_equal 0 , Position.get(@linker.cpu_init).at
end end
def test_cpu_at def test_cpu_at
assert_equal "0x74cc" , Position.get(@linker.cpu_init.first).to_s assert_equal "0x76ec" , Position.get(@linker.cpu_init.first).to_s
end end
def test_cpu_label def test_cpu_label
assert_equal Position , Position.get(@linker.cpu_init.first).class assert_equal Position , Position.get(@linker.cpu_init.first).class