Re: On the implications of adding file-open to SRFI-170 Marc Feeley 29 Nov 2019 02:39 UTC

> On Nov 28, 2019, at 10:20 AM, Lassi Kortela <xxxxxx@lassi.io> wrote:
>
>> In Gambit, the implementation of “write” (and related procedures) causes characters to be written to the port, and the locking is done at the character level.  So indeed separate calls to write in 2 threads may end up interleaving the characters.  This is not a bug… for example you may not want individual calls to write to appear atomic if you are debugging the write procedure itself (and the debugging output goes to the same stdout as is being written to by “write”), which may be the case if the programmer has a way to extend the write procedure for specific types and the code in these extensions is buggy.  This exists in many Schemes, including Gambit.
>
> Thanks for chiming in. Interesting approach. What's the usual idiom to write to the same port from more than one thread?
>

The only reasonable case I can think of where multiple threads are writing to a single character port is for displaying an activity log.  But in that case you probably want a group of output operations to be in a critical section rather than a single call to write.  So an “automatic” locking of the port during the call to “write” doesn’t really help.  This can be achieved effectively and simply with mutexes:

(define m (make-mutex))

(define (log msg)
  (mutex-lock! m)
  (display (time->seconds (current-time)))
  (display ": ")
  (display msg)
  (newline)
  (mutex-unlock! m))

(define (generate-output1) (log "hello"))
(define (generate-output2) (log "world"))

Another case would be implementing a mailbox, where multiple threads are sending messages to a “server” thread, but in that case character ports are a poor choice.  For this Gambit has “object ports” that are streams of Scheme objects, similarly to standard “character ports” that are streams of characters and byte ports that are streams of bytes.  So on object ports, writing an object is an atomic operation.  This is the thing to use in the case of a multiple producer single consumer scenarios (where the objects represent “messages” and the ordering of messages from separate threads doesn’t matter).

> (define p (open-vector))
> (write 1 p)
> (write 2 p)
> (write 3 p)
> (read p)
1
> (read p)
2

Marc