Ensure only a single instance of after-lock runs at a time
This commit is contained in:
		
							
								
								
									
										49
									
								
								lockfile/lockfile.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								lockfile/lockfile.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
package lockfile
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"os"
 | 
			
		||||
	"syscall"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
type Lockfile struct {
 | 
			
		||||
	path string
 | 
			
		||||
	fd int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(path string) *Lockfile {
 | 
			
		||||
	return &Lockfile{path: path}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lf *Lockfile) Lock() error {
 | 
			
		||||
	fd, err := syscall.Open(lf.path, syscall.O_CREAT | syscall.O_RDWR, 0600)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	lf.fd = fd
 | 
			
		||||
	err = syscall.Flock(lf.fd, syscall.LOCK_EX | syscall.LOCK_NB)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lf *Lockfile) Unlock() error {
 | 
			
		||||
	if lf.fd == 0 {
 | 
			
		||||
		panic(errors.New("Lockfile was not locked"))
 | 
			
		||||
	}
 | 
			
		||||
	err := syscall.Flock(lf.fd, syscall.LOCK_UN)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err = syscall.Close(lf.fd)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	os.Remove(lf.path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										71
									
								
								lockfile/lockfile_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								lockfile/lockfile_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
package lockfile_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"after-lock/lockfile"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func tmpFile() string {
 | 
			
		||||
	tf, err := ioutil.TempFile("", "lockfile_test*")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	return tf.Name()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestNotLockedUnlockPanics(t *testing.T) {
 | 
			
		||||
	defer func() {
 | 
			
		||||
        if r := recover(); r == nil {
 | 
			
		||||
            t.Errorf("Unlock() did not panic")
 | 
			
		||||
        }
 | 
			
		||||
    }()
 | 
			
		||||
 | 
			
		||||
	tf := tmpFile()
 | 
			
		||||
	defer os.Remove(tf)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	err := lockfile.New(tf).Unlock()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSecondLockFails(t *testing.T) {
 | 
			
		||||
	tf := tmpFile()
 | 
			
		||||
	defer os.Remove(tf)
 | 
			
		||||
 | 
			
		||||
	fl1 := lockfile.New(tf)
 | 
			
		||||
	err := fl1.Lock()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	defer fl1.Unlock()
 | 
			
		||||
 | 
			
		||||
	fl2 := lockfile.New(tf)
 | 
			
		||||
	err = fl2.Lock()
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Errorf("Second Lock() did not fail")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestLockUnlock(t *testing.T) {
 | 
			
		||||
	tf := tmpFile()
 | 
			
		||||
	defer os.Remove(tf)
 | 
			
		||||
 | 
			
		||||
	fl := lockfile.New(tf)
 | 
			
		||||
	err := fl.Lock()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	err = fl.Unlock()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := os.Stat(tf); !os.IsNotExist(err) {
 | 
			
		||||
		t.Errorf("Lockfile %s still exists", tf)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user