10.5 Places
Parallelism with Places in The Racket Guide introduces places.
Places enable the development of parallel programs that take advantage of machines with multiple processors, cores, or hardware threads.
Currently, parallel support for places is enabled only for Racket 3m (which is the main variant of Racket), and only by default for Windows, Linux x86/x86_64, and Mac OS X x86/x86_64. To enable support for other platforms, use --enable-places with configure when building Racket. The place-enabled? function reports whether places run in parallel.
A place is a parallel task that is effectively a separate instance of the Racket virtual machine. Places communicate through place channels, which are endpoints for a two-way buffered communication.
To a first approximation, place channels support only immutable, transparent values as messages. In addition, place channels themselves can be sent across channels to establish new (possibly more direct) lines of communication in addition to any existing lines. Finally, mutable values produced by shared-flvector, make-shared-flvector, shared-fxvector, make-shared-fxvector, shared-bytes, and make-shared-bytes can be sent across place channels; mutation of such values is visible to all places that share the value, because they are allowed in a shared memory space. See place-message-allowed?.
A place channel can be used as a synchronizable event (see Events) to receive a value through the channel. A place channel is ready for synchronization when a message is available on the channel, and the place channel’s synchronization result is the message (which is removed on synchronization). A place can also receive messages with place-channel-get, and messages can be sent with place-channel-put.
Two place channels are equal? if they are endpoints for the same underlying channels while both or neither is a place descriptor. Place channels can be equal? without being eq? after being sent messages through a place channel.
Constraints on messages across a place channel—
For example, the following expression launches two places, echoes a message to each, and then waits for the places to terminate:
(let ([pls (for/list ([i (in-range 2)]) (dynamic-place "place-worker.rkt" 'place-main))]) (for ([i (in-range 2)] [p pls]) (place-channel-put p i) (printf "~a\n" (place-channel-get p))) (map place-wait pls))
The "place-worker.rkt" module must export the place-main function that each place executes, where place-main must accept a single place channel argument:
#lang racket (provide place-main) (define (place-main pch) (place-channel-put pch (format "Hello from place ~a" (place-channel-get pch))))
Place channels are subject to garbage collection, like other Racket values, and a thread that is blocked reading from a place channel can be garbage collected if place channel’s writing end becomes unreachable. However, unlike normal channel blocking, if otherwise unreachable threads are mutually blocked on place channels that are reachable only from the same threads, the threads and place channels are all considered reachable, instead of unreachable.
procedure
procedure
(place-channel? v) → boolean?
v : any/c
procedure
(dynamic-place module-path start-name) → place?
module-path : (or/c module-path? path?) start-name : symbol?
The module indicated by module-path must export a function with the name start-proc. The function must accept a single argument, which is a place channel that corresponds to the other end of communication for the place descriptor returned by place.
When the place is created, the initial exit handler terminates the place, using the argument to the exit handler as the place’s completion value. Use (exit v) to immediately terminate a place with the completion value v. Since a completion value is limited to an exact integer between 0 and 255, any other value for v is converted to 0.
If the function indicated by module-path and start-proc returns, then the place terminates with the completion value 0.
In the created place, the current-input-port parameter is set to an empty input port, while the values of the current-output-port and current-error-port parameters are connected to the current ports in the creating place. If the output ports in the creating place are file-stream ports, then the connected ports in the created place share the underlying streams, otherwise a thread in the creating place pumps bytes from the created place’s ports to the current ports in the creating place.
The module-path argument must not be a module path of the form (quote sym) unless the module is predefined (see module-predefined?).
procedure
(dynamic-place* module-path start-name [ #:in in #:out out #:err err]) →
place? (or/c output-port? #f) (or/c input-port? #f) (or/c input-port? #f) module-path : (or/c module-path? path?) start-name : symbol? in : (or/c input-port? #f) = #f out : (or/c output-port? #f) = (current-output-port) err : (or/c output-port? #f) = (current-error-port)
The caller of dynamic-place* is responsible for closing all returned ports; none are closed automatically.
The dynamic-place* procedure returns four values:
a place descriptor value representing the created place;
an output port piped to the place’s standard input, or #f if in was a port;
an input port piped from the place’s standard output, or #f if out was a port;
an input port piped from the place’s standard error, or #f if err was a port or 'stdout.
syntax
(place id body ...+)
syntax
(place* maybe-port ... id body ...+)
maybe-port =
| #:in in-expr | #:out out-expr | #:err err-expr
procedure
(place-wait p) → exact-integer?
p : place?
If any pumping threads were created to connect a non-file-stream port to the ports in the place for p (see dynamic-place), place-wait returns only when the pumping threads have completed.
procedure
(place-dead-evt p) → evt?
p : place?
If any pumping threads were created to connect a non-file-stream port to the ports in the place for p (see dynamic-place), the event returned by place-dead-evt may become ready even if a pumping thread is still running.
procedure
(place-kill p) → void?
p : place?
procedure
(place-break p [kind]) → void?
p : place? kind : (or/c #f 'hang-up 'terminate) = #f
procedure
(place-channel) →
place-channel? place-channel?
Typically, one place channel is used by the current place to send messages to a destination place; the other place channel is sent to the destination place (via an existing place channel).
procedure
(place-channel-put pch v) → void
pch : place-channel? v : place-message-allowed?
See place-message-allowed? form information on automatic coercions in v, such as converting a mutable string to an immutable string.
procedure
pch : place-channel?
procedure
(place-channel-put/get pch v) → any/c
pch : place-channel? v : any/c
procedure
v : any/c
If (place-enabled?) returns #f, then the result is always #t and no conversions are performed on v as a message. Otherwise, the following kinds of data are allowed as messages:
numbers, characters, booleans, and #<void>;
symbols, where the eq?ness of uninterned symbols is preserved within a single message, but not across messages;
strings and byte strings, where mutable strings and byte strings are automatically replaced by immutable variants;
paths (for any platform);
pairs, lists, vectors, and immutable prefab structures containing message-allowed values, where a mutable vector is automatically replaced by an immutable vector;
hash tables where mutable hash tables are automatically replaced by immutable variants;
place channels, where a place descriptor is automatically replaced by a plain place channel;
file-stream ports and TCP ports, where the underlying representation (such as a file descriptor, socket, or handle) is duplicated and attached to a fresh port in the receiving place;
C pointers as created or accessed via ffi/unsafe; and
values produced by shared-flvector, make-shared-flvector, shared-fxvector, make-shared-fxvector, shared-bytes, and make-shared-bytes.