3.24. kink/chan/CHAN

Provides chans, which are message queues between threads. Multiple chans can be waited for at one time.

Example:

:CHAN.require_from('kink/chan/')
:THREAD.require_from('kink/thread/')

# calculates the Nth fibonacci number in an inefficient algorithm
:fib <- {(:N)
  (N <= 1).if_else(
    { 1 }
    { fib(N - 1) + fib(N - 2) }
  )
}

# attempts to calculate a fibonacci number in 3 seconds,
# or timeout
:attempt_fib <- {(:N)
  :Ans_ch = CHAN.new(1)
  THREAD.spawn{
    :Ans = fib(N)
    CHAN.select(
      Ans_ch.send(Ans){}
    )
  }
  CHAN.select(
    Ans_ch.recv(
      {(:Ans) 'fib({}) is {}'.format(N Ans) }
      { raise('Ans_ch is never pinned') }
    )
    CHAN.timeout(3){ 'timeout' }
  )
}
stdout.print_line(attempt_fib(10))  # => fib(10) is 89
stdout.print_line(attempt_fib(20))  # => fib(20) is 10946
stdout.print_line(attempt_fib(100)) # => timeout

Chans are much the same as Golang channels, but there are several differences.

• After closing a chan by Chan.pin(Pin), the Pin val can be read by Chan.recv. It is useful to implement futures or such.

• Even after the chan is closed, Chan.send does not cause an exception, but the event is never fired. It could be useful in some use cases.

• CHAN mod is designed to be used as a base layer of frameworks such as worker pools, and not intended to be used directly from ordinary applications. This is why the mod lacks some convenient features such as chan-as-iter.

3.24.1. CHAN.select(... Events)

CHAN.select waits until exactly one chan event in Events is fired.

Preconditions:

• Events must contain at least one element.

• Events from the same chan cannot appear twice or more in Events.

• CHAN.nowait and CHAN.timeout can appear only once in Events.

• CHAN.nowait and CHAN.timeout cannot appear together in Events.

3.24.2. type chan

A chan is a message queue between threads. Multiple chans can be waited for at one time.

Chan.send(Msg $on_sent)

Returns a chan event which sends the Msg through the Chan, or puts the Msg into the buffer.

When the event is fired, $on_sent is tail-called with no args.

If the Chan has been already pinned, the event does not attempt to send the Msg, and the event is never fired.

Chan.recv($on_received $on_pinned)

Returns a chan event which dequeues a message from the buffer of the Chan, receives a message via the Chan, or waits until the Chan is pinned and all the enqueued messages are dequeued.

If the event is fired receiving a message from the Chan or its buffer, $on_received is tail-called with the message as an argument.

If the event is fired because the Chan is pinned, $on_pinned is tail-called with the current pin val.

Chan.pin(Pin)

Pins the Chan with the Pin val.

When the Chan is pinned, no new senders can be enqueued afterward. After all the messages are dequeued, $on_pinned is called for recv events on the Chan.

This method can be called multiple times for one Chan. Each invocation ovwrrites the current Pin val.

Note: this is similar to close(chan) in Golang, but it can also sets the Pin val.

3.24.3. CHAN.chan?(Val)

CHAN.chan? returns whether Val is a chan.

3.24.4. CHAN.new(Capa)

CHAN.new makes new chan with Capa as the capacity of the buffer.

Precondition:

• Capa must be a non-negative int num.

3.24.5. type event

An event is a chan event selected by CHAN.select.

An event provides no public methods.

3.24.6. CHAN.event?(Val)

CHAN.event? returns whether the Val is a chan event.

3.24.7. CHAN.nowait($body_thunk)

CHAN.nowait makes a chan event which is fired when all other events in the selection have to block the thread.

If the event is fired, $body_thunk is tail-called with no args.

Note: this is similar to "default" clause of Golang select statement.

3.24.8. CHAN.timeout(Seconds $body_thunk)

CHAN.timeout returns a chan event which is fired after the specified Seconds.

If the event is fired, $body_thunk is tail-called with no args.