Subject: Re: bug#76200: `with-parameters*' seems to create a continuation barrier

From: "W. Kosior" <[email-masked]>
Date: Fri, 14 Feb 2025 11:51:28 +0100

Hi,

thanks for pointing me in the right direction.

The naming is making me dizzy (the name of `with-fluid*' procedure from
boot-9.scm reads so similar to `with-fluids*' from fluids.c).

Nevertheless, I think I found one relevant Andy Windo's commit from 2010:

    c32b7c4cef1c63a677a1c447a0386e90ab2ecd42
    Remove with-fluids; replaced by with-fluid* and inlined push-fluid primops

So, previously the procedures `with-fluid*' and `with-fluids*' were
defined in fluids.c as `scm_with_fluid' and `scm_with_fluids',
respectively.  One can see they actually did the same thing back then,
except one dealt with multiple fluids at once.  And they must have both
suffered from the continuation barrier issue.

In addition, the macro `with-fluids' used to be defined in expand.c
before 2010.

In that commit, Andy defined `with-fluid*' and `with-fluids' in scheme
in boot-9.scm.  The C function `scm_with_fluid' remained, unexported to
scheme, likely for compatibility with Libguile users.  At the same
the `scm_with_fluids' exported as `with-fluids*' was left untouched,
perhaps by mistake.

What's a clear mistake is part of the description of that commit.  It
says

    * libguile/fluids.c (scm_with_fluid): Rename from scm_with_fluids, and
      don't expose to Scheme.

The function did not get renamed.  Both `scm_with_fluid' and
`scm_with_fluids' existed before and still live alongside each other.

> If the transform from let to let* extends to
> with-fluids->with-fluids* it should be a trivial thing to put into
> boot-9.scm, but I rarely dabble with dynamic environments and
> whatever is in the documentation is somewhat opaque. 

In the light of my findings above it'll likely be clear the transform is
different as it is a macro->procedure shift.

But then reimplementing `with-fluids*' should still be easy.  For
example

--8<---------------cut here---------------start------------->8---
(define (with-fluids* fluids vals thunk)
  (if (not (= (length fluids) (length vals)))
      (scm-error 'out-of-range "with-fluids*" '("Value out of range: ~S")
                 vals vals))

  (let loop ((fluids-left fluids)
             (vals-left vals))
    (if (null? fluids-left)
        (thunk)
        (with-fluid* (car fluids-left) (car vals-left)
          (lambda ()
            (loop (cdr fluids-left) (cdr vals-left)))))))
--8<---------------cut here---------------end--------------->8---

I tested by running the code from my previous message with
`with-parameters*' redefined to use this new `with-fluids*'.

Best!
Wojtek

--
W. Kosior

website: https://koszko.org/koszko.html
fediverse: https://friendica.me/profile/koszko/profile
PGP fingerprint: E972 7060 E3C5 637C 8A4F  4B42 4BC5 221C 5A79 FD1A

On Fri, 14 Feb 2025 08:57:32 +0100
Linus Björnstam <[email-masked]> wrote:

> It seems with-fluids* (used by with-parameters*) is still a c function. I am not really sure how that stuff works, but I believe C can (always?) impose continuation barriers of delimited continuations. 
> 
>  If you look at the source of parameterize, with-fluids, with-fluids* (and more?) in boot-9 ther are all using scheme functions, or things defined in the VM, whereas srfi-39 depends on with-fluids* that is defined in fluids.c. 
> 
> If the transform from let to let* extends to with-fluids->with-fluids* it should be a trivial thing to put into boot-9.scm, but I rarely dabble with dynamic environments and whatever is in the documentation is somewhat opaque. 
> 
> Best regards
>   Linus Björnstam
> 
> On Tue, 11 Feb 2025, at 11:59, W. Kosior via Bug reports for GUILE, GNU's Ubiquitous Extension Language wrote:
> > Hello,
> >
> > I've been experimenting with guile-fibers.  I noticed that
> > `with-parameters*' procedure included as an "extra" in Guile's srfi-39
> > creates a continuation barrier.  From the documentation it is not clear
> > that it does so.
> >
> > Consider this sample program (which I ran with Guile 3.0.9 and Fibers
> > 1.0.0, as they are defined in Guix
> > 2fda889ac992977af7b4cd96d09f737d834e0b2d):
> >
> > --8<---------------cut here---------------start------------->8---
> > (use-modules (fibers)
> >              (fibers channels)
> >              (srfi srfi-18)
> >              ((srfi srfi-39) #:select (with-parameters*)))
> >
> > (define %my-channel
> >   (make-channel))
> >
> > (define %my-param
> >   (make-parameter #f))
> >
> > (define %my-thread
> >   (make-thread (lambda ()
> >                  (run-fibers (lambda ()
> >                                (with-parameters* (list %my-param) '(hello)
> >                                  (lambda ()
> >                                    (put-message %my-channel (%my-param))
> >                                    'ok)))))))
> >
> > (thread-start! %my-thread)
> >
> > (format #t "message: ~a~%" (get-message %my-channel))
> >
> > (thread-join! %my-thread)
> > --8<---------------cut here---------------end--------------->8---
> >
> > It prints:
> >  
> >> Uncaught exception in task:
> >> In fibers.scm:
> >>    186:20  6 (_)
> >>    145:21  5 (_)
> >> In unknown file:
> >>            4 (with-fluids* (#<fluid 7f192cf70d70>) (hello) #<procedure 100903e0 at <…>)
> >> In current input:
> >>     17:35  3 (_)
> >> In fibers/operations.scm:
> >>    154:10  2 (perform-operation _)
> >> In fibers/scheduler.scm:
> >>     357:6  1 (suspend-current-task _)
> >> In ice-9/boot-9.scm:
> >>   1685:16  0 (raise-exception _ #:continuable? _)
> >> ice-9/boot-9.scm:1685:16: In procedure raise-exception:
> >> Attempt to suspend fiber within continuation barrier  
> >
> > But if I use `parameterize' rather than `with-parameters*', no errors
> > occur.  Here's an example of a working program:
> >
> > --8<---------------cut here---------------start------------->8---
> > (use-modules (fibers)
> >              (fibers channels)
> >              (srfi srfi-18))
> >
> > (define %my-channel
> >   (make-channel))
> >
> > (define %my-param
> >   (make-parameter #f))
> >
> > (define %my-thread
> >   (make-thread (lambda ()
> >                  (run-fibers (lambda ()
> >                                (parameterize ((%my-param 'hello))
> >                                  (put-message %my-channel (%my-param))
> >                                  'ok))))))
> >
> > (thread-start! %my-thread)
> >
> > (format #t "message: ~a~%" (get-message %my-channel))
> >
> > (thread-join! %my-thread)
> > --8<---------------cut here---------------end--------------->8---
> >
> > It prints:
> >  
> >> message: hello  
> >
> > Is it a bug that `with-parameters*' doesn't work equivalently?  Or am I
> > reading the info pages wrong?
> >
> > Best :)
> > Wojtek
> >
> > --
> > W. Kosior
> >
> > website: https://koszko.org/koszko.html
> > fediverse: https://friendica.me/profile/koszko/profile
> > PGP fingerprint: E972 7060 E3C5 637C 8A4F  4B42 4BC5 221C 5A79 FD1A