Making asynchronous operations synchronous

I would like to render a display based on objects on other vats, but I’m having trouble causing a sequence of prints. Suppose I generate some playing cards in some vat hosting a card game, and provide some views into the card which let you look at the card, but not peek at it (some cards are hidden and others revealed):

(use-modules (goblins)
             (goblins actor-lib facet)
             (goblins actor-lib joiners)
             (goblins actor-lib methods))

(define* (^card bcom suit number #:optional (hidden #f))
  (methods
   ((peek-suit) suit)
   ((look-suit) (if hidden '* suit))
   ((peek-number) number)
   ((look-number) (if hidden -1 number))))

(define card-views (with-vat (spawn-vat)
                             (map (lambda (card)
                                    (spawn ^facet card
                                           'look-suit 'look-number))
                                  (list (spawn ^card '♠ 3 #f)
                                        (spawn ^card '♥ 3 #t)
                                        (spawn ^card '♣ 3 #f)))))

Naively, if you wanted to print the suits of the cards, you would do something like this:

(with-vat (spawn-vat)
          (display "first line\n")
          (on (all-of* (map (lambda (card) (<- card 'look-suit))
                            card-views))
              (lambda (suits)
                (display (string-join (map symbol->string
                                           suits)))
                (newline)))
          (display "third line\n"))

But this does’t work, simply printing “first line\nthird line\n”. I would like to halt execution until the cards print with (make-condition) like so:

(use-modules (fibers conditions)
             (fibers operations))

(with-vat (spawn-vat)
          (display "first line\n")
          (let ((printed-second-line? (make-condition)))
            (on (all-of* (map (lambda (card) (<- card 'look-suit))
                              card-views))
                (lambda (suits)
                  (pk suits)
                  (display (string-join (map symbol->string
                                             suits)))
                  (pk "displayed cards")
                  (newline)
                  (signal-condition! printed-second-line?)))
            (perform-operation (wait-operation printed-second-line?)))
          (display "third line\n"))

but execution hangs on (perform-operation ...). I got signal-condition!, perform-operation, and wait-operation from Example Greeters over CapTP via Tor (Spritely Goblins). However, I have not seen those used elsewhere.

Is there a different way I should be doing this?

The issue with your last code example is that you have suspended the vat event loop fiber. Because the vat is suspended, it can no longer process messages, and thus your program hangs. Best practice here is to avoid the fibers API in the context of a vat (with-vat, call-with-vat, from an actor’s behavior, etc.) unless you really know what you are doing. Vats use a cooperative multitasking model, so an uncooperative process can grind everything to a halt.

The simplest solution would be to wait until you’ve resolved all of the promises before printing anything:

(on (all-of* (map (lambda (card) (<- card 'look-suit))
                  card-views))
    (lambda (suits)
      (display "first line\n")
      (display (string-join (map symbol->string
                                 suits)))
      (newline)
      (display "third line\n")))

It’s also possible to use conditions but you have to avoid waiting on the condition within the vat:

(display "first line\n")
(let ((printed-second-line? (make-condition)))
  (with-vat (spawn-vat)
    (on (all-of* (map (lambda (card) (<- card 'look-suit))
                      card-views))
        (lambda (suits)
          (pk suits)
          (display (string-join (map symbol->string
                                     suits)))
          (pk "displayed cards")
          (newline)
          (signal-condition! printed-second-line?))))
  (wait printed-second-line?))
(display "third line\n")

Note that the wait call is outside the with-vat form. Both of the above code blocks are untested but should be close to correct.

Hope this helps!