The Crystal Programming Language Forum

Select statement implementation evaluates send data before channel is sendable

I have a select statement that sends data, and looks like this

select
    when @my_channel.send data

In the above expression, data is evaluated at the start of the select statement, before @my_channel could be sent to without blocking. I’d really like to be able to process the data to be sent when @my_channel becomes sendable, before sending it. I don’t see any way to do this given the present implementation of select.

In pseudocode, what I’d like to do is:

select
  sendable @my_channel
    data = some_processing
    @my_channel.send data

What are you trying to, overall, do? It feels like data could simply be a Proc that you execute when needed.

I tried @my_channel.send Deque#to_a, with the select in a loop and another when statement filling up the Deque. That didn’t work as expected. I suppose I might be able to use the select primitives that are methods of Channel, but there is a 2018 PR to document them that was rejected because they are internal.

I think I could do this using Channel#select(my_channel1.send_select_action, my_channel2.receive_select_action) followed by a case based on the argument index returned by Channel#select. But that’s using undocumented functions not meant for the hoi polloi.

I don’t think you will be able to do that. Is equivalent to trying to know if a channel.send action will block you or not: you want to enqueue certain data you don’t know if the channel is “free”.

Suppose you know a channel is free to receive data, between that moment and the moment you actually have the data to send the situation could’ve change. So you are back to square one.

What are you trying to do with that queue? If its data is not only send to the channel, what is are you doing with it?

1 Like

@bcardiff Yes, you are right, I would need my modified select action to take a lock on the channel before it wakes the waiting fiber, and then release the lock when the receive or send action completes (it would be a block). This is a desirable feature IMO, but perhaps should use its own version of Channel, because the existing Channel only uses a SpinLock, and it’s bad to hold a SpinLock for that long. I really want something like Mutex#synchronize for this.

Why did I need it? It happened that I was using a Deque to manage my data, and I wanted to call #to_a before sending it. In the current implementation, this would throw away memory in a loop.

As it happens, I was making a higher-level IPC object - one that is implemented on top of Channel and behaves slightly differently from Channel. If I wanted ultimate performance, I would reimplement Channel with my desired behavior. IMO there is room for shards full of such things.

1 Like