Doing it better 1999-04-15 ### My doodlings are taking shape. I have made a rather radical change to the memory architecture as a result of my troubles with multiple inboxes. It was prompted by the desire to move the inbox complexity into a higher level, which requires a few pointer structures. It involves recognising that if you take away the synchronisation/communication role of channels, you still have a useful thing, which I called a buffer. Here's how it works: An 'Agent' is a program and a thread and (one) inbox, but it has no storage apart from a few registers. A 'Buffer' is a (possibly empty) array of integers, and a (possibly empty) array of pointers to buffers (which can be null). In addition, a buffer has a pointer to an Agent, which may be null. There are plentiful instructions for loading, storing and processing the integers in a buffer, as you'd expect of any processor. There are a few more for swapping pointers between registers and buffers. You can only swap one pointer with another - you can never copy or overwrite a pointer. Lingo: Buffers are linear. This ensures that 1) a pointer to a buffer is the only pointer to that buffer (no sharing), and 2) every buffer has an Agent which has a pointer to it, possibly via other buffers. Aside: If you think about it, it also ensures that there are no cyclic pointer structures, because in order to put a pointer in a buffer the pointer to the buffer must be in a register, so there's no way you can store the last pointer of a cycle. You might be amused to work out how may pointers must be held in registers before your processor is capable of constructing an arbitrary pointer structure (within the rules). I made it three, which is more than you'd expect. But not all of them need to work as part of an address calculation. If you think again, there is no way a pointer can get from a register in one Agent to a register in another. In fact, you can trivially analyse the pointer structure and assign an owner (an Agent) to each Buffer in such a way that ownership is preserved by all the instructions. So now we add communication primitives: Terminology: A buffer has a pointer to an Agent. If this pointer is null, we say that the buffer is not offered. Otherwise, we say it has been offered by the Agent it points to. A Buffer which is not offered can be offered by the Agent which owns it. The Agent just gets the Buffer's pointer into a register, and then executes an offer instruction. A Buffer which is offered can be accepted, in a similar manner. At this point, it magically vanishes, to be replaced by a null pointer, and instead it joins the queue at its offerer's inbox. An Agent can wait for a buffer to arrive in its inbox. The only other buffer-related primitives are: create and dispose, which should be rare as buffers can be reused. The type-scheme is unchanged from before, as is the need to send an offered buffer as part of any message which requires a reply. Also as before, in order to share data between multiple Agents, you have to put an Agent in front of it as a guard. It can then respond to messages from multiple other Agents, and forward their requests to the Buffer. This is all deliberate. One point which has just occurred to me: Buffers have already got built-in the ability to be part of a queue. Maybe I should expose this inside the system? Might be useful. Waddya think of it all? Source code for interpreter (basically one big switch statement, 112 very similar cases) is available on request. Alistair