[gambit-list] Does gambit support flow control on ports?

Marc Feeley feeley at iro.umontreal.ca
Tue Nov 10 10:53:51 EST 2020


> On Nov 8, 2020, at 6:49 AM, Jörg F. Wittenberger <Joerg.Wittenberger at softeyes.net> wrote:
> 
> Hi,
> 
> the following I wrote assuming the `no buffering` of pipe ports would
> block the writer until there is a reader thread ready to receive the
> data.
> 
> Apparently that's not the case.
> 
> Does Gambit support such a flow control?
> 
> (Or alternatively: is there a way to create my own ports, which would?)
> 
> Thanks soo much
> 
> /Jörg
> 
>    (define (make-pipe)
>      (open-u8vector-pipe '(buffering: #f) '(buffering: #f)))
> 
>    (receive (in out) (make-pipe)
>      (display "foo" out) ;; I'd expect this to block,
>      (force-output out) ;; but even this does not.
>      "too bad")

The “buffering:” setting on byte ports indicates how promptly the data is transferred to the “next consumer” in the I/O pipeline.  When using buffering: #f each byte is immediately transferred to the next stage of the pipeline.  For example this code

   (define p (open-output-file (list path: "/dev/stdout" buffering: #f)))

   (let loop ((i 0))
     (if (< i 5000)
         (begin
           (write-u8 42 p)
           (thread-sleep! 0.001)
           (loop (+ i 1)))))

will display a “*” (code 42) every millisecond.  But if the buffering setting is changed to #t the data will be output in chunks of 1024 bytes, which is the size of the byte buffer.  Note that in this example the “next consumer” in the I/O pipeline is the operating system (that moves the data to the process’ stdout).

When using open-u8vector-pipe two ports are created, an output port and an input port.  The “next consumer” in the I/O pipeline of the output port is an in-memory FIFO that accumulates the data received.  The input port consumes data from this in-memory FIFO to fill its buffers.

So in your example, using a buffering: #f has no effect on the buffering being done by the in-memory FIFO.

If you want to limit the buffering done by the in-memory FIFO, for example if you want to avoid a form of memory leak when the consumer is slower than the producer, then you can set a buffering limit with “macro-u8vector-port-buffering-limit-set!” like this:

   (include "~~lib/_gambit#.scm")

   (define (make-pipe)
     (open-u8vector-pipe '(buffering: #f) '(buffering: #f)))

   (receive (in out) (make-pipe)

     ;; set buffering limit... actual limit = (* 64 (+ 2 (quotient lim 64)))
     (let ((lim 1000))
       (macro-u8vector-port-buffering-limit-set! out lim))

     (let ((i 0))
       (with-exception-catcher
        (lambda (e)
          (if (deadlock-exception? e)
              (pp `(deadlock after writing ,i bytes))
              (raise e)))
        (lambda ()
          (let loop ()
            (if (< i 1000000)
                (begin
                  (write-u8 42 out)
                  (set! i (+ i 1))
                  (loop))
                (pp `(wrote ,i bytes without deadlock))))))))

Note that the actual limit that you get is really (* 64 (+ 2 (quotient limit 64))) because the in-memory FIFO is represented by a list of 64 byte chunks.

Marc





More information about the Gambit-list mailing list