github.com/GuanceCloud/cliutils@v1.1.21/diskcache/lock.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the MIT License.
     3  // This product includes software developed at Guance Cloud (https://www.guance.com/).
     4  // Copyright 2021-present Guance, Inc.
     5  
     6  package diskcache
     7  
     8  import (
     9  	"fmt"
    10  	"os"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strconv"
    14  	"sync"
    15  	"syscall"
    16  )
    17  
    18  type flock struct {
    19  	file string
    20  	mtx  *sync.Mutex
    21  }
    22  
    23  func newFlock(path string) *flock {
    24  	return &flock{
    25  		file: filepath.Clean(filepath.Join(path, ".lock")),
    26  		mtx:  &sync.Mutex{},
    27  	}
    28  }
    29  
    30  func (l *flock) lock() error {
    31  	l.mtx.Lock()
    32  	defer l.mtx.Unlock()
    33  
    34  	curPid := os.Getpid()
    35  
    36  	if _, err := os.Stat(l.file); err != nil {
    37  		goto write // file not exist
    38  	} else {
    39  		x, err := os.ReadFile(l.file)
    40  		if err != nil {
    41  			return err
    42  		}
    43  
    44  		if len(x) == 0 {
    45  			goto write
    46  		}
    47  
    48  		pidInFile, err := strconv.Atoi(string(x))
    49  		if err != nil {
    50  			return err
    51  		} else {
    52  			switch pidInFile {
    53  			case -1: // unlocked
    54  				goto write
    55  			case curPid:
    56  				return fmt.Errorf("lock failed(locked by pid %d)", curPid)
    57  			default: // other pid, may terminated
    58  				if pidAlive(pidInFile) {
    59  					return fmt.Errorf("lock failed(locked by alive %d)", pidInFile)
    60  				}
    61  			}
    62  		}
    63  	}
    64  
    65  write:
    66  	return os.WriteFile(l.file, []byte(fmt.Sprintf("%d", curPid)), 0o600)
    67  }
    68  
    69  func (l *flock) unlock() error {
    70  	l.mtx.Lock()
    71  	defer l.mtx.Unlock()
    72  
    73  	return os.WriteFile(l.file, []byte(fmt.Sprintf("%d", -1)), 0o600)
    74  }
    75  
    76  func pidAlive(pid int) bool {
    77  	p, err := os.FindProcess(pid)
    78  	if err != nil {
    79  		return false
    80  	}
    81  
    82  	// Signal not available on windows.
    83  	if runtime.GOOS == "windows" {
    84  		return true
    85  	}
    86  
    87  	if err := p.Signal(syscall.Signal(0)); err != nil {
    88  		switch err.Error() {
    89  		case "operation not permitted":
    90  			return true
    91  		default:
    92  			return false
    93  		}
    94  	} else {
    95  		return true
    96  	}
    97  }