core/sync/chan

sync_chan

Types

4

Chan

Chan :: struct {}Source

A typed wrapper around Raw_Chan which should be used preferably.

Note: all procedures accepting Raw_Chan also accept Chan.

Inputs

Example:

import "core:sync/chan"

chan_example :: proc() {
	// Create an unbuffered channel with messages of type int,
	// supporting both sending and receiving.
	// Creating unidirectional channels, although possible, is useless.
	c, _ := chan.create(chan.Chan(int), context.allocator)
	defer chan.destroy(c)

	// This channel can now only be used for receiving messages
	recv_only_channel: chan.Chan(int, .Recv) = chan.as_recv(c)
	// This channel can now only be used for sending messages
	send_only_channel: chan.Chan(int, .Send) = chan.as_send(c)
}

Raw_Chan

Raw_Chan :: struct { // Shared allocator: runtime.Allocator, allocation_size: int, msg_size: u16, closed: b16, mutex: sync.Mutex, r_cond: sync.Cond, w_cond: sync.Cond, r_waiting: int, w_waiting: int, did_read: bool, // Buffered queue: ^Raw_Queue, // Unbuffered unbuffered_data: rawptr, }Source

Raw_Chan allows for thread-safe communication using fixed-size messages. This is the low-level implementation of Chan, which does not include the concept of Direction.

Example:

import "core:sync/chan"

raw_chan_example :: proc() {
	// Create an unbuffered channel with messages of type int,
	c, _ := chan.create_raw(size_of(int), align_of(int), context.allocator)
	defer chan.destroy(c)
}

Procedures

25

as_recv

as_recv :: proc(c: C) -> (r: Chan(T, .Recv))Source

Creates a version of a channel that can only be used for receiving not sending.

Inputs

  • c: The channel

Returns:

  • An Allocator_Error

Example:

import "core:sync/chan"

as_recv_example :: proc() {
	consumer :: proc(c: chan.Chan(int, .Recv)) {
		value, ok := chan.recv(c)

		// compile-time error:
		// chan.send(c, 22)
	}

	c, err := chan.create(chan.Chan(int), 1, context.allocator)
	assert(err == .None)
	defer chan.destroy(c)

	chan.send(c, 112)
	consumer(chan.as_recv(c))
}

as_send

as_send :: proc(c: C) -> (s: Chan(T, .Send))Source

Creates a version of a channel that can only be used for sending not receiving.

Inputs

  • c: The channel

Returns:

  • An Allocator_Error

Example:

import "core:sync/chan"

as_send_example :: proc() {
	// this procedure takes a channel that can only
	// be used for sending not receiving.
	producer :: proc(c: chan.Chan(int, .Send)) {
		chan.send(c, 112)

		// compile-time error:
		// value, ok := chan.recv(c)
	}

	c, err := chan.create(chan.Chan(int), 1, context.allocator)
	assert(err == .None)
	defer chan.destroy(c)

	producer(chan.as_send(c))
}

can_recv

can_recv :: proc(c: ^Raw_Chan) -> (bool)Source

Returns whether a message can be read without blocking the current thread. Specifically, it checks if the channel is buffered and not full, or if there is already a writer attempting to send a message.

Inputs

  • c: The channel

Returns

  • true if a message can be read, false otherwise

Example:

import "core:sync/chan"

can_recv_example :: proc() {
	c, err := chan.create(chan.Chan(int), 1, context.allocator)
	assert(err == .None)
	defer chan.destroy(c)

	assert(!chan.can_recv(c), "the cannel is empty")
	assert(chan.send(c, 2))
	assert(chan.can_recv(c), "there is message to read")
}

can_send

can_send :: proc(c: ^Raw_Chan) -> (bool)Source

Returns whether a message can be sent without blocking the current thread. Specifically, it checks if the channel is buffered and not full, or if there is already a reader waiting for a message.

Inputs

  • c: The channel

Returns

  • true if a message can be sent, false otherwise

Example:

import "core:sync/chan"

can_send_example :: proc() {
	c, err := chan.create(chan.Chan(int), 1, context.allocator)
	assert(err == .None)
	defer chan.destroy(c)

	assert(chan.can_send(c), "the channel's buffer is not full")
	assert(chan.send(c, 2))
	assert(!chan.can_send(c), "the channel's buffer is full")
}

cap

cap :: proc(c: ^Raw_Chan) -> (int)Source

Returns the number of elements the channel could hold.

Note: Unbuffered channels will always return 0 because they cannot hold elements.

Inputs

  • c: The channel

Returns:

  • Number of elements

Example:

import "core:sync/chan"
import "core:fmt"

cap_example :: proc() {
	c, _ := chan.create(chan.Chan(int), 2, context.allocator)
	defer chan.destroy(c)

	fmt.println(chan.cap(c))
}

Output:

2

close

close :: proc(c: ^Raw_Chan) -> (bool)Source

Closes the channel, preventing new messages from being added.

Inputs

  • c: The channel

Returns:

  • true if the channel was closed by this operation, false if it was already closed

Example:

import "core:sync/chan"

close_example :: proc() {
	c, _ := chan.create(chan.Chan(int), 2, context.allocator)
	defer chan.destroy(c)

	// Sending a message to an open channel
	assert(chan.send(c, 1), "allowed to send")

	// Closing the channel successfully
	assert(chan.close(c), "successfully closed")

	// Trying to send a message after the channel is closed (should fail)
	assert(!chan.send(c, 1), "not allowed to send after close")

	// Trying to close the channel again (should fail since it's already closed)
	assert(!chan.close(c), "was already closed")
}

create_buffered

create_buffered :: proc(C: typeid, cap: int, allocator: runtime.Allocator) -> (c: C, err: runtime.Allocator_Error)Source

Creates a buffered version of the specified Chan type.

Allocates Using Provided Allocator

Inputs

  • $C: Type of Chan to create
  • cap: The capacity of the channel
  • allocator: The allocator to use

Returns:

  • The initialized Chan
  • An Allocator_Error

Example:

import "core:sync/chan"

create_buffered_example :: proc() {
	c, err := chan.create_buffered(chan.Chan(int), 10, context.allocator)
	assert(err == .None)
	defer chan.destroy(c)
}

create_raw_buffered

create_raw_buffered :: proc(any_int, msg_alignment: int, cap: int, allocator: runtime.Allocator, loc = #caller_location) -> (c: ^Raw_Chan, err: runtime.Allocator_Error)Source

Creates a buffered Raw_Chan for messages of the specified size and alignment.

Allocates Using Provided Allocator

Inputs

  • msg_size: The size of the messages the messages being sent
  • msg_alignment: The alignment of the messages being sent
  • cap: The capacity of the channel
  • allocator: The allocator to use

Returns:

  • The initialized Raw_Chan
  • An Allocator_Error

Example:

import "core:sync/chan"

create_raw_unbuffered_example :: proc() {
	c, err := chan.create_raw_buffered(size_of(int), align_of(int), 10, context.allocator)
	assert(err == .None)
	defer chan.destroy(c)
}

create_raw_unbuffered

create_raw_unbuffered :: proc(any_int, msg_alignment: int, allocator: runtime.Allocator, loc = #caller_location) -> (c: ^Raw_Chan, err: runtime.Allocator_Error)Source

Creates an unbuffered Raw_Chan for messages of the specified size and alignment.

Allocates Using Provided Allocator

Inputs

  • msg_size: The size of the messages the messages being sent
  • msg_alignment: The alignment of the messages being sent
  • allocator: The allocator to use

Returns:

  • The initialized Raw_Chan
  • An Allocator_Error

Example:

import "core:sync/chan"

create_raw_unbuffered_example :: proc() {
	unbuffered, err := chan.create_raw(size_of(int), align_of(int), context.allocator)
	assert(err == .None)
	defer chan.destroy(unbuffered)
}

create_unbuffered

create_unbuffered :: proc(C: typeid, allocator: runtime.Allocator) -> (c: C, err: runtime.Allocator_Error)Source

Creates an unbuffered version of the specified Chan type.

Allocates Using Provided Allocator

Inputs

  • $C: Type of Chan to create
  • allocator: The allocator to use

Returns:

  • The initialized Chan
  • An Allocator_Error

Example:

import "core:sync/chan"

create_unbuffered_example :: proc() {
	c, err := chan.create_unbuffered(chan.Chan(int), context.allocator)
	assert(err == .None)
	defer chan.destroy(c)
}

is_buffered

is_buffered :: proc(c: ^Raw_Chan) -> (bool)Source

Checks if the given channel is buffered.

Inputs

  • c: The channel

Returns:

  • true if the channel is buffered, false otherwise

Example:

import "core:sync/chan"

is_buffered_example :: proc() {
	c, _ := chan.create(chan.Chan(int), 1, context.allocator)
	defer chan.destroy(c)
	assert(chan.is_buffered(c))
}

is_unbuffered

is_unbuffered :: proc(c: ^Raw_Chan) -> (bool)Source

Checks if the given channel is unbuffered.

Inputs

  • c: The channel

Returns:

  • true if the channel is unbuffered, false otherwise

Example:

import "core:sync/chan"

is_buffered_example :: proc() {
	c, _ := chan.create(chan.Chan(int), context.allocator)
	defer chan.destroy(c)
	assert(chan.is_unbuffered(c))
}

len

len :: proc(c: ^Raw_Chan) -> (int)Source

Returns the number of elements currently in the channel.

Note: Unbuffered channels will always return 0 because they cannot hold elements.

Inputs

  • c: The channel

Returns:

  • Number of elements

Example:

import "core:sync/chan"
import "core:fmt"

len_example :: proc() {
	c, _ := chan.create(chan.Chan(int), 2, context.allocator)
	defer chan.destroy(c)

	fmt.println(chan.len(c))
	assert(chan.send(c, 1))   // add an element
	fmt.println(chan.len(c))
}

Output:

0
1

recv

recv :: proc(c: C) -> (data: T, ok: bool)Source

Reads a message from the channel, blocking the current thread if:

  • the channel is unbuffered
  • the channel's buffer is empty

until the channel is being written to or the channel is closed. recv will return false when attempting to receive a message on an already closed channel.

Inputs

  • c: The channel

Returns

  • The message
  • true if a message was received, false when the channel was already closed

Example:

import "core:sync/chan"

recv_example :: proc() {
	c, err := chan.create(chan.Chan(int), 1, context.allocator)
	assert(err == .None)
	defer chan.destroy(c)

	assert(chan.send(c, 2))

	value, ok := chan.recv(c)
	assert(ok, "the value was received")

	// this would block since the channel is now empty
	// value, ok = chan.recv(c)

	// reading from a closed channel returns false
	chan.close(c)
	value, ok = chan.recv(c)
	assert(!ok, "the channel is closed")
}

recv_raw

recv_raw :: proc(c: ^Raw_Chan, msg_out: rawptr) -> (ok: bool)Source

Reads a message from the channel, blocking the current thread if:

  • the channel is unbuffered
  • the channel's buffer is empty

until the channel is being written to or the channel is closed. recv_raw will return false when attempting to receive a message on an already closed channel.

Note: The location pointed to by msg_out must match the size and alignment used when the Raw_Chan was created.

Inputs

  • c: The channel
  • msg_out: Pointer to where the message should be stored

Returns

  • true if a message was received, false when the channel was already closed

Example:

import "core:sync/chan"

recv_raw_example :: proc() {
	c, err := chan.create_raw(size_of(int), align_of(int), 1, context.allocator)
	assert(err == .None)
	defer chan.destroy(c)

	value := 2
	assert(chan.send_raw(c, &value))

	assert(chan.recv_raw(c, &value))

	// this would block since the channel is now empty
	// assert(chan.recv_raw(c, &value))

	// reading from a closed channel returns false
	chan.close(c)
	assert(! chan.recv_raw(c, &value))
}

send

send :: proc(c: C, data: T) -> (ok: bool)Source

Sends the specified message, blocking the current thread if:

  • the channel is unbuffered
  • the channel's buffer is full

until the channel is being read from or the channel is closed. send will return false when attempting to send on an already closed channel.

Inputs

  • c: The channel
  • data: The message to send

Returns

  • true if the message was sent, false when the channel was already closed

Example:

import "core:sync/chan"

send_example :: proc() {
	c, err := chan.create(chan.Chan(int), 1, context.allocator)
	assert(err == .None)
	defer chan.destroy(c)

	assert(chan.send(c, 2))

	// this would block since the channel has a buffersize of 1
	// assert(chan.send(c, 2))

	// sending on a closed channel returns false
	chan.close(c)
	assert(! chan.send(c, 2))
}

send_raw

send_raw :: proc(c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool)Source

Sends the specified message, blocking the current thread if:

  • the channel is unbuffered
  • the channel's buffer is full

until the channel is being read from or the channel is closed. send_raw will return false when attempting to send on an already closed channel.

Note: The message referenced by msg_out must match the size and alignment used when the Raw_Chan was created.

Inputs

  • c: The channel
  • msg_out: Pointer to the data to send

Returns

  • true if the message was sent, false when the channel was already closed

Example:

import "core:sync/chan"

send_raw_example :: proc() {
	c, err := chan.create_raw(size_of(int), align_of(int), 1, context.allocator)
	assert(err == .None)
	defer chan.destroy(c)

	value := 2
	assert(chan.send_raw(c, &value))

	// this would block since the channel has a buffersize of 1
	// assert(chan.send_raw(c, &value))

	// sending on a closed channel returns false
	chan.close(c)
	assert(! chan.send_raw(c, &value))
}

try_recv

try_recv :: proc(c: C) -> (data: T, ok: bool)Source

Tries reading a message from the channel in a non-blocking fashion.

Inputs

  • c: The channel

Returns

  • The message
  • true if a message was received, false when the channel was already closed or no message was available

Example:

import "core:sync/chan"

try_recv_example :: proc() {
	c, err := chan.create(chan.Chan(int), context.allocator)
	assert(err == .None)
	defer chan.destroy(c)

	_, ok := chan.try_recv(c)
	assert(!ok, "there is not value to read")
}

try_recv_raw

try_recv_raw :: proc(c: ^Raw_Chan, msg_out: rawptr) -> (bool)Source

Reads a message from the channel if one is available.

Note: The location pointed to by msg_out must match the size and alignment used when the Raw_Chan was created.

Inputs

  • c: The channel
  • msg_out: Pointer to where the message should be stored

Returns

  • true if a message was received, false when the channel was already closed or no message was available

Example:

import "core:sync/chan"

try_recv_raw_example :: proc() {
	c, err := chan.create_raw(size_of(int), align_of(int), context.allocator)
	assert(err == .None)
	defer chan.destroy(c)

	value: int
	assert(!chan.try_recv_raw(c, &value))
}

try_select_raw

try_select_raw :: proc(recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs: []rawptr, recv_out: rawptr) -> (select_idx: int, status: Select_Status)Source

Attempts to either send or receive messages on the specified channels without blocking.

try_select_raw first identifies which channels have messages ready to be received and which are available for sending. It then randomly selects one operation (either a send or receive) to perform.

If no channels have messages ready, the procedure is a noop.

Note: Each message in send_msgs corresponds to the send channel at the same index in sends. If the message is nil, corresponding send channel will be skipped.

Inputs

  • recv: A slice of channels to read from
  • sends: A slice of channels to send messages on
  • send_msgs: A slice of messages to send
  • recv_out: A pointer to the location where, when receiving, the message should be stored

Returns

  • Position of the available channel which was used for receiving or sending
  • true if sending/receiving was successfull, false if the channel was closed or no channel was available

Example:

import "core:sync/chan"
import "core:fmt"

try_select_raw_example :: proc() {
	c, err := chan.create(chan.Chan(int), 1, context.allocator)
	assert(err == .None)
	defer chan.destroy(c)

	// sending value '1' on the channel
	value1 := 1
	msgs := [?]rawptr{&value1}
	send_chans := [?]^chan.Raw_Chan{c}

	// for simplicity the same channel used for sending is also used for receiving
	receive_chans := [?]^chan.Raw_Chan{c}
	// where the value from the read should be stored
	received_value: int

	idx, ok := chan.try_select_raw(receive_chans[:], send_chans[:], msgs[:], &received_value)
	fmt.println("SELECT:        ", idx, ok)
	fmt.println("RECEIVED VALUE ", received_value)

	idx, ok = chan.try_select_raw(receive_chans[:], send_chans[:], msgs[:], &received_value)
	fmt.println("SELECT:        ", idx, ok)
	fmt.println("RECEIVED VALUE ", received_value)

	// closing of a channel also affects the select operation
	chan.close(c)

	idx, ok = chan.try_select_raw(receive_chans[:], send_chans[:], msgs[:], &received_value)
	fmt.println("SELECT:        ", idx, ok)
}

Output:

SELECT:         0 Send
RECEIVED VALUE  0
SELECT:         0 Recv
RECEIVED VALUE  1
SELECT:         -1 None

try_send

try_send :: proc(c: C, data: T) -> (ok: bool)Source

Tries sending the specified message which is:

  • blocking: given the channel is unbuffered
  • non-blocking: given the channel is buffered

Inputs

  • c: The channel
  • data: The message to send

Returns

  • true if the message was sent, false when the channel was

already closed or the channel's buffer was full

Example:

import "core:sync/chan"

try_send_example :: proc() {
	c, err := chan.create(chan.Chan(int), 1, context.allocator)
	assert(err == .None)
	defer chan.destroy(c)

	assert(chan.try_send(c, 2), "there is enough space")
	assert(!chan.try_send(c, 2), "the buffer is already full")
}

try_send_raw

try_send_raw :: proc(c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool)Source

Tries sending the specified message which is:

  • blocking: given the channel is unbuffered
  • non-blocking: given the channel is buffered

Note: The message referenced by msg_out must match the size and alignment used when the Raw_Chan was created.

Inputs

  • c: the channel
  • msg_out: pointer to the data to send

Returns

  • true if the message was sent, false when the channel was

already closed or the channel's buffer was full

Example:

import "core:sync/chan"

try_send_raw_example :: proc() {
	c, err := chan.create_raw(size_of(int), align_of(int), 1, context.allocator)
	assert(err == .None)
	defer chan.destroy(c)

	value := 2
	assert(chan.try_send_raw(c, &value), "there is enough space")
	assert(!chan.try_send_raw(c, &value), "the buffer is already full")
}

Procedure Groups

2

create

create :: proc{create_unbuffered, create_buffered}Source

Creates a buffered or unbuffered Chan instance.

Allocates Using Provided Allocator

Inputs

  • $C: Type of Chan to create
  • [cap: The capacity of the channel] omit for creating unbuffered channels
  • allocator: The allocator to use

Returns:

  • The initialized Chan
  • An Allocator_Error

Example:

import "core:sync/chan"

create_example :: proc() {
	unbuffered: chan.Chan(int)
	buffered: chan.Chan(int)
	err: runtime.Allocator_Error

	unbuffered, err = chan.create(chan.Chan(int), context.allocator)
	assert(err == .None)
	defer chan.destroy(unbuffered)

	buffered, err = chan.create(chan.Chan(int), 10, context.allocator)
	assert(err == .None)
	defer chan.destroy(buffered)
}

create_raw

create_raw :: proc{create_raw_unbuffered, create_raw_buffered}Source

Creates a buffered or unbuffered Raw_Chan for messages of the specified size and alignment.

Allocates Using Provided Allocator

Inputs

  • msg_size: The size of the messages the messages being sent
  • msg_alignment: The alignment of the messages being sent
  • [cap: The capacity of the channel] omit for creating unbuffered channels
  • allocator: The allocator to use

Returns:

  • The initialized Raw_Chan
  • An Allocator_Error

Example:

import "core:sync/chan"

create_raw_example :: proc() {
	unbuffered: ^chan.Raw_Chan
	buffered: ^chan.Raw_Chan
	err: runtime.Allocator_Error

	unbuffered, err = chan.create_raw(size_of(int), align_of(int), context.allocator)
	assert(err == .None)
	defer chan.destroy(unbuffered)

	buffered, err = chan.create_raw(size_of(int), align_of(int), 10, context.allocator)
	assert(err == .None)
	defer chan.destroy(buffered)
}

Reference search

Find anything

Documentation preferences

Settings

System theme variants

Used only while Theme is set to System.