#lang scribble/doc
@(require "mz.rkt" (for-label racket/async-channel))

@(define async-eval
   (lambda ()
     (let ([the-eval (make-base-eval)])
       (the-eval '(require racket/async-channel))
       the-eval)))

@title[#:tag "async-channel"]{Buffered Asynchronous Channels}

@note-lib-only[racket/async-channel]

@section{Creating and Using Asynchronous Channels}

@margin-note/ref{See also @secref["threadmbox"].}

An @deftech{asynchronous channel} is like a @tech{channel}, but it buffers
values so that a send operation does not wait on a receive operation.

In addition to its use with procedures that are specific to
asynchronous channels, an asynchronous channel can be used as a
@tech{synchronizable event} (see @secref["sync"]).  An asynchronous
channel is @tech{ready for synchronization} when
@racket[async-channel-get] would not block; the asynchronous channel's
@tech{synchronization result} is the same as the
@racket[async-channel-get] result.

@defproc[(async-channel? [v any/c]) boolean?]{

Returns @racket[#t] if @racket[v] is an asynchronous channel,
@racket[#f] otherwise.}


@defproc[(make-async-channel [limit (or/c exact-positive-integer? #f) #f]) 
         async-channel?]{

Returns an asynchronous channel with a buffer limit of @racket[limit]
items. A get operation blocks when the channel is empty, and a put
operation blocks when the channel has @racket[limit] items already.
If @racket[limit] is @racket[#f], the channel buffer has no limit (so
a put never blocks).}


@defproc[(async-channel-get [ach async-channel?]) any/c]{

Blocks until at least one value is available in @racket[ach], and then
returns the first of the values that were put into
@racket[async-channel].}


@defproc[(async-channel-try-get [ach async-channel?]) any/c]{

If at least one value is immediately available in @racket[ach],
returns the first of the values that were put into @racket[ach]. If
@racket[async-channel] is empty, the result is @racket[#f].}


@defproc[(async-channel-put [ach async-channel?] [v any/c]) void?]{

Puts @racket[v] into @racket[ach], blocking if @racket[ach]'s buffer
is full until space is available.}


@defproc[(async-channel-put-evt [ach async-channel?] [v any/c]) 
         evt?]{

Returns a @tech{synchronizable event} that is @tech{ready for
synchronization} when @racket[(async-channel-put ach v)] would return
a value (i.e., when the channel holds fewer values already than its
limit); @resultItself{asynchronous channel-put event}.}

@examples[#:eval (async-eval) #:once
(eval:no-prompt
 (define (server input-channel output-channel)
   (thread (lambda ()
             (define (get)
               (async-channel-get input-channel))
             (define (put x)
               (async-channel-put output-channel x))
             (define (do-large-computation)
               (sqrt 9))
             (let loop ([data (get)])
               (case data
                 [(quit) (void)]
                 [(add) (begin
                          (put (+ 1 (get)))
                          (loop (get)))]
                 [(long) (begin
                           (put (do-large-computation))
                           (loop (get)))])))))
 (define to-server (make-async-channel))
 (define from-server (make-async-channel)))

(server to-server from-server)

(async-channel? to-server)
(printf "Adding 1 to 4\n")
(async-channel-put to-server 'add)
(async-channel-put to-server 4)
(printf "Result is ~a\n" (async-channel-get from-server))
(printf "Ask server to do a long computation\n")
(async-channel-put to-server 'long)
(printf "I can do other stuff\n")
(printf "Ok, computation from server is ~a\n" 
        (async-channel-get from-server))
(async-channel-put to-server 'quit)
]

@section{Contracts and Impersonators on Asynchronous Channels}

@defproc[(async-channel/c [c contract?]) contract?]{

Returns a contract that recognizes asynchronous channels. Values put into or
retrieved from the channel must match @racket[c].

If the @racket[c] argument is a flat contract or a chaperone contract, then the
result will be a chaperone contract. Otherwise, the result will be an
impersonator contract.

When an @racket[async-channel/c] contract is applied to an asynchronous channel,
the result is not @racket[eq?] to the input. The result will be either a
@tech{chaperone} or @tech{impersonator} of the input depending on the type of
contract.}

@defproc[(impersonate-async-channel [channel async-channel?]
                                    [get-proc (any/c . -> . any/c)]
                                    [put-proc (any/c . -> . any/c)]
                                    [prop impersonator-property?]
                                    [prop-val any] ...
                                    ...)
         (and/c async-channel? impersonator?)]{

Returns an impersonator of @racket[channel], which redirects the
@racket[async-channel-get] and @racket[async-channel-put] operations.

The @racket[get-proc] must accept the value that @racket[async-channel-get]
produces on @racket[channel]; it must produce a replacement value, which is the
result of the get operation on the impersonator.

The @racket[put-proc] must accept the value passed to @racket[async-channel-put]
called on @racket[channel]; it must produce a replacement value, which is the
value passed to the put procedure called on the original channel.

The @racket[get-proc] and @racket[put-proc] procedures are called for all
operations that get or put values from the channel, not just
@racket[async-channel-get] and @racket[async-channel-put].

Pairs of @racket[prop] and @racket[prop-val] (the number of arguments to
@racket[impersonate-async-channel] must be odd) add
@tech{impersonator properties} or override @tech{impersonator property} values
of @racket[channel].}

@defproc[(chaperone-async-channel [channel async-channel?]
                                  [get-proc (any/c . -> . any/c)]
                                  [put-proc (any/c . -> . any/c)]
                                  [prop impersonator-property?]
                                  [prop-val any] ...
                                  ...)
         (and/c async-channel? chaperone?)]{

Like @racket[impersonate-async-channel], but the @racket[get-proc] procedure
must produce the same value or a chaperone of the original value, and
@racket[put-proc] must produce the same value or a chaperone of the original
value.}
