github.com/altipla-consulting/ravendb-go-client@v0.1.3/completable_future.go (about) 1 package ravendb 2 3 import ( 4 "sync" 5 "time" 6 ) 7 8 // TODO: write tests 9 10 /* 11 Note: 12 13 future = completableFuture.runAsync(() -> foo()) 14 15 (or supplyAsync) is replaced with this pattern: 16 17 future = newCompletableFuture() 18 go func() { 19 res, err := foo() 20 if err != nil { 21 future.completeWithError(err) 22 } else { 23 future.complete(res) 24 } 25 }() 26 */ 27 28 // completableFuture helps porting Java code. Implements only functions needed 29 // by ravendb. 30 type completableFuture struct { 31 mu sync.Mutex 32 33 completed bool 34 // used to wait for Future to finish 35 signalCompletion chan bool 36 37 // result generated by the Future, only valid if completed 38 result interface{} 39 err error 40 } 41 42 func newCompletableFuture() *completableFuture { 43 return &completableFuture{ 44 // channel with capacity 1 so that complete() can finish the goroutine 45 // without waiting for someone to call Get() 46 signalCompletion: make(chan bool, 1), 47 } 48 } 49 50 func newCompletableFutureAlreadyCompleted(result interface{}) *completableFuture { 51 res := newCompletableFuture() 52 res.complete(result) 53 return res 54 } 55 56 func (f *completableFuture) getState() (bool, interface{}, error) { 57 f.mu.Lock() 58 defer f.mu.Unlock() 59 return f.completed, f.result, f.err 60 } 61 62 // must be called with f.mu locked 63 func (f *completableFuture) markCompleted(result interface{}, err error) { 64 f.completed = true 65 f.result = result 66 f.err = err 67 f.signalCompletion <- true 68 } 69 70 // complete marks the future as completed with a given result (which can be nil) 71 func (f *completableFuture) complete(result interface{}) { 72 f.mu.Lock() 73 if !f.completed { 74 f.markCompleted(result, nil) 75 } 76 f.mu.Unlock() 77 } 78 79 // completeWithError marks the future as completed with error 80 func (f *completableFuture) completeWithError(err error) { 81 f.mu.Lock() 82 if !f.completed { 83 f.markCompleted(nil, err) 84 } 85 f.mu.Unlock() 86 } 87 88 // cancel cancels a future 89 func (f *completableFuture) cancel(mayInterruptIfRunning bool) { 90 // mayInterruptIfRunning is ignored, apparently same happens in Java 91 // https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html 92 f.completeWithError(&CancellationError{}) 93 } 94 95 // IsDone returns true if future has been completed, either with a result or error 96 func (f *completableFuture) IsDone() bool { 97 done, _, _ := f.getState() 98 return done 99 } 100 101 // IsCompletedExceptionally returns true if future has been completed due to an error 102 func (f *completableFuture) IsCompletedExceptionally() bool { 103 _, _, err := f.getState() 104 return err != nil // implies f.done 105 } 106 107 // isCancelled returns true if future was cancelled by calling cancel() 108 func (f *completableFuture) isCancelled() bool { 109 var isCancelled bool 110 _, err, _ := f.getState() 111 if err != nil { 112 _, isCancelled = err.(*CancellationError) 113 } 114 return isCancelled 115 } 116 117 // Get waits for completion and returns resulting value or error 118 // If already completed, returns immediately. 119 // TODO: hide it, unused in one test 120 func (f *completableFuture) Get() (interface{}, error) { 121 return f.GetWithTimeout(0) 122 } 123 124 func (f *completableFuture) GetWithTimeout(dur time.Duration) (interface{}, error) { 125 done, res, err := f.getState() 126 if done { 127 return res, err 128 } 129 130 if dur == 0 { 131 // wait for the Future to complete 132 <-f.signalCompletion 133 } else { 134 // wait for the Future to complete or timeout to expire 135 select { 136 case <-f.signalCompletion: 137 // completed, will return the result 138 case <-time.After(dur): 139 // timed out 140 return nil, NewTimeoutError("GetWithTimeout() timed out after %s", dur) 141 } 142 } 143 144 _, res, err = f.getState() 145 return res, err 146 }