Skip to content

Commit e242faa

Browse files
committed
Go: remove BlockUntilReady() and Cancel() from Future
Simplification for the usage of futures: * if Get() is called on a future, all further calls will return a cached value and the future will be automatically closed * if Get() is never called then future will be closed when user calls Close() Cancel() is no more necessary: user can call Close() Similarly, BlockUntilReady() is removed because a call to Get() (ignoring return value) is equivalent.
1 parent 1a43266 commit e242faa

File tree

1 file changed

+58
-91
lines changed

1 file changed

+58
-91
lines changed

bindings/go/src/fdb/futures.go

Lines changed: 58 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -47,35 +47,22 @@ import (
4747
// A Future represents a value (or error) to be available at some later
4848
// time. Asynchronous FDB API functions return one of the types that implement
4949
// the Future interface. All Future types additionally implement Get and MustGet
50-
// methods with different return types. Calling BlockUntilReady, Get or MustGet
51-
// will block the calling goroutine until the Future is ready.
50+
// methods with different return types. Calling Get or MustGet will block
51+
// the calling goroutine until the Future is ready.
5252
type Future interface {
53-
// BlockUntilReady blocks the calling goroutine until the future is ready. A
54-
// future becomes ready either when it receives a value of its enclosed type
55-
// (if any) or is set to an error state.
56-
BlockUntilReady(context.Context) error
57-
5853
// IsReady returns true if the future is ready, and false otherwise, without
5954
// blocking. A future is ready either when has received a value of its
6055
// enclosed type (if any) or has been set to an error state.
6156
IsReady() bool
6257

63-
// Cancel cancels a future and its associated asynchronous operation. If
64-
// called before the future becomes ready, attempts to access the future
65-
// will return an error. Cancel has no effect if the future is already
66-
// ready.
67-
//
68-
// Note that even if a future is not ready, the associated asynchronous
69-
// operation may already have completed and be unable to be cancelled.
70-
Cancel()
71-
7258
// Close will release resources associated with this future.
73-
// It must always be called, and called exactly once.
59+
// It must always be called at least once.
7460
Close()
7561
}
7662

7763
type future struct {
78-
ptr *C.FDBFuture
64+
ptr *C.FDBFuture
65+
closer sync.Once
7966
}
8067

8168
// newFuture returns a future which must be explicitly destroyed with a call to destroy().
@@ -88,42 +75,48 @@ func newFuture(ptr *C.FDBFuture) *future {
8875

8976
type readySignal chan (struct{})
9077

91-
// BlockUntilReady is a Go re-implementation of fdb_future_block_until_ready.
78+
// blockUntilReady blocks the calling goroutine until the given Future is ready.
79+
// This is a Go re-implementation of fdb_future_block_until_ready but it differs because
80+
// it will return the Future's error if any was set.
9281
// Note: This function guarantees the callback will be executed **at most once**.
93-
func (f *future) BlockUntilReady(ctx context.Context) error {
94-
if C.fdb_future_is_ready(f.ptr) != 0 {
95-
return nil
82+
func (f *future) blockUntilReady(ctx context.Context) error {
83+
if C.fdb_future_is_ready(f.ptr) == 0 {
84+
// The channel here is used as a signal that the callback is complete.
85+
// The callback is responsible for closing it, and this function returns
86+
// only after that has happened.
87+
//
88+
// See also: https://groups.google.com/forum/#!topic/golang-nuts/SPjQEcsdORA
89+
rs := make(readySignal)
90+
C.c_set_callback(unsafe.Pointer(f.ptr), unsafe.Pointer(&rs))
91+
92+
select {
93+
case <-rs:
94+
// future is ready (either with value or error)
95+
break
96+
case <-ctx.Done():
97+
// Note: future cancellation does not happen here but rather on the calling Get()
98+
// when the future is closed
99+
100+
return ctx.Err()
101+
}
96102
}
97103

98-
// The channel here is used as a signal that the callback is complete.
99-
// The callback is responsible for closing it, and this function returns
100-
// only after that has happened.
101-
//
102-
// See also: https://groups.google.com/forum/#!topic/golang-nuts/SPjQEcsdORA
103-
rs := make(readySignal)
104-
C.c_set_callback(unsafe.Pointer(f.ptr), unsafe.Pointer(&rs))
105-
106-
select {
107-
case <-rs:
108-
return nil
109-
case <-ctx.Done():
110-
C.fdb_future_cancel(f.ptr)
111-
112-
return ctx.Err()
104+
if err := C.fdb_future_get_error(f.ptr); err != 0 {
105+
return Error{int(err)}
113106
}
107+
108+
return nil
114109
}
115110

116111
func (f *future) IsReady() bool {
117112
return C.fdb_future_is_ready(f.ptr) != 0
118113
}
119114

120-
func (f *future) Cancel() {
121-
C.fdb_future_cancel(f.ptr)
122-
}
123-
124115
// Close must be explicitly called for each future to avoid a memory leak.
125116
func (f *future) Close() {
126-
C.fdb_future_destroy(f.ptr)
117+
f.closer.Do(func() {
118+
C.fdb_future_destroy(f.ptr)
119+
})
127120
}
128121

129122
// FutureByteSlice represents the asynchronous result of a function that returns
@@ -147,22 +140,20 @@ type FutureByteSlice interface {
147140

148141
type futureByteSlice struct {
149142
*future
150-
v []byte
151-
e error
152-
get sync.Once
143+
v []byte
144+
e error
145+
getter sync.Once
153146
}
154147

155148
func (f *futureByteSlice) Get(ctx context.Context) ([]byte, error) {
156-
f.get.Do(func() {
157-
err := f.BlockUntilReady(ctx)
149+
f.getter.Do(func() {
150+
defer f.Close()
151+
152+
err := f.blockUntilReady(ctx)
158153
if err != nil {
159154
f.e = err
160155
return
161156
}
162-
if err := C.fdb_future_get_error(f.ptr); err != 0 {
163-
f.e = Error{int(err)}
164-
return
165-
}
166157

167158
var present C.fdb_bool_t
168159
var value *C.uint8_t
@@ -175,8 +166,6 @@ func (f *futureByteSlice) Get(ctx context.Context) ([]byte, error) {
175166
if present != 0 {
176167
f.v = C.GoBytes(unsafe.Pointer(value), length)
177168
}
178-
179-
C.fdb_future_release_memory(f.ptr)
180169
})
181170

182171
return f.v, f.e
@@ -216,15 +205,13 @@ type futureKey struct {
216205

217206
func (f *futureKey) Get(ctx context.Context) (Key, error) {
218207
f.get.Do(func() {
219-
err := f.BlockUntilReady(ctx)
208+
defer f.Close()
209+
210+
err := f.blockUntilReady(ctx)
220211
if err != nil {
221212
f.e = err
222213
return
223214
}
224-
if err := C.fdb_future_get_error(f.ptr); err != 0 {
225-
f.e = Error{int(err)}
226-
return
227-
}
228215

229216
var value *C.uint8_t
230217
var length C.int
@@ -234,7 +221,6 @@ func (f *futureKey) Get(ctx context.Context) (Key, error) {
234221
}
235222

236223
f.k = C.GoBytes(unsafe.Pointer(value), length)
237-
C.fdb_future_release_memory(f.ptr)
238224
})
239225

240226
return f.k, f.e
@@ -273,17 +259,13 @@ type futureNil struct {
273259

274260
func (f *futureNil) Get(ctx context.Context) error {
275261
f.get.Do(func() {
276-
err := f.BlockUntilReady(ctx)
262+
defer f.Close()
263+
264+
err := f.blockUntilReady(ctx)
277265
if err != nil {
278266
f.e = err
279267
return
280268
}
281-
if err := C.fdb_future_get_error(f.ptr); err != 0 {
282-
f.e = Error{int(err)}
283-
return
284-
}
285-
286-
C.fdb_future_release_memory(f.ptr)
287269
})
288270

289271
return nil
@@ -318,15 +300,11 @@ func stringRefToSlice(ptr unsafe.Pointer) []byte {
318300

319301
func (f *futureKeyValueArray) Get(ctx context.Context) ([]KeyValue, bool, error) {
320302
f.o.Do(func() {
321-
err := f.BlockUntilReady(ctx)
303+
err := f.blockUntilReady(ctx)
322304
if err != nil {
323305
f.e = err
324306
return
325307
}
326-
if err := C.fdb_future_get_error(f.ptr); err != 0 {
327-
f.e = Error{int(err)}
328-
return
329-
}
330308

331309
var kvs *C.FDBKeyValue
332310
var count C.int
@@ -379,15 +357,13 @@ type futureKeyArray struct {
379357

380358
func (f *futureKeyArray) Get(ctx context.Context) ([]Key, error) {
381359
f.get.Do(func() {
382-
err := f.BlockUntilReady(ctx)
360+
defer f.Close()
361+
362+
err := f.blockUntilReady(ctx)
383363
if err != nil {
384364
f.e = err
385365
return
386366
}
387-
if err := C.fdb_future_get_error(f.ptr); err != 0 {
388-
f.e = Error{int(err)}
389-
return
390-
}
391367

392368
var ks *C.FDBKey
393369
var count C.int
@@ -403,8 +379,6 @@ func (f *futureKeyArray) Get(ctx context.Context) ([]Key, error) {
403379

404380
f.v[i] = stringRefToSlice(kptr)
405381
}
406-
407-
C.fdb_future_release_memory(f.ptr)
408382
})
409383

410384
return f.v, f.e
@@ -444,15 +418,13 @@ type futureInt64 struct {
444418

445419
func (f *futureInt64) Get(ctx context.Context) (int64, error) {
446420
f.get.Do(func() {
447-
err := f.BlockUntilReady(ctx)
421+
defer f.Close()
422+
423+
err := f.blockUntilReady(ctx)
448424
if err != nil {
449425
f.e = err
450426
return
451427
}
452-
if err := C.fdb_future_get_error(f.ptr); err != 0 {
453-
f.e = Error{int(err)}
454-
return
455-
}
456428

457429
var value C.int64_t
458430
if err := C.fdb_future_get_int64(f.ptr, &value); err != 0 {
@@ -461,7 +433,6 @@ func (f *futureInt64) Get(ctx context.Context) (int64, error) {
461433
}
462434

463435
f.v = int64(value)
464-
C.fdb_future_release_memory(f.ptr)
465436
})
466437

467438
return f.v, f.e
@@ -502,15 +473,13 @@ type futureStringSlice struct {
502473

503474
func (f *futureStringSlice) Get(ctx context.Context) ([]string, error) {
504475
f.get.Do(func() {
505-
err := f.BlockUntilReady(ctx)
476+
defer f.Close()
477+
478+
err := f.blockUntilReady(ctx)
506479
if err != nil {
507480
f.e = err
508481
return
509482
}
510-
if err := C.fdb_future_get_error(f.ptr); err != 0 {
511-
f.e = Error{int(err)}
512-
return
513-
}
514483

515484
var strings **C.char
516485
var count C.int
@@ -524,8 +493,6 @@ func (f *futureStringSlice) Get(ctx context.Context) ([]string, error) {
524493
for i := 0; i < int(count); i++ {
525494
f.v[i] = C.GoString((*C.char)(*(**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(strings)) + uintptr(i*8)))))
526495
}
527-
528-
C.fdb_future_release_memory(f.ptr)
529496
})
530497

531498
return f.v, f.e

0 commit comments

Comments
 (0)