168 lines
7.7 KiB
Racket
168 lines
7.7 KiB
Racket
#lang scribble/manual
|
|
|
|
@(require (for-label racket/base
|
|
racket/contract/base
|
|
racket/async-channel
|
|
racket/place
|
|
racket/serialize
|
|
port-channel
|
|
uni-channel))
|
|
|
|
@title{uni-channel}
|
|
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
|
|
|
|
@defmodule[uni-channel]
|
|
|
|
The @racketmodname[uni-channel] module provides a small uniform facade over
|
|
three channel-like transports: @racketmodname[racket/async-channel]
|
|
asynchronous channels, @racketmodname[racket/place] place channels, and
|
|
@racketmodname[port-channel] port channels.
|
|
|
|
The purpose is not to hide transport semantics completely. A place channel still
|
|
uses place-channel message constraints and a port channel still serializes values
|
|
with @racketmodname[racket/serialize]. The purpose is to make code that only
|
|
needs send, receive, event synchronization and close operations independent from
|
|
the concrete channel implementation.
|
|
|
|
@section{Model}
|
|
|
|
A uni-channel has a kind, a direction, a name and an underlying backend
|
|
object. The kind is one of @racket['async], @racket['place] or @racket['port].
|
|
The direction is one of @racket['input], @racket['output] or
|
|
@racket['bidirectional]. Receiving is supported for input and bidirectional
|
|
channels. Sending is supported for output and bidirectional channels.
|
|
|
|
A uni-channel that supports receiving is itself a synchronizable event:
|
|
|
|
@racketblock[(sync ch)]
|
|
|
|
For output-only channels, synchronizing on the uni-channel uses
|
|
@racket[never-evt].
|
|
|
|
@section{Constructors}
|
|
|
|
@defproc[(make-uni-channel [backend any/c]
|
|
[#:kind kind (or/c 'auto 'async 'place 'port) 'auto]
|
|
[#:direction direction (or/c 'auto 'input 'output 'bidirectional) 'auto]
|
|
[#:name name any/c #f])
|
|
uni-channel?]{
|
|
Wraps an existing backend channel. With @racket['auto], the kind is inferred from
|
|
@racket[async-channel?], @racket[place-channel?] or @racket[port-channel?].
|
|
|
|
For a @racket[port-channel?] backend, the direction is read from
|
|
@racket[port-channel-direction]. A single port-channel endpoint is therefore
|
|
input-only or output-only. Use @racket[make-uni-port-channel] when one uni
|
|
channel should combine an input port and an output port into a bidirectional
|
|
endpoint.}
|
|
|
|
@defproc[(make-uni-async-channel [ch async-channel? (make-async-channel)]
|
|
[#:direction direction (or/c 'auto 'input 'output 'bidirectional) 'bidirectional]
|
|
[#:name name any/c 'async-channel])
|
|
uni-channel?]{
|
|
Wraps a single asynchronous channel. The same underlying queue is used for both
|
|
sending and receiving when the direction is @racket['bidirectional].}
|
|
|
|
@defproc[(make-uni-async-channel-pair [#:name-a name-a any/c 'async-channel-a]
|
|
[#:name-b name-b any/c 'async-channel-b])
|
|
(values uni-channel? uni-channel?)]{
|
|
Creates two bidirectional uni-channel endpoints backed by two asynchronous channels.
|
|
A value sent on the first endpoint is received on the second endpoint, and the
|
|
other way around.}
|
|
|
|
@defproc[(make-uni-place-channel [ch place-channel?]
|
|
[#:direction direction (or/c 'auto 'input 'output 'bidirectional) 'bidirectional]
|
|
[#:name name any/c 'place-channel])
|
|
uni-channel?]{
|
|
Wraps an existing place-channel endpoint. Values sent through this backend must
|
|
satisfy Racket's place-message constraints.}
|
|
|
|
@defproc[(make-uni-place-channel-pair [#:name-a name-a any/c 'place-channel-a]
|
|
[#:name-b name-b any/c 'place-channel-b])
|
|
(values uni-channel? uni-channel?)]{
|
|
Creates two bidirectional uni-channel endpoints using @racket[place-channel].}
|
|
|
|
@defproc[(make-uni-port-channel [#:input in (or/c #f input-port?) #f]
|
|
[#:output out (or/c #f output-port?) #f]
|
|
[#:source source any/c 'port-channel]
|
|
[#:close? close? any/c #t]
|
|
[#:name name any/c 'port-channel])
|
|
uni-channel?]{
|
|
Creates a uni-channel backed by one or two @racketmodname[port-channel]
|
|
endpoints. At least one of @racket[#:input] or @racket[#:output] must be
|
|
provided. With both, the result is bidirectional: receives come from the input
|
|
port-channel and sends go to the output port-channel.}
|
|
|
|
@section{Predicates and accessors}
|
|
|
|
@defproc[(uni-channel? [v any/c]) boolean?]{Returns true when @racket[v] is a uni-channel.}
|
|
@defproc[(uni-channel-kind [ch uni-channel?]) (or/c 'async 'place 'port)]{Returns the backend kind.}
|
|
@defproc[(uni-channel-direction [ch uni-channel?]) (or/c 'input 'output 'bidirectional)]{Returns the channel direction.}
|
|
@defproc[(uni-channel-name [ch uni-channel?]) any/c]{Returns the diagnostic name supplied when the uni-channel was created.}
|
|
@defproc[(uni-channel-impl [ch uni-channel?]) any/c]{Returns the underlying backend object.}
|
|
|
|
@section{Sending and receiving}
|
|
|
|
@defproc[(uni-channel-put [ch uni-channel?] [v any/c]) void?]{
|
|
Sends @racket[v] on @racket[ch]. The channel must be output-capable. For
|
|
@racket['port] channels, the value must be serializable by @racket[serialize].
|
|
For @racket['place] channels, the value must be allowed as a place message.}
|
|
|
|
@defproc[(uni-channel-send [ch uni-channel?] [v any/c]) void?]{Alias for @racket[uni-channel-put].}
|
|
@defproc[(uni-channel-get [ch uni-channel?]) any/c]{Receives the next value from @racket[ch].}
|
|
@defproc[(uni-channel-recv [ch uni-channel?]) any/c]{Alias for @racket[uni-channel-get].}
|
|
@defproc[(uni-channel-try-get [ch uni-channel?]) any/c]{Attempts to receive a value without blocking. Returns @racket[#f] when no value is currently available.}
|
|
@defproc[(uni-channel-get-evt [ch uni-channel?]) evt?]{Returns a synchronizable receive event.}
|
|
@defproc[(uni-channel-put-evt [ch uni-channel?] [v any/c]) evt?]{Returns an event that sends @racket[v] when selected.}
|
|
|
|
@section{Closing}
|
|
|
|
@defproc[(uni-channel-close [ch uni-channel?]) void?]{
|
|
Closes @racket[ch]. For async-channel and place-channel backends this marks the
|
|
uni-channel wrapper as closed; Racket does not provide a native close operation for
|
|
those channel types. For port-channel backends this delegates to
|
|
@racket[close-port-channel]. For a bidirectional port channel only the output
|
|
side is closed, so already queued values can still be received from the input
|
|
side until end-of-file.}
|
|
|
|
@defproc[(uni-channel-closed? [ch uni-channel?]) boolean?]{Returns true when @racket[uni-channel-close] has been called on the wrapper.}
|
|
|
|
@section{Port-channel errors}
|
|
|
|
@defproc[(uni-channel-error-get [ch uni-channel?]) any/c]{Blocks until a backend error is available. This is meaningful for @racket['port] channels and raises an exception for backends that do not have an error channel.}
|
|
@defproc[(uni-channel-error-try-get [ch uni-channel?]) any/c]{Attempts to get a backend error without blocking. Returns @racket[#f] when no error is available.}
|
|
@defproc[(uni-channel-error-evt [ch uni-channel?]) evt?]{Returns a synchronizable backend-error event. For non-port backends this is @racket[never-evt].}
|
|
|
|
@section{Examples}
|
|
|
|
@subsection{Async-channel pair}
|
|
|
|
@racketblock[
|
|
(require uni-channel)
|
|
|
|
(define-values (a b) (make-uni-async-channel-pair))
|
|
(uni-channel-send a '(hello from a))
|
|
(uni-channel-recv b)
|
|
]
|
|
|
|
@subsection{Place-channel pair}
|
|
|
|
@racketblock[
|
|
(require uni-channel)
|
|
|
|
(define-values (a b) (make-uni-place-channel-pair))
|
|
(uni-channel-send a '(hello from a))
|
|
(sync b)
|
|
]
|
|
|
|
@subsection{Port-channel over a pipe}
|
|
|
|
@racketblock[
|
|
(require uni-channel)
|
|
|
|
(define-values (in out) (make-pipe))
|
|
(define ch (make-uni-port-channel #:input in #:output out))
|
|
|
|
(uni-channel-send ch '(hello 1 2 3))
|
|
(uni-channel-recv ch)
|
|
]
|