Scoped variables

dean.long at dean.long at
Wed Dec 5 08:29:04 UTC 2018

On 12/4/18 4:33 PM, John Rose wrote:
>> Currently the stack
>> is kept when the continuation is unmounted.  The cache keeps track of the stack
>> that has the most recent binding.  This is because the most recent binding could
>> be in a parent continuation or parent thread.
> Good that you are covering that case.  These things need to span all the
> way up the callee-to-caller relation, not stop at continuation boundaries.
> It also adds complexity:  If a parent frame returns, perhaps during concurrent
> execution in a different fiber, but a child frame still needs access to a frame
> local, the frame local binding must be preserved somehow for the child's use.
> Suggestion:  This is one reason*constant*  bindings are preferable to*variable*
> bindings.  It's easier to "fork" a constant to distinct clients than share a link to
> a variable, and there's no loss of generality.

In order for the parent frame to return without the child frame 
returning first, I believe
they must be in separate continuations.  When the child frame tries to 
access the
frame local, the logic should match what a stack walk would find. If 
there is no binding
in the child frame's continuation, we go to the parent continuation.  If 
the top binding in
the parent went away because a frame returned, we keep walking frames.  
In my
prototype, we would miss in the cache, and then look in the stack associated
with each parent continuation.  If I understand correctly, in my 
prototype, the opportunity
for binding "forking" and added complexity on mount/unmount would show up in
trying to cleverly preserve existing cache entries rather rebuilding 
from a clean cache
each time, which is something I considered but haven't attempted.

>> Without fibers, continuations, or
>> global bindings, then the most recent binding reduces to the thread binding, and
>> there shouldn't be any need for a cache.
> (The model can be thread-agnostic, right?  You just have to find the frame that
> defined the local; doesn't matter who owns it or how.)

Yes, it can be thread-agnostic.  In the loom world, if there was always 
a default
continuation, even for threads, then I could use that.  But for now, I 
put the binding
stacks and cache in what I call a FrameLocalsProvider object, which is 
stored as close
to where the frames live as possible: in Threads and Continuations in 
loom.  The
getParent() operation returns a FrameLocalsProvider as well.

>> The cache is a map and is reset every time the continuation is mounted/unmounted.
> Yep.  We could also try to salvage the cache at unmount and reuse it at remount.
> This would be pretty easy if the structure of the cache is (a) rooted in the fiber and
> (b) allocated in the frames of the fiber.  Major events like JIT or deopt could reset the
> cache by zeroing out the root variables in the fiber.

I think it would be safe to clone the parent cache when a fresh 
continuation is mounted.
On a resume, perhaps we could salvage the old cache if 1) we have the 
same parent,
and 2) that parent is not "dirty" (hasn't been umounted and remounted).

>> The stack is looked up in a WeakHashMap, making it basically equivalent to a
>> ThreadLocal.  If we had long-lived frame locals, then perhaps we could simply look
>> them up in an array, but then why not use a long-lived thread local instead that
>> could also be looked up in an array?
> That smells like footprint to me.  There's a WHM per active fiber?

Yes, it's per FrameLocalsProvider.

> I suppose it's
> OK if the WHM is discarded when the fiber unmounts.  (Or keep it in a weak
> reference field on the fiber?)
>> To reduce Continuation storage requirements, we could choose not to retain the
>> stacks after an unmount, but instead lazily rebuild using stack walks.  Right now
>> I'm only using the deep stack walk to verify the results of the cache + stack
>> lookup.
> Yep.  That's the sort of trade-off that faces us.  Another point about allocating
> stacks inside of actual stack frames:  Reconstructing them simply requires
> walking the stack and noticing where they were.

I did try to approximate having slots in native stack frames, using 
local variables.
Our current stackwalk code allows us to read locals in frames but not 
write to them
(at least not without deoptimizing), so I do use the mutable "box" you 
described to
store stack nodes, and those are linked to earlier nodes.

Thanks for all the feedback!


> — John

More information about the loom-dev mailing list