#
Chapter 46
`thread.ss`: Thread Utilities

To load: `(require (lib "thread.ss"))`

`(coroutine``` `proc``)` PROCEDURE

Returns a coroutine object to encapsulate a thread that runs only when allowed. The `proc` procedure should accept one argument, and `proc` is run in the coroutine thread when `coroutine-run` is called. If `coroutine-run` returns due to a timeout, then the coroutine thread is suspended until a future call to `coroutine-run`. Thus, `proc` only executes during the dynamic extent of a `coroutine-run` call.

The argument to `proc` is a procedure that takes a boolean, and it can be used to disable suspends (in case `proc` has critical regions where it should not be suspended). A true value passed to the procedure enables suspends, and `#f` disables suspends. Initially, suspends are allowed.

`(coroutine?``` `v``)` PROCEDURE

Returns `#t` if `v` is a coroutine produced by `coroutine`, `#f` otherwise.

`(coroutine-run``` `timeout-secs coroutine``)` PROCEDURE

Allows the thread associated with `coroutine` to execute for up to `timeout-secs`. If `coroutine`'s procedure disables suspends, then the coroutine can run arbitrarily long until it re-enables suspends.

The `coroutine-run` procedure returns `#t` if `coroutine`'s procedure completes (or if it completed earlier), and the result is available via `coroutine-result`. The `coroutine-run` procedure returns returns `#f` if `coroutine`'s procedure does not complete before it is suspended after `timeout-secs`. If `coroutine`'s procedure raises an exception, then it is re-raised by `coroutine-run`.

`(coroutine-result``` `coroutine``)` PROCEDURE

Returns the result for `coroutine` if it has completed with a value (as opposed to an exception), `#f` otherwise.

`(coroutine-kill``` `coroutine``)` PROCEDURE

Forcibly terminates the thread associated with `coroutine` if it is still running, leaving the coroutine result unchanged.

`(consumer-thread``` `f``` [`init`]`)` PROCEDURE

Returns two values: a thread descriptor for a new thread, and a procedure with the same arity as `f`.10 When the returned procedure is applied, its arguments are queued to be passed on to `f`, and void is immediately returned. The thread created by `consumer-thread` dequeues arguments and applies `f` to them, removing a new set of arguments from the queue only when the previous application of `f` has completed; if `f` escapes from a normal return (via an exception or a continuation), the `f`-applying thread terminates.

The `init` argument is a procedure of no arguments; if it is provided, `init` is called in the new thread immediately after the thread is created.

`(run-server``` `port-k conn-proc conn-timeout``` [`handler-proc listen-proc close-proc accept-proc accept/break-proc`]`)` PROCEDURE

Executes a TCP server on the port indicated by `port-k`. When a connection is made by a client, `conn-proc` is called with two values: an input port to receive from the client, and an output port to send to the client.

Each client connection is managed by a new custodian, and each call to `conn-proc` occurs in a new thread (managed by the connection's custodian). If the thread executing `conn-proc` terminates for any reason (e.g., `conn-proc` returns), the connection's custodian is shut down. Consequently, `conn-proc` need not close the ports provided to it. Breaks are enabled in the connection thread if breaks are enabled when `run-server` is called.

To facilitate capturing a continuation in one connection thread and invoking it in another, the parameterization of the `run-server` call is used for every call to `handler-proc`. In this parameterization and for the connection's thread, the `current-custodian` parameter is assigned to the connection's custodian.

If `conn-timeout` is not `#f`, then it must be a non-negative number specifying the time in seconds that a connection thread is allowed to run before it is sent a break signal. Then, if the thread runs longer than `(* conn-timeout 2)` seconds, then the connection's custodian is shut down. If `conn-timeout` is `#f`, a connection thread can run indefinitely.

If `handler-proc` is provided, it is passed exceptions related to connections (i.e., exceptions not caught by `conn-proc`, or exceptions that occur when trying to accept a connection). The default handler ignores the exception and returns void.

The `listen-proc`, `close-proc`, `accept-proc` and `accept/break-proc` arguments default to the `tcp-listen`, `tcp-close`, `tcp-accept`, and `tcp-accept/enable-break` procedures, respectively. The `run-server` function calls these procedures without optional arguments. Provide alternate procedures to use an alternate communication protocol (such as SSL) or to supply optional arguments in the use of `tcp-listen`.

The `run-server` procedure loops to serve client connections, so it never returns. If a break occurs, the loop will cleanly shut down the server, but it will not terminate active connections.

10 The returned procedure actually accepts any number of arguments, but immediately raises `exn:fail:contract:arity` if `f` cannot accept the provided number of arguments.