Hei,
Thanks for the feedback once more.
1/ function and encapsulated data
I do not intend to draw heavily on the 'class-like' analogy, but aim to paint the picture of data and function encapsulation, constructors and destructors. I'll try to clarify my intent and issues of encapsulation for mobile processes.
1.1/ Intent.
1.1.a/ Firstly a class-like structure, like Mob::var, is not a syntax the programmer would write; I will re-colour-code such references to signify this more clearly. It intends to draw attention to semantics the compiler needs to apply to mobile processes.
1.1.b/ Secondly PiXC extends XC, and not C or C++. Neither classes nor namespaces are supported in XC. (And there is no Occam-like 'PROC' declaration, which could otherwise be developed.)
1.1.c/ Thirdly PiXC should impose the minimal set of changes on XC, that do what is needed. Section A.3 of Programming XC, names and storage, helps to estimate the likely cost of extensions. (However, we ought to debate conveniences that go a bit further than the barest need, such as namespaces, to test their usefulness or expressiveness, but I wouldn't want to end up with a complete language/compiler re-write.)
Your suggestion to use namespaces instead of a class-like analogy would seem to achieve a similar end, but implementing either namespaces or classes more fully increases what the XC compiler would have to do anew, when I intend to implement neither. Namespaces provide a context for symbols, and group related functions and data spread over several source files or libraries. I don't think the XC compiler maintains seperate contexts or searches multiple source files extensively.
To avoid different interpretations, perhaps we'd best avoid borrowing class or namespace terms at all and instead refer to mobile contexts. But whatever the form (internal to the compiler), issues remain that mobile process encapsulation resolves.
1.2/ Issues.
Encapsulation is needed for the XC compiler to allocate storage space for mobile processes, which are i/ long-lived, and ii/ can migrate. Encapsulation solves several implementation issues, including memory map, liveness, coherency and scalability, amongst others.
1.2.a/ Memory Maps (per processor).
The PiXC compiler should generate code for (mobile) processes exactly once. During compilation global variables (or memory allocated to hold them) are fixed offset from dp (register 13). If external variables were used for mobile member variables, then either:
1.2.ai/ the memory maps of different processors would have to be made the same, in order for the common mobile process object code to access member variables, which is wasteful, or
1.2.aii/ the object code would need to be compiled seperately for each processor,in order to access member variables in the different memory maps, which is also wasteful.
1.2.a.iii/ By using encapsulation, the compiler could offset process member variables with respect to another register, say r11, per thread. Or the compiler could treat member variables like far pointers, and go through a jump table. Or another method could be used.
(Also for the memory-map reason, I don't think mobiles would access other global variables, and channels should be preferred for data exchange. However, XC allows global variable access, so I'm moot.)
1.2.b/ Liveness. A mobile process comes into being (is constructed, or initialised, or run) when it is received by a process. Similarly it deceases (is destructed, or terminated) when it is sent by a process. So if external (non-static) data were used, each variable would need a 'lifetime' guard, which would need testing on each and every access by a non-mobile process.
1.2.c/ Coherency (touched on in 2.2.1.3.4).
1.2.ci/ When a process migrates from a source to a destination processor, its destination data members need to store the values already calculated in the source processor. But if external (non-static) data were used, overwriting them would appear as a corruption to other processes running on the destination processor.
1.2.cii/ And running on the new destination processor, changes written by the mobile process to global data would not be visible to other processes running on the original source processor.
1.2.ciii/ The Hoare/Dijkstra multiple-update problem requires a guard on shared global data; accessing a global from parallel processes is guarded in XC at compile time, so only one process is permitted to write to a global. This places restrictions I don't want to impose on mobile member variables.
1.2.d/ Scalability. If in the future mobility is extended to heterogenous networks then we wouldn't want to change/break existing programs or language features. And encapsulation solves mobility problems in a general way, which I expect will scale up to heterogenous processor networks.
2/ On the roaming pattern
Thanks for the bug fix. It should read
Code: Select all
void roam0( chanend c )
{
int t = 1; // initially assume task 1
c :> p1; // wait to receive mobile process p1
for( ; t > 0; )
{
// calculate next task, t, on p1 state
switch( t )
{
case 1:
{
// perform task t1, interacting with p1
}
break;
...
default :
{
// not a task for roam0 in this context
c <: p1; // migrate mobile process p1
t = 0; // exit loop
}
}
}
}
I prefer just the one chanend as channels are not the focus of this pattern. But you could use 2 channels as you suggested.
3/ ..a practical example perhaps using I/O
The examples do remain simple, to communicate concepts while the ideas are still very new. Also it's hard to program in PiXC without a compiler to correct me ;)
With regard to IO (ports) and mobile processes in general, there isn't a concept of a mobile port like there is a mobile channel, as a port is a fixed hardware pin. Rather, I envisage an Operating System selecting on port cases and using mobile processes to service inputs. By way of example, using traditional OS, I often write 'input ready' code in which a device driver handles some input and passes data (pointers) to several application threads. In my vision for a dynamic operating system, a mobile process can be passed between the application threads, instead of using a semaphore.
I will try to think about more detailed examples, while avoiding obfuscation.
4/ concurrent processing patterns
I wouldn't call it a design pattern, but
Code: Select all
select
case c :> void:
PAR
{
proca( );
procb( );
}
is similar to receiving a mobile process procb,
Code: Select all
void proca( chanend c )
{
c :> procb;
// rest of proca
c <: procb;
}
in which proca and procb run concurrently.
Please respond if I've got something wrong or it remains unclear, or any other comments.