From d396da16e3853f629489f1d00ae4695554f03206 Mon Sep 17 00:00:00 2001 From: Torsten Ruger Date: Thu, 23 Aug 2018 19:55:06 +0300 Subject: [PATCH] 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 --- lib/parfait.rb | 1 + lib/parfait/factory.rb | 117 ++++++++++++++++++++ lib/parfait/page.rb | 15 --- lib/risc/parfait_boot.rb | 2 + test/parfait/test_factory.rb | 47 ++++++++ test/parfait/test_space.rb | 2 +- test/risc/interpreter/calling/test_minus.rb | 2 +- test/risc/test_interpreter.rb | 4 +- test/risc/test_linker.rb | 2 +- 9 files changed, 172 insertions(+), 20 deletions(-) create mode 100644 lib/parfait/factory.rb delete mode 100644 lib/parfait/page.rb create mode 100644 test/parfait/test_factory.rb diff --git a/lib/parfait.rb b/lib/parfait.rb index b2664718..30419df2 100644 --- a/lib/parfait.rb +++ b/lib/parfait.rb @@ -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" diff --git a/lib/parfait/factory.rb b/lib/parfait/factory.rb new file mode 100644 index 00000000..fb38c37c --- /dev/null +++ b/lib/parfait/factory.rb @@ -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 diff --git a/lib/parfait/page.rb b/lib/parfait/page.rb deleted file mode 100644 index aedda2c8..00000000 --- a/lib/parfait/page.rb +++ /dev/null @@ -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 diff --git a/lib/risc/parfait_boot.rb b/lib/risc/parfait_boot.rb index d51c5a39..8d420199 100644 --- a/lib/risc/parfait_boot.rb +++ b/lib/risc/parfait_boot.rb @@ -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 , diff --git a/test/parfait/test_factory.rb b/test/parfait/test_factory.rb new file mode 100644 index 00000000..9be959e3 --- /dev/null +++ b/test/parfait/test_factory.rb @@ -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 diff --git a/test/parfait/test_space.rb b/test/parfait/test_space.rb index 9f47b2fe..aa2a35c1 100644 --- a/test/parfait/test_space.rb +++ b/test/parfait/test_space.rb @@ -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 diff --git a/test/risc/interpreter/calling/test_minus.rb b/test/risc/interpreter/calling/test_minus.rb index 9a353896..001b6e24 100644 --- a/test/risc/interpreter/calling/test_minus.rb +++ b/test/risc/interpreter/calling/test_minus.rb @@ -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 diff --git a/test/risc/test_interpreter.rb b/test/risc/test_interpreter.rb index 1538ff6a..6fee456e 100644 --- a/test/risc/test_interpreter.rb +++ b/test/risc/test_interpreter.rb @@ -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} diff --git a/test/risc/test_linker.rb b/test/risc/test_linker.rb index bd3e7402..b285fd66 100644 --- a/test/risc/test_linker.rb +++ b/test/risc/test_linker.rb @@ -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