Thanks. So here's my main concern. Suppose the following code. Again (pause) thing is to avoid Chez's limitation.====(define cc #f)
(define p (make-thread-parameter 1))
(define (pause) (sleep (make-time 'time-duration 0 10000)))(fork-thread
(lambda ()
(call/cc
(lambda (k0)
(parameterize ((p 2))
(p 3)
(call/cc
(lambda (k1)
(set! cc k1)
(format #t ">>> ~s\n" (p))
(k0)))
(format #t ">>> ~s\n" (p))
)))
(pause)))
(fork-thread cc)====Chez prints ">>> 3" for both threads. That is, the parameterization captured by call/cc sees the mutation of the parameterized p across the threads. Gauche does the same with the equivalent code, even parameters use thread-specific storage.That's because dynamic-wind's after thunk saves the mutated value, and restores it when the continuation is invoked.I think in SRFI-226, the second thread prints ">>> 2", because the mutation is invisible to other threads.The above code can happen when async-await type control flow is implemented in continuations and a thread pool, that is, you won't know if the future is run in the same thread or a different thread. We could say srfi-226 thead parameters can't be used with such purpose, but that can pose limitations of thread parameters----library calls deep inside the call tree may use a future which is run in a different thread...With Gauche and Chez thread parameters, you can ensure the parameter's portability across threads by parameterizing concerned parameters early in the call tree.On Wed, Sep 13, 2023 at 7:19 AM Marc Nieper-Wißkirchen <xxxxxx@gmail.com> wrote:The interaction of threads with continuations seems to be complicated in Chez Scheme (where parameters are implemented using dynamic-wind). I have no experience here.As far as SRFI 226 is concerned, Gauche is correct. You can add the following test to the SRFI 226 unit tests:(test 2
(let ([p (make-parameter 1)])
(let ([t (make-thread
(lambda ()
(parameterize ([p 2])
((call/cc
(lambda (cc)
(lambda () cc)))))))])
(thread-start! t)
(let ([cc (thread-join! t)])
(let ([t (make-thread
(lambda ()
(cc (lambda () (p)))))])
(thread-start! t)
(thread-join! t))))))A slightly more elegant version:
(test 2
(let ([p (make-parameter 1)])
(let ([t (make-thread
(lambda ()
(parameterize ([p 2])
(call/cc values))))])
(thread-start! t)
(let ([cc (thread-join! t)])
(let ([t (make-thread
(lambda ()
(call-in-continuation cc p)))])
(thread-start! t)
(thread-join! t))))))Am Mi., 13. Sept. 2023 um 18:38 Uhr schrieb Shiro Kawai <xxxxxx@gmail.com>:I inserted pause b/c Chez doesn't seem to allow a continuation to be invoked when its capturing thread has already terminated.Anyway, my thought process is this:- The call/cc captures the current parameterization, which is p == 2.- Thus, when that continuation is invoked in a different thread, the parameterization p == 2 is restored, and prints ">>> 2".So, SRFI-226 disagrees with Chez and agrees with Gauche, correct?On Wed, Sep 13, 2023 at 6:30 AM Marc Nieper-Wißkirchen <xxxxxx@gmail.com> wrote:Don't you have a race condition in your example because you don't wait for the first thread to terminate?In any case, the current set of parameters (the "current parameterization" in the SRFI 226 language) is part of the current continuation. So when you replace the current continuation, you replace the current parameterization as well.Does this help?Am Mi., 13. Sept. 2023 um 18:17 Uhr schrieb Shiro Kawai <xxxxxx@gmail.com>:I brought it up because the SRFI-226 thread parameter behaves differently from Gauche legacy parameter which also uses thread-local storage (The difference actually comes from the fact that parameterize is realized by dynamic-wind in legacy parameter.)But maybe I don't fully understand the implication of interaction of parameters. So let me back up one step.If I run the following code in Chez REPL, I get ">>>2" from the first thread, and ">>>1" from the second thread. If I run the equivalent code in Gauche, the second thread prints ">>>2" as well, for the call/cc captures the dynamic environment and restores it in the second thead. Does SRFI-226 agrees with Chez?====(define cc #f)
(define p (make-parameter 1))
(define (pause) (sleep (make-time 'time-duration 0 10000)))
(fork-thread
(lambda ()
(parameterize ((p 2))
(call/cc (lambda (k) (set! cc k)))
(format #t ">>> ~s\n" (p))
(pause))))
(fork-thread cc)====On Tue, Sep 12, 2023 at 9:38 PM Marc Nieper-Wißkirchen <xxxxxx@gmail.com> wrote:Parameters can be equivalently expressed in terms of fluids (see SRFI 226), which mimic variables. Thread parameters are to parameters what thread-local fluids are to fluids, so one argument is symmetry.Often, you want to use thread-local objects in conjunction with the parameter mechanism, so thread parameters come in handy. The nice thing about them being defined natively is that they can then share the same form to parameterize them as non-thread local parameters.For good "real world" examples, take a look at the documentation of the Chez system. Some of its parameters are non-thread local; others are thread-local. (NB: Chez's parameters are just parameter-like objects in the language of SRFI 226.)Am Mi., 13. Sept. 2023 um 08:19 Uhr schrieb Shiro Kawai <xxxxxx@gmail.com>:I'm preparing for a new release of Gauche which supports part of SRFI-226. While documenting it, I noticed I couldn't recall why thread parameters were added, and what for. I remember It was added at some point, but I can't find any discussion about it. I may have just overlooked it. Can somebody (Marc?) help me recall it?I do remember we discussed whether parameter storage should be thread-specific or not, and I concurred that it should not. Thread-local storage can be realized by thread locals. Is there a case that you need parameters to be thread specific?--shiro