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:
parent
9687d6611f
commit
d396da16e3
@ -3,6 +3,7 @@ module Parfait
|
||||
end
|
||||
|
||||
require_relative "parfait/object"
|
||||
require_relative "parfait/factory"
|
||||
require_relative "parfait/data_object"
|
||||
require_relative "parfait/integer"
|
||||
require_relative "parfait/behaviour"
|
||||
|
117
lib/parfait/factory.rb
Normal file
117
lib/parfait/factory.rb
Normal 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
|
@ -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
|
@ -160,6 +160,8 @@ module Parfait
|
||||
NamedList: {},
|
||||
NilClass: {},
|
||||
Object: {},
|
||||
Factory: { for_type: :Type , next_object: :Object , last_object: :Object ,
|
||||
reserve: :Object , attribute_name: :Word },
|
||||
ReturnAddress: {next_integer: :ReturnAddress},
|
||||
Space: {classes: :Dictionary , types: :Dictionary ,
|
||||
next_message: :Message , messages: :Message ,
|
||||
|
47
test/parfait/test_factory.rb
Normal file
47
test/parfait/test_factory.rb
Normal 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
|
@ -5,7 +5,7 @@ module Parfait
|
||||
|
||||
def classes
|
||||
[: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,
|
||||
:Space,:TrueClass,:Type,:VoolMethod,:Word]
|
||||
end
|
||||
|
@ -45,7 +45,7 @@ module Risc
|
||||
ret = main_ticks(64)
|
||||
assert_equal FunctionReturn , ret.class
|
||||
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
|
||||
|
@ -54,7 +54,7 @@ module Risc
|
||||
end
|
||||
def test_pc1
|
||||
@interpreter.tick
|
||||
assert_equal 26136 , @interpreter.pc
|
||||
assert_equal 26680 , @interpreter.pc
|
||||
end
|
||||
def test_tick2
|
||||
@interpreter.tick
|
||||
@ -68,7 +68,7 @@ module Risc
|
||||
def test_pc2
|
||||
@interpreter.tick
|
||||
@interpreter.tick
|
||||
assert_equal 26140 , @interpreter.pc
|
||||
assert_equal 26684 , @interpreter.pc
|
||||
end
|
||||
def test_tick_14_jump
|
||||
14.times {@interpreter.tick}
|
||||
|
@ -25,7 +25,7 @@ module Risc
|
||||
assert_equal 0 , Position.get(@linker.cpu_init).at
|
||||
end
|
||||
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
|
||||
def test_cpu_label
|
||||
assert_equal Position , Position.get(@linker.cpu_init.first).class
|
||||
|
Loading…
x
Reference in New Issue
Block a user