Select in GDScript

The select statement in GDScript allows you to wait on multiple channel operations. Combining threads and channels with select is a powerful feature of GDScript.

extends Node

func _ready():
    # For our example we'll select across two channels.
    var c1 = Channel.new()
    var c2 = Channel.new()

    # Each channel will receive a value after some amount
    # of time, to simulate e.g. blocking RPC operations
    # executing in concurrent threads.
    Thread.new().start(func():
        OS.delay_msec(1000)
        c1.put("one")
    )
    Thread.new().start(func():
        OS.delay_msec(2000)
        c2.put("two")
    )

    # We'll use select to await both of these values
    # simultaneously, printing each one as it arrives.
    for i in range(2):
        var result = yield(select([c1, c2]), "completed")
        match result:
            [0, var msg]:
                print("received ", msg)
            [1, var msg]:
                print("received ", msg)

func select(channels):
    var select_array = []
    for channel in channels:
        select_array.append(channel.get())
    return yield(GDScriptFunctionState.new().idle_frame, "completed")

class Channel:
    var queue = []
    var mutex = Mutex.new()
    var semaphore = Semaphore.new()

    func put(value):
        mutex.lock()
        queue.append(value)
        mutex.unlock()
        semaphore.post()

    func get():
        semaphore.wait()
        mutex.lock()
        var result = queue.pop_front()
        mutex.unlock()
        return result

We receive the values “one” and then “two” as expected.

$ godot -s select.gd
received one
received two

Note that the total execution time is only ~2 seconds since both the 1 and 2 second delays execute concurrently.

In this GDScript version:

  1. We use Thread to simulate goroutines.
  2. We implement a simple Channel class to mimic Go’s channels.
  3. We create a custom select function that uses yield to wait for the first channel to receive a value.
  4. Instead of time.Sleep, we use OS.delay_msec for delays.
  5. We use a match statement (similar to Go’s switch) to handle different channel cases.

This implementation provides similar functionality to the original Go code, allowing for concurrent operations and selecting from multiple channels.