Skip to content

proposal: allow send on a closed channel to gracefully fail in a select #15411

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
OneOfOne opened this issue Apr 22, 2016 · 7 comments
Closed

Comments

@OneOfOne
Copy link
Contributor

OneOfOne commented Apr 22, 2016

Currently, the only way to close a channel with multiple writers is to use a hacky defer func() { recover() }() or using a waitgroup and a counter.

My proposal is to allow something like this to work without the hack:

func SendVal(ch chan *Val, closeCh chan struct{}, v *Val) (ok bool) {
    select {
    case <-closeCh:
        return false
    case ch <- v:
        return true
    }
}

It should still panic without the select, or an alternative syntax but I know this is highly unlikely in the 1.x cycle.

func SendVal(ch chan *Val, closeCh chan struct{}, v *Val) (ok bool) {
    ok = ch <- v
    return
}

This is not about allowing multiple writers to close a channel, this is about allowing a closed channel in a multiple select situation.

closed <- v
// and
select {
case closed <- v:
}

Should still panic, however:

select {
case closed <- v:
case <-anotherCh:
}

Wouldn't panic (check the CL and the test in it).

@bradfitz
Copy link
Contributor

The current semantics are intentional. Multiple writers each independently closing a channel indicates a design problem. Neither a recover nor a language change is the solution.

This has been discussed at length on various mailing lists.

/cc @adg who might be able to provide references.

@OneOfOne
Copy link
Contributor Author

This isn't a proposal of multiple writers closing the channel, this is a proposal of allowing the channel to be closed by an outside call and gracefully shutting down all the writers.

Right now you can NOT close the channel if there are multiple writers, even when synchronizing with a close channel.

You can use a sync.WaitGroup but this just adds overhead, unless I'm missing something obvious.

@OneOfOne
Copy link
Contributor Author

@bradfitz
Take this example, having to write all that extra code for something that can be done with a select, try to reconsider the use case.

type KeyValueChan struct {
    ch     chan *KeyValue
    wg     sync.WaitGroup
    closed uint32
}

func newKVChan(sz int) *KeyValueChan {
    return &KeyValueChan{
        ch: make(chan *KeyValue, sz),
    }
}

func (ch *KeyValueChan) send(v *KeyValue) (ok bool) {
    if atomic.LoadUint32(&ch.closed) == 1 {
        return
    }
    ch.wg.Add(1)
    ch.ch <- v
    ch.wg.Done()
    return
}

func (ch *KeyValueChan) Break() {
    if atomic.SwapUint32(&ch.closed, 1) != 0 {
        return
    }
    ch.wg.Wait()
    close(ch.ch)
    for range ch.ch {
    }
}

@bradfitz
Copy link
Contributor

Let's move this discussion to the mailing list.

@OneOfOne
Copy link
Contributor Author

@bradfitz I tried to post but it didn't go through, here's an example of what I was talking about https://go-review.googlesource.com/22315

@ianlancetaylor
Copy link
Contributor

@OneOfOne If you send me your e-mail address (you can send to iant@golang.org or reply here) I'll check whether you are somehow blocked on the mailing lists.

@bradfitz
Copy link
Contributor

@golang golang locked and limited conversation to collaborators Apr 23, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants