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

Re: threads & dynamic environment & continuations

This page is part of the web mail archives of SRFI 18 from before July 7th, 2015. The new archives for SRFI 18 contain all messages, not just those from before July 7th, 2015.

   > I think the current document should state these things explicitly. To sum up:
   >     - dynamic env is part of the continuation, 
   >     - hence, throwing to a continuation changes the dynamic env.

   This was so "obvious" to me that I did not mention it.  On the other
   hand, is it the role of SRFI 18 to specify this, or a dynamic
   environment SRFI?

Well, your SRFI does not mention fluid-var sorts of notions at all, so
if you are suggesting it's outside the scope of the SRFI, and up to a
fluid-var SRFI to get it right, then I think you are right.

However, SRFI-18 *does* explicitly mention the dynamic *exception*
environment. So I think it *would* be a good thing to explicitly state this is
part of the continuation.

   As for call/null-continuation, you can get the same result with
   call-with-current-continuation, if you also have dynamic variables.
   I.e. you capture the primordial continuation of the thread and bind
   that to a dynamic variable that you can then use to return to the
   primordial continuation.

I don't think this gets you all the way home. For example, assume K
is a continuation -- something we made with CALL/CC. This won't work:
    (k (thunk))
That sort-of punts the current continuation and "installs" K as
the continuation for the THUNK call. But a simple implementation of
Scheme wouldn't even realise that K was a continuation until THUNK
had returned -- we wanted to punt the current stack eagerly, *before*
we leapt off to THUNK. See the problem?

However, it's worse than implementation/storage-leak issue -- we don't even
get everything we wanted *semantically*. THUNK runs in the current dynamic
environment (e.g., for exception handlers). We don't get to install K's
dynamic env until THUNK returns and we do the call (throw) to THUNK. Oops.
What I *wanted* was the ability to say "call THUNK with continuation K. That
is, throw away the current continuation *now*, and *then* call THUNK -- run it
with continuation K, *including* K's dynamic env."

We still don't have that. So you gotta build it in. There are three paths
I see:
    - A primitive like (call/continuation kont thunk)
      Calls THUNK with continuation KONT.

      The problem with this is that we are not "playing fair" with our
      continuations. Scheme continuations are *not* "true continuations." They
      are *procedures* that are somehow closed over a true continuation.
      CALL/CONTINUATION has to reach into KONT and pull out the
      true continuation in order to have KONT's dynamic env installed
      before running THUNK. So the procedures constructed by CALL/CC
      are somehow special -- i.e., KONT is not the same as 
      (LAMBDA (X) (KONT X)) when KONT is a continuation made by CALL/CC.


    - A primitive like CALL/NULL-CONTINUATION
      We can use it like this
          (call/null-continuation (lambda () (kont (thunk))))
      This solves our space-leak problem -- the CALL/NULL-CONTINUATION
      operator can drop the current stack & dynamic env *before* calling
      THUNK. But we haven't solved  our semantics problem -- we still aren't
      running THUNK in KONT's dynamic environment.


    - Export the actual underlying non-procedure continuations:
        (call-with-current-kontinuation proc)
	(throw k val ...) ; THROW is n-ary -- 0 or more VAL's
	(call/kontinuation k thunk) ; Run THUNK with kontinuation K.

	;; R5RS CALL/CC can be defined with the kontinuation mechanism:
	(define (call-with-current-continuation proc)
            (lambda (k) (proc (lambda vals (apply throw k vals))))))

This is an issue that bedevils thread/continuation implementations *all the
time.* And it's sufficiently subtle that people never figure it out until
their threaded programs start blowing out the heap mysteriously... and then
there's no simple fix. Get it right now.

   Should SRFI 18 specify a mechanism for manipulating the dynamic
   environment?  I'm fond of:

      (dynamic-define var-name expression)
      (dynamic-let ((var-name expression)) body)
      (dynamic-ref var-name)
      (dynamic-set! var-name expression)

   Note that in this scheme, dynamic variables and lexically scoped
   variables are completely independent.

Uhh... I can see plusses & minusses. I'd say punt it, and let's get that
done in a separate SRFI, which can contain language mentioning how fluids
interact with thread systems, explicitly citing SRFI-18 by way of example.

But put into SRFI-18 some *generic* language saying that dynamic binding
facilities should be part of the continuation, hence threads pick up
a new dynamic env on a continuation throw.

How do you like that?