Thread runners Marc Nieper-Wißkirchen (10 Nov 2022 20:16 UTC)
Re: Thread runners Marc Nieper-Wißkirchen (11 Nov 2022 15:53 UTC)
Re: Thread runners John Cowan (14 Nov 2022 19:01 UTC)
Re: Thread runners Marc Nieper-Wißkirchen (14 Nov 2022 19:06 UTC)
Re: Thread runners John Cowan (18 Feb 2023 03:00 UTC)
Re: Thread runners Marc Nieper-Wißkirchen (18 Feb 2023 14:42 UTC)

Re: Thread runners Marc Nieper-Wißkirchen 11 Nov 2022 15:53 UTC

Having thought about this a bit more, I think a good answer to 4. is
that forcefully terminating the thread that holds the (last reference
to the) thread runner should not terminate the registered threads as
well.  A good interpretation of forcefully terminating a thread (which
won't unwind the call stack) is to put a thread to sleep (forever).

I would not make the with-thread-runner procedure return the
registered threads but let the user choose to return the threads that
are of interest to the caller of with-thread-runner.  This way, the
user can also return other values along.

Since we allow the thread type to be extended, we cannot easily add an
optional thread runner argument to make-thread.  But we can let the
thread runner call make-thread:

(with-thread-runner
  (lambda (in-runner)
    (define t1 (in-runner make-thread (lambda () 1))
    (define t2 (in-runner make-thread (lambda () (thread-join! t1))
    (thread-start! t1)
    (thread-start! t2)
    t2))

This will join the second thread and return it.

Issue 3. is probably best resolved if the threads are joined whenever
the dynamic extent of the procedure argument to with-thread-runner ist
left.

This leads us to an implementation like the following one:

(define with-thread-runner
  (case-lambda
    [(proc) (proc (lambda () #f)]
    [(proc timeout-thunk)
     (define registry '())
     (assert (procedure? proc))
     (assert (procedure? proc))
     (with-interrupts-disabled
       (dynamic-wind ()
         (lambda () (values))
         (lambda ()
           (with-interrupts-enabled
             (proc
               (lambda (make-thread . args)
                 (assert (procedure? make-thread))
                 (let ([thread (apply make-thread args)])
                   (assert (thread? thread))
                   (with-interrupts-disabled (set! registry (cons
thread registry)))
                 thread)))))
         (lambda ()
           (define timeout (with-interrupts-enabled (timeout-thunk)))
           (let f ()
             (unless (null? registry)
               (let ([thread (car registry)])
                 (set! registry (cdr registry))
                 (with-interrupts-enabled (thread-join! thread timeout))
                 (f)))))]))

(The above is still not thread-safe as the IN-RUNNER procedure may be
passed to other threads.)

As the above can be implemented portably in what is already there in
SRFI 226, I tend to think that it is better to put with-thread-runner
into a separate SRFI together with other higher-level multi-threading
procedures.  For example, we also want something like with-lock that
mutex-guards a block of code, etc.

Am Do., 10. Nov. 2022 um 21:16 Uhr schrieb Marc Nieper-Wißkirchen
<xxxxxx@nieper-wisskirchen.de>:
>
> Concerning the design of the thread runner facility, the following
> needs to be resolved:
>
> 1. John, you suggested adding an optional timeout parameter to
> `with-thread-runner'.  It may be better to add an optional thunk
> argument that is called when the thread runner procedure returns. The
> thunk is supposed to return a timeout. This can be helpful if the
> timeout should depend on the current program state.
>
> 2. If a timeout happens, what should be the return values for the
> threads that didn't finish in time? Or no particular return value and
> let the following thread-join! with a timeout of 0 detect it?
>
> 3. The body of the thread runner procedure can be entered several
> times, the typical problem in Scheme when trying to make things from
> other languages work.  We could give a thread-running procedure
> unprotect-wind semantics, not allowing reentering the dynamic extent,
> but this could limit the facility's applicability.
>
> 4. What should happen when the thread runner is GC'd (e,g, because a
> thread is abnormally terminated)? Should the registered threads be
> terminated as well?
>
> I would like to hear your opinions about these design issues.
>
> Thanks,
>
> Marc