github.com/google/fleetspeak@v0.1.15-0.20240426164851-4f31f62c1aea/fleetspeak/src/common/fscontext/fscontext.go (about) 1 // Copyright 2024 Google LLC 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 fscontext contains helpers for migrating Fleetspeak to context.Context. 16 // 17 // Fleetspeak should better use context.Context for cancelation than chan struct{}s. 18 package fscontext 19 20 import ( 21 "context" 22 "fmt" 23 "sync" 24 ) 25 26 // ErrStopRequested is the cancelation cause to be used when callers 27 // intentionally cancel a context from the outside. 28 var ErrStopRequested = fmt.Errorf("%w: stop requested", context.Canceled) 29 30 // WithDoneChan returns a new context and cancel function. 31 // 32 // The context is automatically canceled with the given cause as soon as done is 33 // closed. 34 // 35 // This is not meant as a long-term solution, but as a migration path for code 36 // which has a "done" channel but which needs to provide a context. 37 // 38 // Callers must always call cancel after the context is done. 39 // 40 // Example usage: 41 // 42 // errDoneClosed := fmt.Errorf("my magic done channel was closed: %w", fscontext.ErrStopRequested) 43 // ctx, cancel := fscontext.WithDoneChan(ctx, errDoneClosed, xyz.done) 44 // defer cancel(nil) 45 func WithDoneChan(ctx context.Context, cause error, done <-chan struct{}) (newCtx context.Context, cancel context.CancelCauseFunc) { 46 myCtx, myCancel := context.WithCancelCause(ctx) 47 var wg sync.WaitGroup 48 wg.Add(1) 49 go func() { 50 defer wg.Done() 51 select { 52 case <-myCtx.Done(): 53 // no need to cancel - it is already canceled 54 case <-done: 55 myCancel(cause) 56 } 57 }() 58 return myCtx, func(cause error) { 59 myCancel(cause) 60 wg.Wait() 61 } 62 }