Skip to content

Instantly share code, notes, and snippets.

@bcardiff
Created October 8, 2019 20:11
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bcardiff/289953a80eb3a0512a2a2f8c8dfeb1db to your computer and use it in GitHub Desktop.
Save bcardiff/289953a80eb3a0512a2a2f8c8dfeb1db to your computer and use it in GitHub Desktop.
Blocking and non-blocking channel actions

Blocking and non-blocking channel actions

Channel#receive vs Channel#receive? differs on their behavior for closed channels. Using them directly is always blocking.

Performing an operation over a closed and closing the channel during the operation must behave in the same way.

Channel#receive

Given a ch : Channel(T), ch2 : Channel(T2):

1. blocking raise-on-close single-channel

m = ch.receive
select
when m = ch.receive
end
i, m = Channel.select(ch.receive_select_action)
  • i : Int32, m : T
  • will suspend current fiber if there are no pending messages in ch
  • if ch was closed, or is closed while fiber is suspended, it will raise ClosedError
  • on ch.send(t), i == 0 && m == t

2. non-blocking raise-on-close single-channel

select
when m = ch.receive
else
end
  • m : T
  • will execute else block if there are no pending messages in ch
  • if channel was closed will raise ClosedError
    • Since it is a non-blocking version, there is no need need to consider if the channel is closed while fiber is suspended.
  • on ch.send(t), m == t, execute first when block.

2.1 non-blocking raise-on-close single-channel (without select)

i, m = Channel.non_blocking_select(ch.receive_select_action)
  • i : Int32, m : T | NotReady
  • i == 1 && m == NotReady if there are no pending messages in ch
  • if any ch was closed, it will raise ClosedError
    • Since it is a non-blocking version, there is no need need to consider if the channel is closed while fiber is suspended.

3. blocking raise-on-close multi-channel

select
when m = ch.receive
when m2 = ch2.receive
end
  • m : T, m2 : T2
  • if non of the channel has messages, the current fiber will be suspended.
  • if any ch or ch2 was or is closed, the select will raise ClosedError
  • on ch.send(t), m == t, execute first when block.
  • on ch2.send(t), m2 == t, execute second when block.

3.1 blocking raise-on-close multi-channel (without select)

i, m = Channel.select(ch.receive_select_action, ch2.receive_select_action)
  • i : Int32, m : T | T2
  • will suspend current fiber if there are no pending messages in neither ch nor ch2
  • if any ch or ch2 was closed, or is closed while fiber is suspended, it will raise ClosedError

4. non-blocking raise-on-close multi-channel

select
when m1 = ch.receive
when m2 = ch2.receive
else
end
  • m : T, m2 : T2
  • if any ch or ch2 was closed, the select will raise ClosedError
    • Since it is a non-blocking version, there is no need need to consider if the channel is closed while fiber is suspended.
  • if non of the channel has messages, the else block will execute.

5. blocking nil-on-close single-channel

m = ch.receive?
select
when m = ch.receive?
end
i, m = Channel.select(ch.receive_select_action?)
  • i : Int32, m : T | Nil
  • will suspend current fiber if there are no pending messages in ch
  • if channel was closed (or is closed while fiber is suspended) i == 0 && m == nil

6. blocking nil-on-close multi-channel

select
when m1 = ch.receive?
when m2 = ch2.receive?
end
  • m1 : T1 | Nil, m2 : T2 | Nil
  • will suspend current fiber if there are no pending messages in neither ch nor ch2
  • if channel was closed (or is closed while fiber is suspended) i == 0 && m1 == nil (for ch) or i == 1 && m2 == nil (for ch2)
    • Note: if there are many channels closed, then one of them will be picked.

6.1 blocking nil-on-close multi-channel (without select)

i, m = Channel.select(ch.receive_select_action?, ch2.receive_select_action?)
  • i : Int32, m : T | T2 | Nil
  • will suspend current fiber if there are no pending messages in neither ch nor ch2
  • if channel was closed (or is closed while fiber is suspended) i == 0 && m == nil (for ch) or i == 1 && m == nil (for ch2)
    • Note: if there are many channels closed, then one of them will be picked.

7. non-blocking nil-on-close single-channel

select
when m = ch.receive?
else
end
i, m = Channel.non_blocking_select(ch.receive_select_action?)
  • i : Int32, m : T | Nil | NotReady
  • m == NotReady if there are no pending messages in ch
  • m == nil if channel was closed
    • Since it is a non-blocking version, there is no need need to consider if the channel is closed while fiber is suspended

8. non-blocking nil-on-close multi-channel

select
when m = ch.receive?
when m2 = ch2.receive?
else
end
  • m : T | Nil, m2 : T2 | Nil
  • will execute else block if there are no pending messages in neither ch, nor ch2
  • m == nil if ch was closed
  • m2 = nil if ch2 was closed
    • Since it is a non-blocking version, there is no need need to consider if the channel is closed while fiber is suspended

8.1 non-blocking nil-on-close multi-channel (without select)

i, m = Channel.non_blocking_select(ch.receive_select_action?, ch2.receive_select_action?)
  • i : Int32, m : T | T2 | Nil | NotReady
  • m == NotReady if there are no pending messages in neither ch, nor ch2
  • m == nil if channel was closed (i == 0 for ch, i == 1 for ch2)
    • Since it is a non-blocking version, there is no need need to consider if the channel is closed while fiber is suspended

Channel#send

Given ch : Channel(T), ch2 : Channel(T2), t : T, t2 : T2:

1. blocking raise-on-close single-channel

ch.send t
select
when ch.send(t)
end
i, m = Channel.select(ch.send_select_action(t))
  • i : Int32, m : Nil
  • will suspend current fiber if there are no receivers for ch
  • if ch was closed, or is closed while fiber is suspended, it will raise ClosedError
  • on ch.receive, i == 0, m == nil, will execute the first when block

2. non-blocking raise-on-close single-channel

select
when ch.send(t)
else
end
i, m = Channel.non_blocking_select(ch.send_select_action(t))
  • i : Int32, m : Nil | NotReady
  • if ch was closed, it will raise ClosedError
    • Since it is a non-blocking version, there is no need need to consider if the channel is closed while fiber is suspended
  • if there is a receiver on ch, i == 0, m == nil, will execute the first when block
  • if there are no receivers on ch, m == NotReady, will execute the else block

3. blocking raise-on-close multi-channel

select
when ch.send(t)
when ch2.send(t2)
end
i, m = Channel.select(ch.send_select_action(t), ch2.send_select_action(t2))
  • i : Int32, m : Nil
  • will suspend current fiber if there are no receivers for ch and ch2
  • if ch or ch2 were closed, or some is closed while fiber is suspended, it will raise ClosedError
  • on ch.receive, i == 0, m == nil, will execute the first when block
  • on ch2.receive, i == 1, m == nil, will execute the first when block

4. non-blocking raise-on-close multi-channel

select
when ch.send(t)
when ch2.send(t2)
else
end
i, m = Channel.non_blocking_select(ch.send_select_action(t), ch2.send_select_action(t2))
  • i : Int32, m : Nil | NotReady
  • if ch or ch2 were closed, it will raise ClosedError
    • Since it is a non-blocking version, there is no need need to consider if the channel is closed while fiber is suspended
  • if there is a receiver on ch, i == 0, m == nil, will execute the first when block
  • if there is a receiver on ch2, i == 1, m == nil, will execute the second when block
  • if there are no receivers on ch and ch2, m == NotReady, will execute the else block
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment