Skip to content

Commit 0681b59

Browse files
authored
Fix send on closed channel panic (#140)
1 parent 099cb5b commit 0681b59

File tree

3 files changed

+19
-30
lines changed

3 files changed

+19
-30
lines changed

internal/common/common.go

-8
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,6 @@ func CloneBytes(b []byte) []byte {
3535
return clone
3636
}
3737

38-
func SafeCloseDoneChan(c chan<- struct{}) (ok bool) {
39-
defer func() {
40-
ok = recover() == nil
41-
}()
42-
close(c)
43-
return
44-
}
45-
4638
func ToMilliseconds(duration time.Duration) int64 {
4739
return int64(duration) / 1e6
4840
}

rx/mono/block_subscriber.go

+18-19
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@ import (
66
"github.com/jjeffcaii/reactor-go"
77
"github.com/rsocket/rsocket-go/internal/common"
88
"github.com/rsocket/rsocket-go/payload"
9+
"go.uber.org/atomic"
910
)
1011

1112
type blockSubscriber struct {
12-
done chan struct{}
13-
vchan chan<- payload.Payload
14-
echan chan<- error
13+
// Atomic bool to ensure that 'done' is closed only once.
14+
isDone *atomic.Bool
15+
done chan struct{}
16+
vchan chan<- payload.Payload
17+
echan chan<- error
1518
}
1619

1720
func newBlockSubscriber(
@@ -20,34 +23,30 @@ func newBlockSubscriber(
2023
echan chan<- error,
2124
) reactor.Subscriber {
2225
return blockSubscriber{
23-
done: done,
24-
vchan: vchan,
25-
echan: echan,
26+
isDone: atomic.NewBool(false),
27+
done: done,
28+
vchan: vchan,
29+
echan: echan,
2630
}
2731
}
2832

2933
func (b blockSubscriber) OnComplete() {
30-
select {
31-
case <-b.done:
32-
default:
33-
_ = common.SafeCloseDoneChan(b.done)
34+
swapped := b.isDone.CAS(false, true)
35+
if swapped {
36+
close(b.done)
3437
}
3538
}
3639

3740
func (b blockSubscriber) OnError(err error) {
38-
select {
39-
case <-b.done:
40-
default:
41-
if common.SafeCloseDoneChan(b.done) {
42-
b.echan <- err
43-
}
41+
swapped := b.isDone.CAS(false, true)
42+
if swapped {
43+
b.echan <- err
44+
close(b.done)
4445
}
4546
}
4647

4748
func (b blockSubscriber) OnNext(any reactor.Any) {
48-
select {
49-
case <-b.done:
50-
default:
49+
if !b.isDone.Load() {
5150
if r, ok := any.(common.Releasable); ok {
5251
r.IncRef()
5352
}

rx/mono/utils.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,11 @@ func toBlock(ctx context.Context, m mono.Mono) (payload.Payload, error) {
165165
done := make(chan struct{})
166166
vchan := make(chan payload.Payload, 1)
167167
echan := make(chan error, 1)
168+
// 'blockSubscriber' takes ownership of the above channels (w.r.t. closing them)
168169
b := newBlockSubscriber(done, vchan, echan)
169170
m.SubscribeWith(ctx, b)
170171
<-done
171172

172-
defer close(vchan)
173-
defer close(echan)
174-
175173
select {
176174
case value := <-vchan:
177175
return value, nil

0 commit comments

Comments
 (0)