Re: [Vala] Implicit lamdas/closures



Based on the idea I posted last night, I've done a proof-of-concept for what I'm going to rename for the third time as "unrolled continuations" which allows asynchronous clients (in server-client relationship) to be coded synchronously but still implemented asynchronously by means of continuations. This is most useful where the client also happens to be an asynchronous server as samba4 is in my case.

The work is built upon the (not yet written) local variable lambda support for vala.

I have for download a small vala project that uses the gtk event loop to drive asynchronous handlers for continuations.
http://www.liddicott.com/~sam/sam/contin-0.1.0.tar.gz

When running the demo, if you click on the first button it makes an basic rpc server object and issues an async request.
Click on the second button to answer that request.

When the answer is received a loop commences which issues 3 more requests that need responding too. Altogether the respond button is clicked 4 times, final response is then ready to send back to the non-existent client.

This shows off that continuations can break-off and carry-on halfway through a loop.

This all makes more sense after reviewing rpc_server.vala and rpc_server.c

rpc_server.vala uses a notation I will discuss below, but of course vala can't handle this, so rpc_server.c and rpc_server.h have been severely tweaked to have the form which I think ought to be generated.

I'm proposing a concept of inline lambdas that are supported using C's label notation; e.g.

int a_function(int x) {
 do_something(x);
a_label:
 do_someting_else(x);

and within a_function, "a_label" can be used as if it were a function pointer which internally takes 1 void* parameter which points to the heap-allocated locals as is currently on the roadmap for lambda functions.

The labels and heap-allocated local variables allow a callback to continue part-way through the function as if it had never left off.

I also propose this extended notation where parameters can follow the label. If the parameters are already declared as local variables then the type could (must?) be omitted. They are a means of the callback passing extra data for cases where the callback provides more than just a void* private data parameter. It is important to realise that the label could also be reached WITHOUT a callback occuring so the default value is also used to initialize the local variable as well as a default parameter when (if) the callback occurs. (Hmmm... do default parameters work for delegates?)

e.g.
int a_function(int x = 100) {
 do_something(x);
// note that the label also has parameters
a_label(int y = 10):
do_something....

so that the generated wrapper will be declared something like:
int a_function__a_label(a_function__local_vars *vars, int y) {
 vars->y=y;
 return a_function__body(vars,1);
}

where the 2nd parameter 1 signifies the first label that a case statement at the top of in a_function__body will "goto" (see below)

The a_function will generate two C functions, one to prepare and initalize the heap:
int a_function(int x) {
 struct a_function_vars vars=malloc(sizeof(struct a_function_vars));
 vars->parameters.x=x;
 vars->locals.y=10;
 return a_function__body(vars, 0);
}

and then the real function body:
static int a_function__body(struct a_function__vars *vars, int label) {
 switch (label) {
 case 0: break;
 case 1: goto a_label;
 }
 do_something(vars->locals.x);
a_label:
...
...

So, when making an async call that wants to use the rest of the function as a continuation:

int a_function(int x) {
 request_callback(next_position);
 return;
next_position:
 // do some more here
 return;
}

For purity the async "return" maybe should be hidden? For samba4 I'll be using a vapi-wrapped C macro which checks that the callback was allocated and then returns so vala won't even know there was a return - which also aids readability, but the other problem is that if vala knows there is a return it starts unref'ing al the local variables which is clearly wrong for a lambda - I guess vala will have to deal with this when it finishes lambda support.

I imagine the heap-allocated local-vars struct will be ref-counted, so that the variables will all get free'd when there are no more continuation references. Otherwise maybe all the heap-allocated local vars could be explicitly freed with "finally return" or some such strong hint.

Maybe for even better neatness we want an implicit label called "next" or something, which if used causes vala to effectively insert a return_default after this vala statement, and imply a label following that..

Sam



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]