# Am I using select properly?

**URL:** https://forum.crystal-lang.org/t/am-i-using-select-properly/2943
**Category:** Community
**Created:** [February 7, 2021, 1:29am UTC](https://forum.crystal-lang.org/t/am-i-using-select-properly/2943 "2021-02-07T01:29:10Z")
**Posts on this page:** 2
**Page:** 1

<div class="post-metadata">

### Author: ![BrucePerens](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/bruceperens/32/852_2.png) [@BrucePerens](https://forum.crystal-lang.org/u/BrucePerens)
#### Post date: [February 7, 2021, 1:29am UTC](https://forum.crystal-lang.org/t/am-i-using-select-properly/2943/1 "2021-02-07T01:29:10Z")

</div>

Ah, the mysterious `select`, undocumented except for “it works like select in go”, although Crystal doesn’t look much like go 🙂

I cooked up this concurrent queue container example, which is supposed to collect data with `fill` until it is asked for the data with `drain_all`, at which time it returns an array of all that it has collected. `drain` returns just one datum. Both should pause the calling fiber until there is at least one datum for them to supply. `Channel` could do this job by itself, but it doesn’t auto-grow, and you would need `select` to do multiple receives without blocking. I have some questions:

Am I using `select` properly?

Does the fiber just magically get killed when it is garbage collected?

```
Thanks

Bruce

```

```crystal
# Concurrent queue container.
# A datum is added to the queue with `#fill`.
# `drain` returns one datum, `drain_all` returns an array (actually a deque)
# of all of the data that have been added since it was last called, or the
# start of this object. `drain` and `#drain_all` will not return until there
# is at least one datum.
class ConcurrentQueue(T)
  @fiber : Fiber
  @fill : Channel(T)
  @drain_all : Channel(Deque(T))
  @drain : Channel(T)

  def initialize
    @fill = Channel(T).new(capacity: 10)
    @drain = Channel(T).new(capacity: 0)
    @drain_all = Channel(Deque(T)).new(capacity: 0)

    @fiber = spawn name: "Queue(#{T.to_s}) fiber." do
      data = Deque(T).new
      data << @fill.receive
      loop do
        select
        when @drain_all.send data
          data = Deque(T).new
          data << @fill.receive
        when @drain.send data[0]
          data.shift
          data << @fill.receive if data.size == 0
        when r = @fill.receive
          data << r
        end
      end
    end
  end

  def fill(i : T)
    @fill.send(i)
  end

  def drain : T
    @drain.receive
  end

  def drain_all : Deque(T)
    @drain_all.receive
  end
end

q = ConcurrentQueue(String).new
q.fill("One")
q.fill("Two")
q.fill("Three")
Fiber.yield # Because the queue is more eager to drain than fill.
p q.drain
p q.drain_all

```

---

<div class="post-metadata">

### Author: ![sol.vin](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/sol.vin/32/73_2.png) [@sol.vin](https://forum.crystal-lang.org/u/sol.vin)
#### Post date: [February 7, 2021, 9:44pm UTC](https://forum.crystal-lang.org/t/am-i-using-select-properly/2943/2 "2021-02-07T21:44:33Z")

</div>

I’d recommend this article. [5 use cases for Crystal's select statement - lbarasti's blog](https://lbarasti.com/post/select_statement/)

Lays out some of the best use cases for select, namely timeout and multiple receives at the same time.

As for the code, you should do something to break out of the `loop`, otherwise it will go on forever until the main fiber is killed, which will keep it in memory even when it’s not in use. You need to add something to tell the `loop` “I’m done! Stop running this fiber so I can be garbage collected at some point”. You also may want to guard against `Channel::ClosedError` when using `Channel#send` or `Channel#receive`, as well you should `close` your channels. a `begin, rescue, finally` block would do that nicely here.
