github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/cluster/progress_notifier.go (about) 1 // Copyright 2023 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cluster 16 17 import ( 18 "context" 19 ) 20 21 // ProgressNotifier is a way for clients to be notified of successful progress 22 // which a monotonic agent makes after they register to receive notification by 23 // taking a callback with |Wait|. 24 // 25 // As a monotonic agent implementation, you should call |BeginAttempt()| 26 // anytime you begin attempting to do work. If that work succeeds, you can call 27 // |RecordSuccess|. If the work fails, you must call |RecordFailure|. 28 // |RecordSuccess| makes a later call to |RecordFailure| with the same 29 // |*Attempt| a no-op, so that the call to |RecordFailure| can be safely placed 30 // in a defer block. 31 // 32 // As a client of the agent, you can call |Wait|, which will return a function 33 // you can call to block until either progress was made since the call to 34 // |Wait| or the provided |context.Context| is |Done|. If progress is made, the 35 // function returned from |Wait| returns |nil|. If the context was canceled, it 36 // returns |context.Cause(ctx)|. 37 // 38 // All accesses to ProgressNotifier should be externally synchronized except 39 // for calling into the functions returned by |Wait|. 40 type ProgressNotifier struct { 41 chs []chan struct{} 42 } 43 44 type Attempt struct { 45 chs []chan struct{} 46 } 47 48 func (p *ProgressNotifier) HasWaiters() bool { 49 return len(p.chs) > 0 50 } 51 52 func (p *ProgressNotifier) BeginAttempt() *Attempt { 53 chs := p.chs 54 p.chs = nil 55 return &Attempt{chs: chs} 56 } 57 58 func (*ProgressNotifier) RecordSuccess(a *Attempt) { 59 if a.chs != nil { 60 for i := range a.chs { 61 close(a.chs[i]) 62 } 63 a.chs = nil 64 } 65 } 66 67 func (p *ProgressNotifier) RecordFailure(a *Attempt) { 68 if a.chs != nil { 69 p.chs = append(p.chs, a.chs...) 70 } 71 } 72 73 func (p *ProgressNotifier) Wait() func(context.Context) error { 74 if len(p.chs) == 0 { 75 p.chs = append(p.chs, make(chan struct{})) 76 } 77 ch := p.chs[0] 78 return func(ctx context.Context) error { 79 select { 80 case <-ctx.Done(): 81 return context.Cause(ctx) 82 case <-ch: 83 return nil 84 } 85 } 86 }