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