Skip to content

Commit 5498e2d

Browse files
committed
bugfix: race conditions in Future methods
Before the patch it may be possible to close several times fut.ready and fut.done channels from the public API calls.
1 parent 4b066ae commit 5498e2d

File tree

3 files changed

+33
-6
lines changed

3 files changed

+33
-6
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
3333

3434
- Build with OpenSSL < 1.1.1 (#194)
3535
- Add `ExecuteAsync` and `ExecuteTyped` to common connector interface (#62)
36+
- Race conditions in methods of `Future` type (#195)
3637

3738
## [1.6.0] - 2022-06-01
3839

future.go

+9-6
Original file line numberDiff line numberDiff line change
@@ -129,38 +129,41 @@ func NewFuture() (fut *Future) {
129129
// AppendPush appends the push response to the future.
130130
// Note: it works only before SetResponse() or SetError()
131131
func (fut *Future) AppendPush(resp *Response) {
132+
fut.mutex.Lock()
133+
defer fut.mutex.Unlock()
134+
132135
if fut.isDone() {
133136
return
134137
}
135138
resp.Code = PushCode
136-
fut.mutex.Lock()
137139
fut.pushes = append(fut.pushes, resp)
138-
fut.mutex.Unlock()
139140

140141
fut.ready <- struct{}{}
141142
}
142143

143144
// SetResponse sets a response for the future and finishes the future.
144145
func (fut *Future) SetResponse(resp *Response) {
146+
fut.mutex.Lock()
147+
defer fut.mutex.Unlock()
148+
145149
if fut.isDone() {
146150
return
147151
}
148-
fut.mutex.Lock()
149152
fut.resp = resp
150-
fut.mutex.Unlock()
151153

152154
close(fut.ready)
153155
close(fut.done)
154156
}
155157

156158
// SetError sets an error for the future and finishes the future.
157159
func (fut *Future) SetError(err error) {
160+
fut.mutex.Lock()
161+
defer fut.mutex.Unlock()
162+
158163
if fut.isDone() {
159164
return
160165
}
161-
fut.mutex.Lock()
162166
fut.err = err
163-
fut.mutex.Unlock()
164167

165168
close(fut.ready)
166169
close(fut.done)

future_test.go

+23
Original file line numberDiff line numberDiff line change
@@ -233,3 +233,26 @@ func TestFutureGetIteratorError(t *testing.T) {
233233
}
234234
}
235235
}
236+
237+
func TestFutureSetStateRaceCondition(t *testing.T) {
238+
err := errors.New("any error")
239+
resp := &Response{}
240+
respAppend := &Response{}
241+
242+
for i := 0; i < 1000; i++ {
243+
fut := NewFuture()
244+
for j := 0; j < 9; j++ {
245+
go func(opt int) {
246+
if opt%3 == 0 {
247+
fut.AppendPush(respAppend)
248+
} else if opt%3 == 1 {
249+
fut.SetError(err)
250+
} else {
251+
fut.SetResponse(resp)
252+
}
253+
}(j)
254+
}
255+
}
256+
// It may be false-positive, but very rarely - it's ok for such very
257+
// simple race conditions tests.
258+
}

0 commit comments

Comments
 (0)