Previous: Port handlers, Up: Programmatic ports


4.5.2.3 Buffered ports & handlers

Along with bare port handlers, Scheme48 provides conveniences for many patterns of buffered ports & port handlers. These names are exported by the i/o-internal structure. Buffered ports are integrated with Scheme48's optimistic concurrency facilities.

Note: Although internally buffered ports are integrated with optimistic concurrency, operations on buffered ports, like operations on channels, cannot be reliably fusibly atomic.

— procedure: make-buffered-input-port handler data buffer index limit –> input-port
— procedure: make-buffered-output-port handler data buffer index limit –> output-port

Constructors for buffered ports. Handler is the port's handler, which is usually constructed with one of the buffered port handler constructors (see below). Data is arbitrary data to go in the port's data field. Buffer is a byte vector whose length is greater than or equal to both index & limit. Index is the initial index into buffer to go in the port's index field. Limit is the limit in the port's buffer, to go into the port's limit field; nothing will be written into buffer at or past limit.

— procedure: make-unbuffered-input-port handler data –> input-port
— procedure: make-unbuffered-output-port handler data –> output-port

Conveniences for ports that are explicitly not buffered. Only the relevant fields are passed; all fields pertaining to buffering are initialized with #f.

— procedure: make-buffered-input-port-handler discloser closer buffer-filler readiness-tester –> port-handler

This creates a port handler for buffered input ports. The arguments are as follows:

discloser port-data –> disclosed
closer port-data –> ignored
Discloser & closer are like the similarly named regular port handler fields, but they are applied directly to the port's data, not to the port itself.
buffer-filler port wait? –> committed?
Used to fill port's buffer when it no longer has contents from which to read in its current buffer. Wait? is a boolean flag, #t if the operation should wait until the I/O transaction necessary to fill the buffer completes, or #f if it may simply initiate an I/O transaction but not wait until it completes (e.g., use channel-maybe-commit-and-read, but not wait on the condition variable passed to channel-maybe-commit-and-read). Buffer-filler is called with a fresh proposal in place, and it is the responsibility of buffer-filler to commit it. It returns a boolean flag denoting whether the proposal was committed. The last call in buffer-filler is usually either (maybe-commit) or a call to a procedure that causes that effect (e.g., one of the operation on condition variables that commits the current proposal. See condition variables.)
readiness-tester port –> [committed? ready?]
Called when char-ready? is applied to port and the buffer of port is empty. Like buffer-filler, readiness-tester is applied with a fresh proposal in place, which it should attempt to commit. Readiness-tester should return two values, each a boolean flag: the first denotes whether or not the current proposal was successfully committed, and, if it was successful, whether or not a character is ready.

— procedure: make-buffered-output-port-handler discloser buffer-emptier readiness-tester –> port-handler

This creates a port handler for buffered output ports. Discloser & closer are as with buffered input ports. The remaining fields are as follows:

buffer-emptier port necessary? –> committed?
Buffer-emptier is used when port's buffer is full and needs to be emptied. It is called with a fresh proposal in place. It should reset port's index field, call note-buffer-reuse! to invalidate other threads' transactions on the recycled buffer, and attempt to commit the new proposal installed. It returns a boolean flag indicating whether or not the commit succeeded.
readiness-tester port –> [committed? ready?]
Readiness-tester is applied to port when its buffer is full (i.e. its index & limit fields are equal) and output-port-ready? is applied to port. After performing the test, it should attempt to commit the current proposal and then return two values: whether it succeeded in committing the current proposal, and, if it was successful, whether or not a character is ready to be outputted.

— constant: default-buffer-size –> integer

The default size for port buffers. This happens to be 4096 in the current version of Scheme48.

— procedure: note-buffer-reuse! port –> unspecified
— procedure: check-buffer-timestamp! port –> unspecified

These are used to signal the resetting of a buffer between multiple threads. Note-buffer-reuse! is called — in the case of an output port — when a buffer fills up, is emptied, and flushed; or — in the case of an input port — when a buffer is emptied and needs to be refilled. Note-buffer-reuse! logs in the current proposal a fresh value to store in port. When that proposal is committed, this fresh value is stored in the port. Other threads that were using port's buffer call check-buffer-timestamp!, which logs a read in the current proposal. If another thread commits a buffer reuse to memory, that read will be invalidated, invalidating the whole transaction.