gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

feature.go (2519B)


      1 package internal
      2 
      3 import (
      4 	"errors"
      5 	"fmt"
      6 	"sync"
      7 )
      8 
      9 // ErrNotSupported indicates that a feature is not supported by the current kernel.
     10 var ErrNotSupported = errors.New("not supported")
     11 
     12 // UnsupportedFeatureError is returned by FeatureTest() functions.
     13 type UnsupportedFeatureError struct {
     14 	// The minimum Linux mainline version required for this feature.
     15 	// Used for the error string, and for sanity checking during testing.
     16 	MinimumVersion Version
     17 
     18 	// The name of the feature that isn't supported.
     19 	Name string
     20 }
     21 
     22 func (ufe *UnsupportedFeatureError) Error() string {
     23 	if ufe.MinimumVersion.Unspecified() {
     24 		return fmt.Sprintf("%s not supported", ufe.Name)
     25 	}
     26 	return fmt.Sprintf("%s not supported (requires >= %s)", ufe.Name, ufe.MinimumVersion)
     27 }
     28 
     29 // Is indicates that UnsupportedFeatureError is ErrNotSupported.
     30 func (ufe *UnsupportedFeatureError) Is(target error) bool {
     31 	return target == ErrNotSupported
     32 }
     33 
     34 type featureTest struct {
     35 	sync.RWMutex
     36 	successful bool
     37 	result     error
     38 }
     39 
     40 // FeatureTestFn is used to determine whether the kernel supports
     41 // a certain feature.
     42 //
     43 // The return values have the following semantics:
     44 //
     45 //   err == ErrNotSupported: the feature is not available
     46 //   err == nil: the feature is available
     47 //   err != nil: the test couldn't be executed
     48 type FeatureTestFn func() error
     49 
     50 // FeatureTest wraps a function so that it is run at most once.
     51 //
     52 // name should identify the tested feature, while version must be in the
     53 // form Major.Minor[.Patch].
     54 //
     55 // Returns an error wrapping ErrNotSupported if the feature is not supported.
     56 func FeatureTest(name, version string, fn FeatureTestFn) func() error {
     57 	ft := new(featureTest)
     58 	return func() error {
     59 		ft.RLock()
     60 		if ft.successful {
     61 			defer ft.RUnlock()
     62 			return ft.result
     63 		}
     64 		ft.RUnlock()
     65 		ft.Lock()
     66 		defer ft.Unlock()
     67 		// check one more time on the off
     68 		// chance that two go routines
     69 		// were able to call into the write
     70 		// lock
     71 		if ft.successful {
     72 			return ft.result
     73 		}
     74 		err := fn()
     75 		switch {
     76 		case errors.Is(err, ErrNotSupported):
     77 			v, err := NewVersion(version)
     78 			if err != nil {
     79 				return err
     80 			}
     81 
     82 			ft.result = &UnsupportedFeatureError{
     83 				MinimumVersion: v,
     84 				Name:           name,
     85 			}
     86 			fallthrough
     87 
     88 		case err == nil:
     89 			ft.successful = true
     90 
     91 		default:
     92 			// We couldn't execute the feature test to a point
     93 			// where it could make a determination.
     94 			// Don't cache the result, just return it.
     95 			return fmt.Errorf("detect support for %s: %w", name, err)
     96 		}
     97 
     98 		return ft.result
     99 	}
    100 }