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)
|
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