Skip to content

Instantly share code, notes, and snippets.

@ahmagdy
Last active February 14, 2021 21:31
Show Gist options
  • Save ahmagdy/484ab84c8bc6781b972867f750d30a41 to your computer and use it in GitHub Desktop.
Save ahmagdy/484ab84c8bc6781b972867f750d30a41 to your computer and use it in GitHub Desktop.
File Locking In Go
package main
import (
"flockexper/lock"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
wg.Add(2)
go func() {
if err := readerTask("/tmp/test"); err != nil {
fmt.Println(err.Error())
}
}()
go func() {
if err := updateTask("/tmp/test"); err != nil {
fmt.Println(err.Error())
}
}()
wg.Wait()
}
func updateTask(dirName string) error {
defer wg.Done()
locker := lock.NewExclusiveLock(dirName)
if err := locker.TakeLock(true); err != nil {
return err
}
defer locker.Close()
dirEntries, err := ioutil.ReadDir(dirName)
if err != nil {
return fmt.Errorf("failed to read dir: %w", err)
}
for _, dirEntry := range dirEntries {
fmt.Println("== " + dirEntry.Name())
srcSubDirName := filepath.Join(dirName, dirEntry.Name())
if dirEntry.IsDir() {
destSubDirName := filepath.Join(dirName, dirEntry.Name()+"_v2")
fmt.Printf("== Moving %s to %s\n", srcSubDirName, destSubDirName)
if err := os.Rename(srcSubDirName, destSubDirName); err != nil {
return err
}
continue
}
if dirEntry.Name() == "c.d" {
if err := os.Remove(srcSubDirName); err != nil {
return nil
}
}
}
return nil
}
func readerTask(dirName string) error {
defer wg.Done()
locker := lock.NewSharedLock(dirName)
if err := locker.TakeLock(true); err != nil {
return err
}
defer locker.Close()
return read(dirName)
}
func read(dirName string) error {
dirEntries, err := ioutil.ReadDir(dirName)
if err != nil {
return fmt.Errorf("failed to read dir: %w", err)
}
for _, dirEntry := range dirEntries {
filePath := filepath.Join(dirName, dirEntry.Name())
fmt.Println("-- Reading " + filePath)
if dirEntry.IsDir() {
read(filePath)
continue
}
f, err := ioutil.ReadFile(filePath)
if err != nil {
return err
}
fmt.Println(string(f))
time.Sleep(1 * time.Second)
}
return nil
}
package lock
import (
"os"
"path/filepath"
"syscall"
)
const _lockPostfix = ".lock"
type Manager struct {
dirName string
fileName string
lockType int
file *os.File
}
func NewExclusiveLock(dirName string) *Manager {
return &Manager{
dirName: dirName,
fileName: getLockFileName(dirName),
lockType: syscall.LOCK_EX,
}
}
func NewSharedLock(dirName string) *Manager {
return &Manager{
dirName: dirName,
fileName: getLockFileName(dirName),
lockType: syscall.LOCK_SH,
}
}
func (m *Manager) TakeLock(waitToAcquire bool) error {
lockType := syscall.LOCK_EX
if !waitToAcquire {
lockType = lockType | syscall.LOCK_NB
}
file, err := os.OpenFile(m.fileName, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return err
}
m.file = file
return syscall.Flock(int(file.Fd()), lockType)
}
func (m *Manager) Close() error {
if err := syscall.Flock(int(m.file.Fd()), syscall.LOCK_UN); err != nil {
return err
}
return m.file.Close()
}
func getLockFileName(dirName string) string {
parentDir := filepath.Dir(dirName)
base := filepath.Base(dirName)
return filepath.Join(parentDir, base+_lockPostfix)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment