Skip to content

Commit a746d47

Browse files
committed
fix: race problem in timeout mono (#42)
* fix: race problem in timeout mono * add ut * fix ut * set codecov (cherry picked from commit a7bd3e9)
1 parent fe24342 commit a746d47

File tree

2 files changed

+36
-26
lines changed

2 files changed

+36
-26
lines changed

codecov.yml

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
coverage:
2+
range: 70..100
3+
round: down
4+
precision: 2

mono/mono_timeout.go

+32-26
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package mono
22

33
import (
44
"context"
5+
"math"
6+
"sync/atomic"
57
"time"
68

79
"github.com/jjeffcaii/reactor-go"
@@ -13,6 +15,21 @@ type monoTimeout struct {
1315
timeout time.Duration
1416
}
1517

18+
func newMonoTimeout(source reactor.RawPublisher, timeout time.Duration) *monoTimeout {
19+
return &monoTimeout{
20+
source: source,
21+
timeout: timeout,
22+
}
23+
}
24+
25+
func (m *monoTimeout) SubscribeWith(ctx context.Context, s reactor.Subscriber) {
26+
m.source.SubscribeWith(ctx, &timeoutSubscriber{
27+
actual: s,
28+
timeout: m.timeout,
29+
done: make(chan struct{}),
30+
})
31+
}
32+
1633
func (m *monoTimeout) Parent() reactor.RawPublisher {
1734
return m.source
1835
}
@@ -21,33 +38,37 @@ type timeoutSubscriber struct {
2138
actual reactor.Subscriber
2239
timeout time.Duration
2340
done chan struct{}
41+
closed int32
2442
}
2543

2644
func (t *timeoutSubscriber) OnComplete() {
27-
select {
28-
case <-t.done:
29-
default:
45+
if atomic.CompareAndSwapInt32(&t.closed, 0, math.MaxInt32) || atomic.CompareAndSwapInt32(&t.closed, 1, math.MaxInt32) {
3046
close(t.done)
3147
t.actual.OnComplete()
3248
}
3349
}
3450

3551
func (t *timeoutSubscriber) OnError(err error) {
36-
select {
37-
case <-t.done:
38-
hooks.Global().OnErrorDrop(err)
39-
default:
52+
if atomic.CompareAndSwapInt32(&t.closed, 0, -1) {
4053
close(t.done)
4154
t.actual.OnError(err)
55+
return
56+
}
57+
58+
// item is emitted before error reach, should be processed as completed.
59+
if atomic.CompareAndSwapInt32(&t.closed, 1, -1) {
60+
close(t.done)
61+
t.actual.OnComplete()
4262
}
63+
64+
hooks.Global().OnErrorDrop(err)
4365
}
4466

4567
func (t *timeoutSubscriber) OnNext(any reactor.Any) {
46-
select {
47-
case <-t.done:
48-
hooks.Global().OnNextDrop(any)
49-
default:
68+
if atomic.CompareAndSwapInt32(&t.closed, 0, 1) {
5069
t.actual.OnNext(any)
70+
} else {
71+
hooks.Global().OnNextDrop(any)
5172
}
5273
}
5374

@@ -63,18 +84,3 @@ func (t *timeoutSubscriber) OnSubscribe(ctx context.Context, subscription reacto
6384
}()
6485
t.actual.OnSubscribe(ctx, subscription)
6586
}
66-
67-
func (m *monoTimeout) SubscribeWith(ctx context.Context, s reactor.Subscriber) {
68-
m.source.SubscribeWith(ctx, &timeoutSubscriber{
69-
actual: s,
70-
timeout: m.timeout,
71-
done: make(chan struct{}),
72-
})
73-
}
74-
75-
func newMonoTimeout(source reactor.RawPublisher, timeout time.Duration) *monoTimeout {
76-
return &monoTimeout{
77-
source: source,
78-
timeout: timeout,
79-
}
80-
}

0 commit comments

Comments
 (0)