(sigil channels)
(sigil channels) - CSP-Style Channels
Communicating Sequential Processes (CSP) channels for concurrent programming. Channels provide safe communication between tasks with support for both synchronous and buffered operation.
Basic Usage
(import (sigil async)
(sigil channels))
(define ch (make-channel))
(with-async
(go (channel-send ch "hello"))
(display (channel-receive ch))) ; prints: helloBuffered Channels
Channels can have a buffer to decouple send and receive timing:
(define ch (make-channel 10)) ; buffer holds 10 values
(channel-try-send ch 'a) ; => #t (doesn't block)Iteration and Select
;; Iterate until channel closed (like Go's range)
(for-channel (msg ch)
(process msg))
;; Select on multiple channels
(channel-select
((recv ch1) => (lambda (val) (handle val)))
((send ch2 msg) => (lambda () (sent!)))
(else (nothing-ready)))Exports
make-channelprocedureCreate a channel for communication between concurrent tasks.
With no arguments, creates an unbuffered (synchronous) channel where sends block until a receiver is ready. With a numeric argument, creates a buffered channel that can hold that many values.
(make-channel) ; unbuffered - send blocks until receive
(make-channel 10) ; buffered - can hold 10 values before blockingchannel?procedureCheck if obj is a channel.
(channel? (make-channel)) ; => #t
(channel? 'foo) ; => #fchannel-closed?procedureCheck if a channel has been closed.
A closed channel can still be drained of buffered values, but no new values can be sent.
(define ch (make-channel 1))
(channel-closed? ch) ; => #f
(channel-close! ch)
(channel-closed? ch) ; => #tchannel-empty?procedureCheck if a buffered channel's buffer is empty.
For unbuffered channels, always returns #t.
(define ch (make-channel 3))
(channel-empty? ch) ; => #t
(channel-try-send ch 'x)
(channel-empty? ch) ; => #fchannel-full?procedureCheck if a buffered channel's buffer is full.
For unbuffered channels, always returns #t (no buffer space).
(define ch (make-channel 2))
(channel-full? ch) ; => #f
(channel-try-send ch 'a)
(channel-try-send ch 'b)
(channel-full? ch) ; => #tchannel-try-sendprocedureTry to send a value without blocking.
Returns #t if the value was sent successfully, #f if sending would block (no receiver waiting and buffer full) or if the channel is closed.
(define ch (make-channel 1))
(channel-try-send ch 'a) ; => #t (buffered)
(channel-try-send ch 'b) ; => #f (buffer full)channel-try-receiveprocedureTry to receive a value without blocking.
Returns (value) (a one-element list) if a value was received, #f if receiving would block (no value available), or 'closed if the channel is closed and empty.
(define ch (make-channel 1))
(channel-try-send ch 'hello)
(channel-try-receive ch) ; => (hello)
(channel-try-receive ch) ; => #f (empty)
(channel-close! ch)
(channel-try-receive ch) ; => closedchannel-sendprocedureSend a value to a channel, blocking if necessary.
Blocks until a receiver is ready (unbuffered) or buffer space is available (buffered). Must be called within a with-async context. Returns #t on success, #f if the channel is closed.
(with-async
(go (channel-send ch 'hello))
(channel-receive ch)) ; => hellochannel-receiveprocedureReceive a value from a channel, blocking if necessary.
Blocks until a value is available. Must be called within a with-async context. Returns the received value, or 'closed if the channel is closed and empty.
(with-async
(go (channel-send ch 'hello))
(channel-receive ch)) ; => hellochannel-close!procedureClose a channel.
After closing, no new values can be sent. Waiting receivers will receive 'closed, and waiting senders will get #f. Buffered values can still be received until the channel is empty.
(define ch (make-channel 2))
(channel-try-send ch 'a)
(channel-try-send ch 'b)
(channel-close! ch)
(channel-try-receive ch) ; => (a)
(channel-try-receive ch) ; => (b)
(channel-try-receive ch) ; => closedchannel-selectsyntaxSelect on multiple channel operations.
Tries each clause in order and executes the first one that can proceed without blocking. Supports recv for receiving, send for sending, and else as a default when nothing is ready.
(channel-select
((recv ch1) => (lambda (val) (process val)))
((send ch2 msg) => (lambda () (display "sent")))
(else (display "nothing ready")))for-channelsyntaxIterate over values from a channel until it's closed.
Similar to Go's for v := range ch. Receives values from the channel and binds each to the variable, executing the body for each value. The loop terminates when the channel is closed.
(with-async
(go
(channel-send ch 1)
(channel-send ch 2)
(channel-send ch 3)
(channel-close! ch))
(for-channel (n ch)
(display n)
(newline))) ; prints 1, 2, 3make-broadcastprocedureCreate a broadcast channel for fan-out messaging.
A broadcast channel distributes messages to multiple subscribers. Each subscriber receives its own copy of every message sent.
(define bc (make-broadcast))
(define sub1 (broadcast-subscribe bc))
(define sub2 (broadcast-subscribe bc))
(broadcast-send bc 'hello)
(channel-receive sub1) ; => hello
(channel-receive sub2) ; => hellobroadcast?procedureCheck if obj is a broadcast channel.
broadcast-subscribeprocedureSubscribe to a broadcast channel.
Creates and returns a new buffered channel that will receive all messages sent to the broadcast. The optional buffer-size argument controls how many messages can be buffered (default: 16).
(define bc (make-broadcast))
(define my-channel (broadcast-subscribe bc))
;; my-channel now receives all broadcast messagesbroadcast-unsubscribeprocedureUnsubscribe a channel from a broadcast.
Removes the channel from the subscriber list. After unsubscribing, the channel will no longer receive broadcast messages.
(broadcast-unsubscribe bc my-channel)broadcast-sendprocedureSend a message to all broadcast subscribers.
The message is copied to each subscriber's channel using non-blocking send. If a subscriber's buffer is full, that subscriber misses the message (no blocking or error).
(broadcast-send bc "hello everyone")broadcast-subscriber-countprocedureGet the number of current subscribers.
(broadcast-subscriber-count bc) ; => 3