Files
port-channel/scribblings/port-channel.scrbl
T

146 lines
5.4 KiB
Racket

#lang scribble/manual
@(require (for-label racket/base
racket/contract
racket/serialize
"../main.rkt"))
@title{port-channel}
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
@defmodule[port-channel]
The @racketmodname[port-channel] module wraps a Racket port as an asynchronous,
channel-like object. It is intended as a small building block for stream-based
communication protocols, for example between a parent Racket process and a
subprocess connected through standard input and standard output.
A port-channel transports serialized Racket values. The implementation uses
@racketmodname[racket/serialize]: values are passed through @racket[serialize],
written to the port, read back as Racket data and reconstructed with
@racket[deserialize]. Ordinary data values work directly. Custom structures must
be defined with @racket[serializable-struct]. Ports, threads, custodians and
ordinary procedures are not serializable values.
@section{Model}
A port-channel has one of two directions:
@itemlist[
@item{@racket['input]: a reader thread reads complete serialized values from an
input port and puts the deserialized values on an internal asynchronous
channel. The port-channel is itself a synchronizable event, so
@racket[(sync pc)] returns the next complete value.}
@item{@racket['output]: @racket[port-channel-put] queues values on an internal
asynchronous channel. A writer thread serializes and writes queued values
to the output port.}]
Buffered I/O alone is not enough to provide this behaviour. Synchronizing on a
raw input port only says that some input is available; it does not guarantee that
a complete serialized value is ready. The reader thread converts the byte stream
into complete channel messages.
@section{Reference}
@defproc[(make-port-channel [port port?]
[#:direction direction (or/c 'auto 'input 'output) 'auto]
[#:source source any/c 'port]
[#:close? close? any/c #t])
port-channel?]{
Creates a port-channel for @racket[port]. With @racket['auto], the direction is
inferred from the port. An input-only port becomes an input port-channel. An
output-only port becomes an output port-channel. If the port is both an input and
an output port, @racket[#:direction] must be supplied explicitly.
The @racket[source] value is stored in reported @racket[port-channel-error]
values. When @racket[close?] is true, @racket[close-port-channel] closes the
underlying port.}
@defproc[(port-channel? [v any/c]) boolean?]{
Returns true when @racket[v] is a port-channel.}
@defproc[(port-channel-direction [pc port-channel?]) (or/c 'input 'output)]{
Returns the direction of @racket[pc].}
@defproc[(port-channel-put [pc port-channel?] [v any/c]) void?]{
Queues @racket[v] for writing to an output port-channel. The call only enqueues
the value; serialization and port I/O are performed by the writer thread.
The value must be serializable by @racket[serialize]. If serialization or writing
fails, a @racket[port-channel-error] is published on the error channel.}
@defproc[(port-channel-get [pc port-channel?]) any/c]{
Returns the next value from an input port-channel. This is equivalent to
@racket[(sync pc)]. When the underlying input stream reaches end-of-file,
@racket[eof] is published as the final value.}
@defproc[(port-channel-try-get [pc port-channel?]) any/c]{
Attempts to get a value from an input port-channel without blocking. Returns
@racket[#f] when no value is currently available.}
@defproc[(port-channel-evt [pc port-channel?]) evt?]{
Returns @racket[pc] as a synchronizable event. This is mainly a convenience for
code that wants an explicit event-producing function.}
@defproc[(close-port-channel [pc port-channel?]) void?]{
Closes the port-channel. For an input port-channel, this closes the input port.
For an output port-channel, a close marker is queued, so values already queued
before the close marker are written first.}
@defproc[(port-channel-wait [pc port-channel?]) void?]{
Waits until the reader or writer thread of @racket[pc] has terminated.}
@section{Errors}
@defstruct*[port-channel-error ([source any/c]
[message string?])]{
Represents an error reported by the reader or writer thread.}
@defproc[(port-channel-error-get [pc port-channel?]) port-channel-error?]{
Blocks until the next error is available.}
@defproc[(port-channel-error-try-get [pc port-channel?]) any/c]{
Attempts to get the next error without blocking. Returns @racket[#f] when no
error is currently available.}
@defproc[(port-channel-error-evt [pc port-channel?]) evt?]{
Returns the error channel as a synchronizable event.}
@section{Example}
@racketblock[
(require port-channel)
(define-values (in out) (make-pipe))
(define reader (make-port-channel in))
(define writer (make-port-channel out))
(port-channel-put writer '(hello 1 2 3))
(sync reader)
]
The result is:
@racketblock[
'(hello 1 2 3)
]
@section{Serializable structures}
@racketblock[
(require racket/serialize
port-channel)
(serializable-struct message (id payload) #:transparent)
(define-values (in out) (make-pipe))
(define reader (make-port-channel in))
(define writer (make-port-channel out))
(port-channel-put writer (message 1 '(a b c)))
(sync reader)
]
The received value is a reconstructed @racket[message] structure.