Channel behaviour

Newbie question on channels:

I send a message to a buffered channel which has a receiver waiting on a select.

If I send the message and immediately check len(channel), its empty.
This appears to be because the receiver has immediately read the channel before the len() is executed. This is reasonable behaviour.

I’m trying to understand if I can see if the receiver process is busy or not. Especially for a unit test, and maybe later to see if an entire set of goroutines are quiescent (i.e. all block on a select). Currently I’m setting a boolean immediately after the select fires, but there is a race condition where len() is zero but the boolean is not yet set, and so the code thinks the receiver is not busy but in fact it has only just begun.

Is there a way to solve this, e.g. by being able to place a lock as soon as the channel is read?

current code:

select {
        case msg := <- channel:
                mu.Lock()
                Busy = true
                mu.Unlock()

In the above there is a window where the channel has been read (len() is 0) but we have not yet assigned Busy.

Is there a way to solve this?

But perhaps a better question is is there a way to know if a specific goroutine is blocked on a select in a thread safe way?

Since channels are the fundamental way for cross process communication, you should probably stick to them.
One way to fix the problem would be to have 2 channels for each process: start and end. When we want to post work for the routine we push an item into both channels - first a token into the end-channel then the actual work-item into the start-channel.
The process will consume an item from the start-channel to start working and consumes the token from the end-channel when it is done.
To see if the process is busy, we just look at len(endChannel)

I have derived one possible solution to this but its a bit grungy. I can fetch the stacks of the goroutines in question and extract the run status. If the select has fired then the status is no longer ‘select’ and I know the goroutine is active.
But it requires parsing the text based stack information and also fetching and storing the goid of the goroutine when created – which appears to be discouraged by the golang gods.

Thanks Falco. I’ll give this some thought as its probably better than my solution above based on parsing goroutine stack information.