Under heavy development. This site describes the intended 1.0 language. Current features and performance vary. Development status
Sn50SnThe SnAPI programming language
Development · Evolving reference

Channel

Owns a bounded FIFO with suspension-backed send/receive backpressure. Creation pre-reserves all slots. Successful send transfers one owned payload; failed send returns it in ChannelSendError. Immediate receives distinguish temporary Empty from final Closed; asynchronous receives return optional EOF. Close stops new send commits and drains committed items before EOF. Cancellation can remove pending permit waits, never duplicate a committed payload. Published readonly borrows may operate concurrently; moving/disposal requires ordinary synchronization and live-loan rules. No fairness or waiter-order guarantee. Queue operations/Close briefly contend on an internal mutex; guards never cross await. Creation allocates; ordinary successful transfers do not grow backing. Destruction closes before releasing state; pending work retains private leases. Element move/destruction costs and traps remain those of T.

Sn is under heavy development. This reference includes the intended 1.0 design; features may be incomplete in the current compiler. Read the status →

Module std.sync

import std.sync;

Public declaration contracts and authored examples. A page can document several related symbols. Implementation bodies are excluded.

Channel

pub final class Channel<T> where T: Movable

Owns a bounded FIFO with suspension-backed send/receive backpressure. Creation pre-reserves all slots. Successful send transfers one owned payload; failed send returns it in ChannelSendError. Immediate receives distinguish temporary Empty from final Closed; asynchronous receives return optional EOF. Close stops new send commits and drains committed items before EOF. Cancellation can remove pending permit waits, never duplicate a committed payload. Published readonly borrows may operate concurrently; moving/disposal requires ordinary synchronization and live-loan rules. No fairness or waiter-order guarantee. Queue operations/Close briefly contend on an internal mutex; guards never cross await. Creation allocates; ordinary successful transfers do not grow backing. Destruction closes before releasing state; pending work retains private leases. Element move/destruction costs and traps remain those of T. T: Movable payload; stored views retain ordinary origin/transfer rules.

Example

module example.channel;
from std.sync import Channel;
from std.task import Task;
async fn Main() -> Task<i32> {
    var Queue = {
        // The scoped Result keeps its exact writable payload until transfer.
        var QueueCreation = Channel<i32>.TryCreate(1);
        switch QueueCreation {
            case .Ok { Value = Value: mut Channel<i32>& } => break move(Value);
            case .Err => return 1;
        }
    };
    if Queue.Capacity != 1 { return 2; }
    if Queue.TrySend(40) is .Err { Error = _ } { return 3; }
    switch Queue.TrySend(2) {
        case .Ok => return 4;
        case .Err { Error = Error } => {
            if !(Error.Reason is .Full) || Error.Value != 2 { return 5; }
        }
    }
    switch Queue.TryReceive() {
        case .Ok { Value = PatternPayload2 } => {
            switch PatternPayload2 {
                case .Item { Value = Value } => { if Value != 40 { return 6; } }
                default => return 7;
            }
        }
        default => return 7;
    }
    switch Queue.TryReceive() {
        case .Ok { Value = PatternPayload3 } if (PatternPayload3 is .Empty) => {}
        default => return 8;
    }
    if await Queue.SendAsync(42) is .Err { Error = _ } { return 9; }
    if Queue.Close() is .Err { Error = _ } { return 10; }
    switch await Queue.ReceiveAsync() {
        case .Ok { Value = PatternPayload4 } => {
            switch PatternPayload4 {
                case .Some { Value = Value } => { if Value != 42 { return 11; } }
                default => return 12;
            }
        }
        default => return 12;
    }
    switch await Queue.ReceiveAsync() {
        case .Ok { Value = PatternPayload5 } if (PatternPayload5 is .None) => {}
        default => return 13;
    }
    switch Queue.TrySend(99) {
        case .Err { Error = Error } => { if !(Error.Reason is .Closed) || Error.Value != 99 { return 14; } }
        case .Ok => return 15;
    }
    var Moved = move(Queue);
    if !Queue.IsEmpty || Moved.IsEmpty { return 16; }
    if Moved.Close() is .Err { Error = _ } { return 17; }
    return 0;
}

Channel.init

pub init()

Creates inert owner without allocating.

Channel.init

pub init(Source: readonly Self&) = delete;

Rejects duplicate visible closing authority. Source: Existing owner.

Channel.init

pub init(Source: mut Self&&)

Transfers closing authority, preserving separately retained operations. Source: Owner left inert.

Channel.operator

pub fn operator =(Source: readonly Self&) -> void = delete;

Rejects duplicate closing authority. Source: Existing owner. Return: No runtime result; this assignment is deleted and cannot be invoked.

Channel.operator

pub fn operator =(Source: mut Self&&) -> void = delete;

Rejects implicit close-and-replace because close can report failure. Source: Existing owner. Return: No runtime result; this assignment is deleted and cannot be invoked.

Channel.IsEmpty

pub IsEmpty: bool;

Observes default/moved owner identity; not queue occupancy.

Channel.Capacity

pub Capacity: usize;

Observes logical bound, or zero for inert owner.

Channel.TryCreate

pub static fn TryCreate(Capacity: usize) -> Result<Self, ChannelError>

Allocates bounded FIFO and native/shared synchronization transactionally. Capacity: Positive logical capacity representable as u64; zero/overflow return InvalidCapacity. Return: Published owner or portable error; partial resources clean up normally. Warning: May allocate and acquire provider resources.

Channel.TrySend

pub readonly fn TrySend(mut Value: T) -> Result<void, ChannelSendError<T>>

Attempts one send without waiting for capacity. Value: Owned item transferred on success, returned on failure. Return: Ok or exact owning error, including Full/Closed/EmptyOwner. Warning: May briefly contend on queue mutex.

Channel.SendAsync

pub readonly async fn SendAsync(mut Value: T, mut Token: CancellationToken) -> Task<Result<void, ChannelSendError<T>>>

Waits asynchronously for capacity, then attempts an owned FIFO commit. Value: Owned input held until commit or returned failure. Token: Owned cancellation observation; cancellation only removes pending wait. Return: Task yielding Ok or error retaining the exact unsent value. Warning: Receiver/storage loans last through completion; no mutex guard crosses await.

Channel.SendAsync

pub readonly fn SendAsync(mut Value: T) -> Task<Result<void, ChannelSendError<T>>>

Supplies the omitted arguments to SendAsync. Note: Defaults: Token = CancellationToken(). Value: Same input, ownership and lifetime contract as the full operation. Return: Exact full-operation result; no additional async frame is created.

Channel.TryReceive

pub readonly fn TryReceive() -> Result<ChannelReceive<T>, ChannelError>

Attempts receive without waiting for item arrival. Return: Owned Item, open Empty, final Closed, or portable failure. Warning: May briefly contend on queue mutex.

Channel.ReceiveAsync

pub readonly async fn ReceiveAsync(mut Token: CancellationToken) -> Task<Result<T?, ChannelError>>

Waits asynchronously for one item, draining buffered values after close. Token: Owned cancellation observation, effective only while pending. Return: Task yielding owned optional item, none for closed EOF, or portable error. Warning: Receiver/storage loans last through completion; no guard crosses await.

Channel.ReceiveAsync

pub readonly fn ReceiveAsync() -> Task<Result<T?, ChannelError>>

Supplies the omitted arguments to ReceiveAsync. Note: Defaults: Token = CancellationToken(). Return: Exact full-operation result; no additional async frame is created.

Channel.CreateAsyncIterator

pub readonly fn CreateAsyncIterator() -> ChannelAsyncIterator<T>

Creates retained consumer state without receiving or allocating backing. Return: Noncopy cursor competing for this FIFO; inert owner yields inert cursor. Warning: Cursor retention does not prevent visible-owner cleanup from closing the channel; committed items remain drainable afterward.

Channel.Close

pub readonly fn Close() -> Result<void, ChannelError>

Idempotently forbids sends and wakes waits; committed values remain drainable. Return: Ok for inert/already closed owner, or unchanged-state lock error. Warning: May briefly contend on queue mutex.

Documentation snapshot: October 24, 2026 About versions ↗
Searching Development · language & standard library