( picoVerse-:( a simpler future ) )

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

( picoVerse-:( a simpler future ) )

Kjell Godo
I would like to propose a simpler version of Croquet world
syncronization.  With the idea that maybe by telling me
why it wouldn't work Croquet developers can help me to
understand how Croquet world syncronization works.
The other purpose is perhaps to make a simpler world
syncronization for picoLARC or Dolphin Smalltalk.

Okay so suppose you have a machine called PC with World W.
A World is a collection of Objects that is replicated on several
     computers.  Actions in one of the copies is replicated to all
     the other copies.
In that World W you have a big Array OC.
There are ObjectFutures which point at OC and have Integer ids
     in them.
Actors are Objects that are in OC.  This could include Strings
     Integers and other literals as well as arbitrary Objects.
     In other words any Object that is in OC is called an Actor.
Whenever a new Actor A comes into World W it gets put into OC at
     the end and gets an ObjectFuture OFA assigned to it.  Maybe in a
     Dictionary of < Object ObjectFuture > pairs also.  The ObjectFuture
     OFA gets the index id of A in OC.  And Actor A gets copied to all the
     other Worlds Wi on all the other machines PCi and gets stuck into
     all the other OCi in those Wi and each Wi gets ObjectFuture OFAi.
     So there is World W and the other Worlds Wi i=1..N.
( anObject future ) looks in the list of Worlds in the Dictionaries to
     find the ObjectFuture for anObject.  ( anObject futureIn: W ) does
     the same.  If an Actor A knew which World W it was in that would
     speed ( A future ) up.  Some Actors know which World they are in.  If
     anObject is not in W yet then ( anObject futureIn: W ) puts it in
     OC and gives it an ObjectFuture and replicates anObject into
     all the other Wi on the net.  In this way an Actor can disembowel
     itself such that it contains nothing but ObjectFutures in its instance
     variables.  The ObjectFuture Class would have to override all
     the methods in the Object Class
     in order to forward them to its Actor.  The rest of
     the messages are dispatched using
     ObjectFuture>>doesNotUnderstand: aMessage
          ^( aMessage
               receiver:( actorOC at: id ) ;
               perform )
     ( thats a lot of methods to override.  perhaps we can just forget
     about overriding them all and just override the ones that need it
     as time goes by.  Perhaps the overriding could be done
     automatically since all the method bodies would be the same:
     message: bla bla: bla1
          ^( actorOC at: id ) message: bla bla: bla1 )
Now when other Actors B want to refer to Actor A they do so by
     having ObjectFuture OFA which points at A via OC.
Now suppose Actor B is running method MM( self = B )
     which sends message M to Actor A.  Then B sends
     M to ObjectFuture OFA which doesn't understand it and forwards
     M to Actor A via OC and returns the answer to Actor B.
At the same time ObjectFuture tells the Router R of World W to
     send M to the other replicated Worlds Wi on other machines
     i = 1..N .
Now message M gets translated into
     < ( PC's id ) ( Wi's id ) ( M asSent ) aSequenceInteger
          optionalTimeToExecute >
     which is sent down the pipe to World Wi on machine PCi.
( M asSent ) is set up so that the sender and receiver and all input
     parameters in the message are
     ObjectFutures translated into Bytes for transmission.
     Perhaps some literals like Strings and Integers could be sent
     as is.  The rest of the Actors would have been replicated
     already.  Perhaps certain Objects should be designated as
     able to be
     Actors like instances of subClasses of the WorldActor Class
     and all other Objects would be considered to be private and
     remain unseen within the Actors.  Actors would never pass
     nonActors as inputs into messages to other Actors.  All
     Actors would communicate with other Actors and by way of
     Actors.  No nonActors could be used in messages between
     Actors.  But perhaps literals could be the exception to that
     rule.  Each nonReplicated message internal to Actors has to
     be part of a replicated message to an Actor.  Or instead of
     using WorldActor to make Actors ( anObject futureIn: W )
     would cause anObject to become an Actor where Actor means
     anything in one of the OCi Arrays and associated
     < anObject anObjectFuture > Dictionaries in W and the Wi.
     So any Object could become an Actor.  And then instances
     of WorldActor could have special knowledge of being an Actor
     in a World.  Perhaps they would be more reliable that way.
When message M gets to machine PCi it hits the Router Ri
     in Wi which
     sticks message M into a Queue PCQi for PC in Ri which is
     sorted according to the sequence numbers and
     timesToExecute in the messages.  Where the
     timeToExecutes override the sequence numbers.
Router Ri for World Wi has a different message Queue for each
     PC that is sending messages to it so that sorting of these
     messages is speeded up and simplified.  Then all the
     Queues are executed round robin and depending on the
     timeToExecutes in the top message in each.
Message M finally gets to the top of Queue PCQi. ( Messages
     go in the bottom and out the top of the Queues. )
Now all the ObjectFutures in M get recreated and are set to point
     at OCi.
Now message M is performed.  Actor Ai is found in OCi and
     message M is performed but where does the return value go?
     Where is Actor Bi?  Message M is supposed to be coming
     from Actor Bi?  Some method MMi is supposed to be running
     on Actor Bi( self = Bi ) where the return value from
     message M will go but there is no Actor Bi and no method MMi
     running.  So we have a problem.

The problem with this setup that I can see is that in W Actor B
is at the top of the Smalltalk message Activation stack.  In other
words Actor B is in the middle of some method MM which sends
the message M to Actor A.
But over in Wi Actor Bi is not in MMi when message M gets sent to
Actor Ai from Actor Bi but there is no Bi.  So where does
message M return to in Wi?   It returns to the Router Controller
Queue.  Let message M in World Wi on machine PCi be called Mi.

So it looks like all messages between Objects in W need to be
originating from the Queue PCQ in Controller C in W.  So the message
Queues will be in a Controller in World W.  So each World will have a
Router and a Controller.  The Controller will have the Queues.

So when message M from B to A in W is originating from the
Controller C how does B get the return value?  Should there be another
message which passes the return value back to Actor B?  So
message M would actually be a message which gets sent from the
Controller C to A and then the return value from M gets sent to B
from the Controller C via a second message MS?

It's like the Controller is directing everything.  All messages have
to go through the Controller but where is method MM?

Perhaps Actor A can have internal private messages that it
sends to itself and its instance variables.  And public messages
like M that it sends to other Actors.  Yes.

So instead of method MM having a statement in it like:
     instVar1 := instVar2 future messageM.

Method MM would have to do it like:
     instVar2 future messageM<---( which contains MS ).

Where MS is a Method in Actor B something like
>>instVar1: returnValue
     ^instVar1 := returnValue.

Or perhaps method MM should contain:
     ( instVar2 future messageMblablablaReturningTo:
          [ :return | instVar1 := return ] )
or
     ( instVar2 future messageMblablablaReturningTo: #instVar1: )
or
     ( instVar2 future returningTo: #instVar1: )
          messageMblablabla...
and then
ObjectFuture>>doesNotUnderstand: aMessage
     ^( router
          sendMessage:( aMessage ) ;
          returningTo:( returnSelector ) )
and then the Controller would take the receiver of aMessage
and send returnSelector to it with the return value as the input.

instVar2 would probably be an ObjectFuture so the >>future would
just return self.  But just in case and to label this as a replicated
message the >>future should be called.  In the case that instVar2
was not an ObjectFuture then >>future could log it into all the OCs
in all the Worlds W and Wi on all the PCs and
tell instVar2 to become the new ObjectFuture.  Hopefully instVar2
would know which W it was in.  If there was only one W running
at a time that would simplify things.  Perhaps >>futureIn: W could be
used.  Perhaps one of the instance variables inside of instVar2 would
know which World on PC it was in and then instVar2 could be logged
into that World.  Hopefully W.  Or better yet maybe in Actor B in
method MM we could send message M like:

     ( instVar2 := instVar2
          futureIn:( self )returningTo:( #instVar1: )"<---( sets MS
into M send )"
     ) messageM...

where
Object>>futureIn: anActor returningTo: aSymbol
     ^( self futureIn: anActor world )
          futureIn: anActor returningTo: aSymbol

Object>>futureIn: aWorld
     ^( ObjectFuture log:( self )into:( aWorld ) )
ObjectFuture class>>log: anObject into: aWorld
     ^( super new )
          logAt:( aWorld log: anObject )"<---( logs it into all Wi and
returns id )"
          onWorld:( aWorld )
ObjectFuture>>logAt: anInteger onWorld: aWorld
     actorOC := aWorld actorOC .
     id := anInteger . router := aWorld router .

ObjectFuture>>futureIn: anObject returningTo: aSymbol
     messageToReturnValueBy := ( aSymbol asReturnTo: anObject )

Symbol>>asReturnTo: anObject
     ^Message next
          selector:( #instVar1: )
          receiver:( anObject )"<---( anObject is Actor B )"
          "withArguments:( Array new: 1 )<---( is done by >>next )"
where
Message class>>next
     returns the next Message in a Queue of empty Messages
     such that memory allocation is reduced.  The Controller
     could free these Messages for reuse.

ObjectFuture>>doesNotUnderstand: anActorBasedMessage
     ^( router
          sendActorMessage: anActorBasedMessage
          returningTo: messageToReturnValueBy )
which returns true or false.

We have WorldActor Class being used.  because of the >>world.
or any Object the responds to >>world would work as an Actor
above.

When I say instVar2 I mean the Object in the instVar2 instance
variable.

So each message M would have to include where the return value
was to be placed.  And so this might be inconvenient because
a lot of times you want to stick return values in temporary variables.
Perhaps there could be instance variables labelled tempInstVar
that would be just for temporary use.  Or perhaps special temporary
value Objects that respond to >>value: and >>value could be logged
into the OCs temporarily to handle the job of temporary variables.

     ( instVar2 := instVar2
          futureIn:( self )
          returningToTemp:( temp := ActorValueHolder in: self )
     ) messageM...
     ...
     temp delete.

ActorValueHolder class>>in: anActor
     would make a new ActorValueHolder and log it into all the Worlds W
     in their OCs and return the resulting ObjectFuture.
ActorValueHolder>>delete
     would delete the ActorValueHolder from all the OCs perhaps putting it
     on a free list for reuse.


Every future message M like

     ( instVar2 := instVar2
          futureIn:( self )returningTo:( #instVar1: )
     ) messageM...

would return something like true if it
worked or false if it didn't work so that the return value would
mostly not be used since there isn't one.  Thus the programmer
would be forced to keep each future message M from trying to
feed return values into other enclosing messages.  No
     ( obj
          messageN:( obj1 future messageM... ) )
because ( obj future messageM... ) would always return true or false.
To speed things up maybe it could just always return nil.  You
don't want to return self because people would get the idea that
something was really being returned.  So future messageM...
would always just return nil if time were short. or true or false if
it were not short.

Perhaps in the case of
     ( instVar2 future messageMblablablaReturningTo:
          [ :return | instVar1 := return ] )
it could be reduced to
     ( instVar2 future messageMblablabla )
by doing
ObjectFuture>>doesNotUnderstand: aMessage
     | answer process |
     process := self process."or something"
     router
          message: aMessage
          returningTo:[ :r | process signal. answer := r ].
     process wait.
     ^answer
but this might be really slow if there was a lot of it
going on.  But in every case like this, World W would be in the
middle of a method MM and all the external replicated Worlds Wi
would not be in the middle of method MM so the above could not be
done however you did it.  Because just because W is in the middle
of method MM all the replicated Wi most probably won't be.

So you have to get away from this idea that World W will be
in the middle of method MM which would send message M
to Actor A
and get a return value back again from Actor A.  Because none
of the replicate Worlds Wi would be in method MM so their
messages M would have no MMi to return to.  At least in my
way of doing things here.

So each World W and Wi would have a Controller with a Queue
of messages in it and this Queue would take the place of all
methods MM.  There would be no method MM to return values
to in the replicated Wi so we dispense with having message M
return a value to method MM in World W.  The replicated message
M would return true or false to the method MM that originated it.

There is a problem if World W sticks an Actor C into OC at j
and the replicated Worlds Wi stick Actor C into OCi at k not
equal to j.  Now all the ObjectFutures for Actor C are screwed
up.  Okay so every time World Wi is trying to stick some Object
into its OCi it will have to consult with World W the root World.
Only after World W has logged Actor C into is OC at j will all the
other Worlds be allowed to log Actor C into their OCi's at j.
With large Worlds with lots of users there might need to be
several root Worlds that would collaborate.


A new FidoNet of Worlds

Now how are the various Wi connected?  Is each Wi pointing
at all the other Wi?  I think that W should be like a root node
in a FidoNet tree.  So it would connect to 10 Wi and each of
those would connect to 10 Wi and so on.  And each Wi would
point back at W.  So W would be the root node and would
disseminate all replicated messages out to all the Wi.  If W
became a bottle neck then the set of the Wi could be divided
up into groups and each group G would have its own FidoNet tree
going out to all the other Wi, i=1..N.
and all the Wi in group G would send all replicated messages into
the FidoNet tree for group G and those messages would get to all
the Wi.  All the Wi in group G send their messages to the root node
for group G and those messages would then go out to all the other
Wi, i=1..N.  Thus no single Wi would become
a bottle neck.  The entire set of Wi would be divided up into several
overlapping FidoNet trees.  And each Wi
would have its own FidoNet tree to
dump messages into such that each Wi would be connected to
every other Wi but each Wi would not send messages directly
to every
other Wi.  Each Wi would have a list of groups Gj and each
group Gj would have a -from- World and 10 or less -to- Worlds.  So
whenever Wi gets a message from the -from- world of Gj it sends
that message out to all the 10 or less -to- Worlds for Gj.  So you
could have millions of Wi all connected together but each Wi
would not have to point out to millions of other Worlds and yet
all the Wi would be connected.  And yet if such a thing were done
each World would be getting millions of messages.  So the basic
speed of the socket and PC would still be a limiting factor.  Perhaps
large Worlds could be divided up into smaller worlds which would
be like rooms or clustered seats in a stadium.  So Wi would only
have to send messages to the 50 or 100 Worlds that were near to
Wi because characters who own those Worlds were sitting near
to the character who owns Wi in the stadium.  So it might be like
you could see individuals in your section of the stands but all around
that section would just be stub characters.  And when you went from
section to section the section you came to would have individuals
and the section you just left would turn from individuals back into
stubs.  In a stadium where the rooms are sections of seats you
would be able to see through the walls of the rooms to the stubs
inside.

So using FidoNet trees you could divide up large numbers of Worlds
into a single mass where each World is connected to all the other
Worlds indirectly via a tree.  And when there was too much traffic
the large world could be divided up into smaller worlds with Characters
able to walk from subWorld to subWorld.

How would you connect to one of these large Worlds?  There
could be multiple entryway PCs, one for each subGroup of Wi. And
then you would get added to one of the subGroups and added as
a leaf node in one of the subGroup FidoNet trees.  So each entryway
PC would have to keep track of all the Wi in its subGroup and all
the entryway PCs would have to contact each other to do load
balancing so each subGroup would have a similar number of Wi
to all the other subGroups.  There could be a webpage somewhere
that had a list of IP addresses to all the entryway PCs.  And you
would keep trying each one until you got connected to the network
of Worlds that constitute the one single World W.

So it would be like the old FidoNet but it would be Croquet.  And
people could build stuff in their part of the World like they do in
MOOs and MUDs.  So you would try to get a commitment from
people to keep their node in the FidoNet trees running just like they
used to do in the old FidoNet.

So what do you think?  Is this a simpler version of what is going
on in Croquet?  Is the Croquet compiler doing some tricks?

What are the Croquet future related messages that are essential
to know in order to make it work as a user of the future system?

What are the Croquet future related methods that one should know
about in order to understand the implementation of futures?  In other
words what are the different Aspects of futures?  Here aspect means
a sequence of related methods.

Are there any test cases that could be stepped that would show how
to use futures?  These test cases should go from simple to
complex and cover all the different ways of using futures.  They
should be like a guided tour of futures.  To see an example of
how this can be done download and run picoLARC on
sourceforge.net which requires the free Dolphin Smalltalk
community edition.
Reply | Threaded
Open this post in threaded view
|

Re: ( picoVerse-:( a simpler future ) )

Joshua Gargus-2

On Dec 6, 2007, at 10:25 AM, Kjell Godo wrote:

> I would like to propose a simpler version of Croquet world
> syncronization.  With the idea that maybe by telling me
> why it wouldn't work Croquet developers can help me to
> understand how Croquet world syncronization works.

This seems like a very roundabout way to achieve this goal.  Instead,  
why don't you try to ask specific questions about parts that you don't  
understand?

Josh


> The other purpose is perhaps to make a simpler world
> syncronization for picoLARC or Dolphin Smalltalk.


<snip detailed description that I don't have nearly enough time to grok>