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. |
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> |
Free forum by Nabble | Edit this page |