Re: New draft (#2) of SRFI 226: Control Features
Marc Feeley 29 Oct 2022 02:53 UTC
> On Oct 27, 2022, at 2:01 AM, Marc Nieper-Wißkirchen <xxxxxx@gmail.com> wrote:
>
> I am also still hoping for a reply from Marc in the discussion about
> "weak threads".
>
> [...]
Sorry for not getting back sooner. I have continued my reading of the SRFI 226 spec. Unfortunately my time is still constrained and the spec is huge so my comments are bound to be more superficial than I’d like. Here's what stands out.
(call-in-continuation cont thunk) misses an opportunity of having the more general form (call-in-continuation cont proc arg1...) so that it can be called with a procedure and as many arguments as needed. Instead of (call-in-continuation k (lambda () (values tmp ...)) you could write (call-in-continuation k values tmp ...). See the definition of the continuation-graft form that you cite:
"Remark: That a primitive like call-in-continuation is needed, was already noted in A better API for first-class continuations. In that paper, it is called continuation-graft. (The two accompanying procedures continuation-capture and continuation-return in that paper are just call/cc and apply in the context of this SRFI, which treats continuations as procedures for compatiblity reasons.)"
Note also that one of the main points of the "Better API" paper is to treat continuations as a specific type different from procedures so that the burden of the procedure representation can be avoided (conceptual and also run-time cost for creating the procedure), and also have other operations such as (continuation? obj), (continuation-length k), etc. I view "continuations as procedures" to be a historical blunder that was motivated by CPS style. If you have ever tried to explain how call/cc works to students you will probably understand what I'm talking about: "call/cc receives a procedure and calls this procedure with a procedure that represents the continuation". Too many procedures for most students. With SRFI 226 there's an opportunity to correct this by making (call-with-non-composable-continuation proc) call proc with a continuation object that is separate from procedures. It changes very little to the API, except that those continuations have to be called with (call-in-continuation k values ...) or some new more specific procedure (return-to-continuation k ...).
Concerning (thread-terminate! thread), the part "the current thread waits until the termination of thread has occurred" is not ideal. This was also specified by SRFI 18, and it is OK in a single processor system (because the scheduler is centralized), but I now think it causes issues in a multiprocessor system because it is impossible to predict how long the wait might be. It is better to have an asynchronous termination, and to use (thread-join! thread), possibly with timeout, when it is necessary to ensure the thread has terminated before proceeding.
An alternative to thread-terminate! that is similarly powerful and more elegant is to have an asynchronous (thread-interrupt! thread thunk) procedure that causes thunk to be called at a safe point at the current point of execution of the target thread. The thunk could then call raise or abort-current-continuation to terminate the thread “from within”, allowing the target thread to do some cleanup.
Concerning the addition of (mutex-owner mutex) as a companion to (mutex-state mutex), this has introduced a race condition. If (eq? (mutex-state mutex) 'owned) is true then extracting the owner thread with (mutex-owner mutex) may return #f. The API of the SRFI 18 (mutex-state mutex) was designed to not have this race condition.
The mutex-unlock! procedure's parameter list does not have a timeout parameter, but the description talks about that parameter. Timeouts are important on all blocking operations.
The section on thread locals is rather vague and unconvincing. The thread-specific field has been removed because "If these are needed, weak hash tables could be used instead." but the same can be said for thread locals which are a thin wrapper around weak hash tables indexed by thread. The point of thread-specific was to have constant time (with small constant) access to thread specific data.
I’ll have to address weak threads at some other time… (and also the initial continuations section which I have to read carefully).
Marc