github.com/pingcap/failpoint@v0.0.0-20240412033321-fd0796e60f86/failpoint.go (about) 1 // Copyright 2019 PingCAP, 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 failpoint 16 17 import ( 18 "context" 19 "sync" 20 ) 21 22 const failpointCtxKey HookKey = "__failpoint_ctx_key__" 23 24 type ( 25 // HookKey represents the type of failpoint hook function key in context 26 HookKey string 27 28 // Value represents value that retrieved from failpoint terms. 29 // It can be used as following types: 30 // 1. val.(int) // GO_FAILPOINTS="failpoint-name=return(1)" 31 // 2. val.(string) // GO_FAILPOINTS="failpoint-name=return(\"1\")" 32 // 3. val.(bool) // GO_FAILPOINTS="failpoint-name=return(true)" 33 Value interface{} 34 35 // Hook is used to filter failpoint, if the hook returns false and the 36 // failpoint will not to be evaluated. 37 Hook func(ctx context.Context, fpname string) bool 38 39 // Failpoint is a point to inject a failure 40 Failpoint struct { 41 mu sync.RWMutex 42 t *terms 43 waitChan chan struct{} 44 } 45 ) 46 47 // Pause will pause until the failpoint is disabled. 48 func (fp *Failpoint) Pause() { 49 <-fp.waitChan 50 } 51 52 // Enable sets a failpoint to a given failpoint description. 53 func (fp *Failpoint) Enable(inTerms string) error { 54 t, err := newTerms(inTerms, fp) 55 if err != nil { 56 return err 57 } 58 fp.mu.Lock() 59 fp.t = t 60 fp.waitChan = make(chan struct{}) 61 fp.mu.Unlock() 62 return nil 63 } 64 65 // EnableWith enables and locks the failpoint, the lock prevents 66 // the failpoint to be evaluated. It invokes the action while holding 67 // the lock. It is useful when enables a panic failpoint 68 // and does some post actions before the failpoint being evaluated. 69 func (fp *Failpoint) EnableWith(inTerms string, action func() error) error { 70 t, err := newTerms(inTerms, fp) 71 if err != nil { 72 return err 73 } 74 fp.mu.Lock() 75 defer fp.mu.Unlock() 76 fp.t = t 77 fp.waitChan = make(chan struct{}) 78 if err := action(); err != nil { 79 return err 80 } 81 return nil 82 } 83 84 // Disable stops a failpoint 85 func (fp *Failpoint) Disable() { 86 select { 87 case <-fp.waitChan: 88 // already disabled 89 return 90 default: 91 close(fp.waitChan) 92 } 93 94 fp.mu.Lock() 95 defer fp.mu.Unlock() 96 fp.t = nil 97 } 98 99 // Eval evaluates a failpoint's value, It will return the evaluated value or 100 // an error if the failpoint is disabled or failed to eval 101 func (fp *Failpoint) Eval() (Value, error) { 102 fp.mu.RLock() 103 defer fp.mu.RUnlock() 104 if fp.t == nil { 105 return nil, ErrDisabled 106 } 107 v, err := fp.t.eval() 108 if err != nil { 109 return nil, err 110 } 111 return v, nil 112 }