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  }