Skip to content

Commit f3afc06

Browse files
committed
fix: race condition
1 parent 5995a5b commit f3afc06

File tree

3 files changed

+47
-50
lines changed

3 files changed

+47
-50
lines changed

connection_impl.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -503,17 +503,10 @@ func (c *connection) flush() error {
503503
if c.outputBuffer.IsEmpty() {
504504
return nil
505505
}
506-
if c.operator.getMode() == ophup {
507-
// triggered read throttled, so here shouldn't trigger read event again
508-
err = c.operator.Control(PollHup2W)
509-
} else {
510-
err = c.operator.Control(PollR2RW)
511-
}
512-
c.operator.done()
513-
if err != nil {
514-
return Exception(err, "when flush")
515-
}
516506

507+
// no need to check if resume write successfully
508+
// if resume failed, the connection will be triggered triggerWrite(err), and waitFlush will return err
509+
c.resumeWrite()
517510
return c.waitFlush()
518511
}
519512

@@ -546,8 +539,8 @@ func (c *connection) waitFlush() (err error) {
546539
default:
547540
}
548541
// if timeout, remove write event from poller
549-
// we cannot flush it again, since we don't if the poller is still process outputBuffer
550-
c.operator.Control(PollRW2R)
542+
// we cannot flush it again, since we don't know if the poller is still processing outputBuffer
543+
c.pauseWrite()
551544
return Exception(ErrWriteTimeout, c.remoteAddr.String())
552545
}
553546
}

connection_reactor.go

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ func (c *connection) closeBuffer() {
8080

8181
// inputs implements FDOperator.
8282
func (c *connection) inputs(vs [][]byte) (rs [][]byte) {
83+
// trigger throttle
84+
if c.readBufferThreshold > 0 && int64(c.inputBuffer.Len()) >= c.readBufferThreshold {
85+
c.pauseRead()
86+
return
87+
}
88+
8389
vs[0] = c.inputBuffer.book(c.bookSize, c.maxSize)
8490
return vs[:1]
8591
}
@@ -123,6 +129,7 @@ func (c *connection) inputAck(n int) (err error) {
123129
func (c *connection) outputs(vs [][]byte) (rs [][]byte, supportZeroCopy bool) {
124130
if c.outputBuffer.IsEmpty() {
125131
c.pauseWrite()
132+
c.triggerWrite(nil)
126133
return rs, c.supportZeroCopy
127134
}
128135
rs = c.outputBuffer.GetBytes(vs)
@@ -137,50 +144,47 @@ func (c *connection) outputAck(n int) (err error) {
137144
}
138145
if c.outputBuffer.IsEmpty() {
139146
c.pauseWrite()
147+
c.triggerWrite(nil)
140148
}
141149
return nil
142150
}
143151

152+
/* The race description of operator event monitoring
153+
- Pause operation will remove old event monitor of operator
154+
- Resume operation will add new event monitor of operator
155+
- Only poller could use Pause to remove event monitor, and poller already hold the op.do() locker
156+
- Only user could use Resume, and user's operation maybe compete with poller's operation
157+
- If competition happen, because of all resume operation will monitor all events, it's safe to do that with a race condition.
158+
* If resume first and pause latter, poller will monitor the accurate events it needs.
159+
* If pause first and resume latter, poller will monitor the duplicate events which will be removed after next poller triggered.
160+
And poller will ensure to remove the duplicate events.
161+
- If there is no readBufferThreshold option, the code path will be more simple and efficient.
162+
*/
163+
144164
// pauseWrite removed the monitoring of write events.
145165
// pauseWrite used in poller
146166
func (c *connection) pauseWrite() {
147-
switch c.operator.getMode() {
148-
case opreadwrite:
149-
c.operator.Control(PollRW2R)
150-
case opwrite:
151-
c.operator.Control(PollW2Hup)
152-
}
153-
c.triggerWrite(nil)
167+
c.operator.Control(PollRW2R)
168+
}
169+
170+
// resumeWrite add the monitoring of write events.
171+
// resumeWrite used by users
172+
func (c *connection) resumeWrite() {
173+
c.operator.Control(PollR2RW)
154174
}
155175

156176
// pauseRead removed the monitoring of read events.
157177
// pauseRead used in poller
158178
func (c *connection) pauseRead() {
159-
// Note that the poller ensure that every fd should read all left data in socket buffer before detach it.
160-
// So the operator mode should never be ophup.
161-
var changeTo PollEvent
162-
switch c.operator.getMode() {
163-
case opread:
164-
changeTo = PollR2Hup
165-
case opreadwrite:
166-
changeTo = PollRW2W
167-
}
168-
if changeTo > 0 && atomic.CompareAndSwapInt32(&c.operator.throttled, 0, 1) {
169-
c.operator.Control(changeTo)
179+
if atomic.CompareAndSwapInt32(&c.operator.throttled, 0, 1) {
180+
c.operator.Control(PollRW2W)
170181
}
171182
}
172183

173184
// resumeRead add the monitoring of read events.
174185
// resumeRead used by users
175186
func (c *connection) resumeRead() {
176-
var changeTo PollEvent
177-
switch c.operator.getMode() {
178-
case ophup:
179-
changeTo = PollHup2R
180-
case opwrite:
181-
changeTo = PollW2RW
182-
}
183-
if changeTo > 0 && atomic.CompareAndSwapInt32(&c.operator.throttled, 1, 0) {
184-
c.operator.Control(changeTo)
187+
if atomic.CompareAndSwapInt32(&c.operator.throttled, 1, 0) {
188+
c.operator.Control(PollW2RW)
185189
}
186190
}

poll.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,31 +48,31 @@ type PollEvent int
4848
const (
4949
// PollReadable is used to monitor whether the FDOperator registered by
5050
// listener and connection is readable or closed.
51-
PollReadable PollEvent = 0x1
51+
PollReadable PollEvent = iota + 1
5252

5353
// PollWritable is used to monitor whether the FDOperator created by the dialer is writable or closed.
5454
// ET mode must be used (still need to poll hup after being writable)
55-
PollWritable PollEvent = 0x2
55+
PollWritable
5656

5757
// PollDetach is used to remove the FDOperator from poll.
58-
PollDetach PollEvent = 0x3
58+
PollDetach
5959

6060
// PollR2RW is used to monitor writable for FDOperator,
6161
// which is only called when the socket write buffer is full.
62-
PollR2RW PollEvent = 0x4
62+
PollR2RW
6363
// PollRW2R is used to remove the writable monitor of FDOperator, generally used with PollR2RW.
64-
PollRW2R PollEvent = 0x5
64+
PollRW2R
6565

6666
// PollRW2W is used to remove the readable monitor of FDOperator.
67-
PollRW2W PollEvent = 0x6
67+
PollRW2W
6868
// PollW2RW is used to add the readable monitor of FDOperator, generally used with PollRW2W.
69-
PollW2RW PollEvent = 0x7
70-
PollW2Hup PollEvent = 0x8
69+
PollW2RW
70+
PollW2Hup
7171

7272
// PollR2Hup is used to remove the readable monitor of FDOperator.
73-
PollR2Hup PollEvent = 0x9
73+
PollR2Hup
7474
// PollHup2R is used to add the readable monitor of FDOperator, generally used with PollR2Hup.
75-
PollHup2R PollEvent = 0xA
75+
PollHup2R
7676
// PollHup2W is used to add the writeable monitor of FDOperator.
77-
PollHup2W PollEvent = 0xB
77+
PollHup2W
7878
)

0 commit comments

Comments
 (0)