Shayne .. when you're playing with fthread be aware of the
well known "gotcha".

When you do something like

        var x = 1;
        spawn_fthread { .. println$ x; };
        x = 2;

the "x" in the fthread is just a reference to a variable.
There's no knowing what the value will be, it may be 1 or 2.
Unlike pthreads the assignment can't be pre-empted half
way through resulting in crap, but still the order of access
is notionally non-deterministic.

Now you may think if you do this:

        proc myproc (myx:int) () { println$ myx; }
        spawn_fthread { myproc x; }

that will use the current value at the point of spawning but you'd be wrong.
Even when "myproc" starts to run it is not copying the value of x at the time
it runs into myx, because myx can be lazily evaluated. In particular expect this
code to reduce by inlining and substitution to just saying "println$ x; ".

Again you may think this will fix it:

        proc myproc (var myx:int) () { .. }

because that forces myx to be eagerly evaluated. Again, you'd be wrong.
All that means it is that myx is calculated before the print. So inlining
myproc the code reads:

        myx = x;
        println myx;

instead of

        println x;

In other words, what's being "assigned to myx" is just the current value
of x. The only way to fix this is to ensure that the closure is formed
at the time you want the value of x, one way to do that is:

        myproc ...
        var closure = myproc x;
        spawn_fthread closure;

"Technically" the semantics don't even guarantee that will work,
but it does at the moment. You can also mark a procedure 
"noinline" which prevents it being inlined, which means a closure
has to be formed to call it. Note that again is not a guarantee 
semantically. That's because "noinline" means the procedure must
not be inlined at its point of call. It does NOT mean the arguments
to the procedure cannot be inlined into the procedure.

As already mentioned this *includes* making the parameter a var.
This does not technically ensure the "right" value of the variable
is assigned. All it ensures is that the assignment takes place
before the body of the procedure evaluates .. that can be ANY time
before. With lazy evaluation you're ensuring the parameter is not
assigned by the argument expression until it is used, which is typically
implemented in Felix by simply inlining the expression, but it can
also be done by passing a closure (which inlines the expression 
for later evaluation).

With fthreads there is a semantically guaranteed way to fix this problem.
Use schannels. Instead of passing a value .. WRITE the value down an
schannel at the point in time you want to, and read it in the fthread.

For example:

        var x = 1;
        var ich, och = @mk_ioschannel_pair;
        spawn_fthread { var y = read ich; println$ y; };
        ++x;
        write (och, x); // writes 2, no questions asked!
        ++x;

We will always print 2 because the value of x at the time
the write occurs is 2. The increment happens after that,
and the "2" is already gone up the channel. In fact Felix
copies the value of x to do this, and passes a pointer 
to the copy:

        var p = new x;
        send p along och;

Note of course if x is a mutable data structure represented by a pointer
you're only copying the pointer :)

With ordinary procedures and functions you cannot use schannels,
because read and write block the fthread. You can, however, cheat
the way I just did: 

        var x = 1;
        var p = new x; // copies the value in x NOW
        ++x;
        f p; // passes p

This will only work if p is a var, for a val any "p" can be replaced
by its initialiser. Again this is a cheat, its not guaranteed semantically.
In this case the rule guarantee that new x is done before ++x,
and the call of f must be done after ++x.

If we remove the ++x, it still works because "f p" is a direct call.
But suppose we use a closure:

        proc f (px:&int) () { println$ *px; }

and write:

        var x = 1;
        var p = new x;
        var cls = f p;
        ++x;
        cls ();

it will still work but it's less certain .. p has to be bound at the point
cls is calculated .. but p is just a variable name .. there's no assurance
that the value in "slot p" is calculated yet. It has to be calculated before
ps is used .. but that's AFTER x is incremented. I actually think this
is assured by the semantics .. but i'm not certain (particularly as the
semantics are just in my head :)

--
john skaller
skal...@users.sourceforge.net
http://felix-lang.org




------------------------------------------------------------------------------
CenturyLink Cloud: The Leader in Enterprise Cloud Services.
Learn Why More Businesses Are Choosing CenturyLink Cloud For
Critical Workloads, Development Environments & Everything In Between.
Get a Quote or Start a Free Trial Today. 
http://pubads.g.doubleclick.net/gampad/clk?id=119420431&iu=/4140/ostg.clktrk
_______________________________________________
Felix-language mailing list
Felix-language@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/felix-language

Reply via email to