func (l Lockfile) TryLock() error {
	name := string(l)

	// This has been checked by New already. If we trigger here,
	// the caller didn't use New and re-implemented it's functionality badly.
	// So panic, that he might find this easily during testing.
	if !filepath.IsAbs(name) {
		panic(ErrNeedAbsPath)
	}

	tmplock, err := ioutil.TempFile(filepath.Dir(name), filepath.Base(name)+".")
	if err != nil {
		return err
	}

	cleanup := func() {
		_ = tmplock.Close()
		_ = os.Remove(tmplock.Name())
	}
	defer cleanup()

	if err := writePidLine(tmplock, os.Getpid()); err != nil {
		return err
	}

	// EEXIST and similiar error codes, caught by os.IsExist, are intentionally ignored,
	// as it means that someone was faster creating this link
	// and ignoring this kind of error is part of the algorithm.
	// The we will probably fail the pid owner check later, if this process is still alive.
	// We cannot ignore ALL errors, since failure to support hard links, disk full
	// as well as many other errors can happen to a filesystem operation
	// and we really want to abort on those.
	if err := os.Link(tmplock.Name(), name); err != nil {
		if !os.IsExist(err) {
			return err
		}
	}

	fiTmp, err := os.Lstat(tmplock.Name())
	if err != nil {
		return err
	}
	fiLock, err := os.Lstat(name)
	if err != nil {
		// tell user that a retry would be a good idea
		if os.IsNotExist(err) {
			return ErrNotExist
		}
		return err
	}

	// Success
	if os.SameFile(fiTmp, fiLock) {
		return nil
	}

	proc, err := l.GetOwner()
	switch err {
	default:
		// Other errors -> defensively fail and let caller handle this
		return err
	case nil:
		if proc.Pid != os.Getpid() {
			return ErrBusy
		}
	case ErrDeadOwner, ErrInvalidPid:
		// cases we can fix below
	}

	// clean stale/invalid lockfile
	err = os.Remove(name)
	if err != nil {
		// If it doesn't exist, then it doesn't matter who removed it.
		if !os.IsNotExist(err) {
			return err
		}
	}

	// now that the stale lockfile is gone, let's recurse
	return l.TryLock()
}
func (l Lockfile) TryLock() error {
	name := string(l)

	
	
	
	if !filepath.IsAbs(name) {
		panic(ErrNeedAbsPath)
	}

	tmplock, err := ioutil.TempFile(filepath.Dir(name), filepath.Base(name)+".")
	if err != nil {
		return err
	}

	cleanup := func() {
		_ = tmplock.Close()
		_ = os.Remove(tmplock.Name())
	}
	defer cleanup()

	if err := writePidLine(tmplock, os.Getpid()); err != nil {
		return err
	}

	
	
	
	
	
	
	
	if err := os.Link(tmplock.Name(), name); err != nil {
		if !os.IsExist(err) {
			return err
		}
	}

	fiTmp, err := os.Lstat(tmplock.Name())
	if err != nil {
		return err
	}
	fiLock, err := os.Lstat(name)
	if err != nil {
		
		if os.IsNotExist(err) {
			return ErrNotExist
		}
		return err
	}

	
	if os.SameFile(fiTmp, fiLock) {
		return nil
	}

	proc, err := l.GetOwner()
	switch err {
	default:
		
		return err
	case nil:
		if proc.Pid != os.Getpid() {
			return ErrBusy
		}
	case ErrDeadOwner, ErrInvalidPid:
		
	}

	
	err = os.Remove(name)
	if err != nil {
		
		if !os.IsNotExist(err) {
			return err
		}
	}

	
	return l.TryLock()
}
func ( l Lockfile ) TryLock ( ) error { name := string ( l ) // This has been checked by New already. If we trigger here, // the caller didn't use New and re-implemented it's functionality badly. // So panic, that he might find this easily during testing. if ! filepath . IsAbs ( name ) { panic ( ErrNeedAbsPath ) } tmplock , err := ioutil . TempFile ( filepath . Dir ( name ) , filepath . Base ( name ) + " " ) if err != nil { return err } cleanup := func ( ) { _ = tmplock . Close ( ) _ = os . Remove ( tmplock . Name ( ) ) } defer cleanup ( ) if err := writePidLine ( tmplock , os . Getpid ( ) ) ; err != nil { return err } // EEXIST and similiar error codes, caught by os.IsExist, are intentionally ignored, // as it means that someone was faster creating this link // and ignoring this kind of error is part of the algorithm. // The we will probably fail the pid owner check later, if this process is still alive. // We cannot ignore ALL errors, since failure to support hard links, disk full // as well as many other errors can happen to a filesystem operation // and we really want to abort on those. if err := os . Link ( tmplock . Name ( ) , name ) ; err != nil { if ! os . IsExist ( err ) { return err } } fiTmp , err := os . Lstat ( tmplock . Name ( ) ) if err != nil { return err } fiLock , err := os . Lstat ( name ) if err != nil { // tell user that a retry would be a good idea if os . IsNotExist ( err ) { return ErrNotExist } return err } // Success if os . SameFile ( fiTmp , fiLock ) { return nil } proc , err := l . GetOwner ( ) switch err { default : // Other errors -> defensively fail and let caller handle this return err case nil : if proc . Pid != os . Getpid ( ) { return ErrBusy } case ErrDeadOwner , ErrInvalidPid : // cases we can fix below } // clean stale/invalid lockfile err = os . Remove ( name ) if err != nil { // If it doesn't exist, then it doesn't matter who removed it. if ! os . IsNotExist ( err ) { return err } } // now that the stale lockfile is gone, let's recurse return l . TryLock ( ) }
// TryLock tries to own the lock.
// It Returns nil, if successful and and error describing the reason, it didn't work out.
// Please note, that existing lockfiles containing pids of dead processes
// and lockfiles containing no pid at all are simply deleted.
TryLock tries to own the lock . It Returns nil if successful and and error describing the reason it didn t work out . Please note that existing lockfiles containing pids of dead processes and lockfiles containing no pid at all are simply deleted .