os: filesystem level locking api (#11191)

pull/11215/head
pancake 2021-08-17 07:21:33 +02:00 committed by GitHub
parent 8521e227b4
commit 90b25e7a4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 200 additions and 2 deletions

View File

@ -0,0 +1,27 @@
import os
import os.filelock
fn test_flock() {
lockfile := 'test.lock'
mut l := filelock.new(lockfile)
assert !os.exists(lockfile)
l.acquire() or { panic(err) }
assert os.exists(lockfile)
// do stuff
l.release()
assert !os.exists(lockfile)
}
fn test_flock_try() {
lockfile := 'test-try.lock'
mut l := filelock.new(lockfile)
assert l.try_acquire()
l.release()
assert !os.exists(lockfile)
assert l.try_acquire()
assert os.exists(lockfile)
l.release()
assert l.try_acquire()
l.release()
assert !os.exists(lockfile)
}

View File

@ -0,0 +1,14 @@
module filelock
pub struct FileLock {
name string
mut:
fd int
}
pub fn new(fileName string) FileLock {
return FileLock{
name: fileName
fd: -1
}
}

View File

@ -0,0 +1,82 @@
module filelock
import time
#include <sys/file.h>
fn C.unlink(&char) int
fn C.open(&char, int, int) int
fn C.flock(int, int) int
[unsafe]
pub fn (mut l FileLock) unlink() {
if l.fd != -1 {
C.close(l.fd)
l.fd = -1
}
C.unlink(&char(l.name.str))
}
pub fn (mut l FileLock) acquire() ?bool {
if l.fd != -1 {
// lock already acquired by this instance
return false
}
fd := open_lockfile(l.name)
if fd == -1 {
return error('cannot create lock file $l.name')
}
if C.flock(fd, C.LOCK_EX) == -1 {
C.close(fd)
return error('cannot lock')
}
l.fd = fd
return true
}
pub fn (mut l FileLock) release() bool {
if l.fd != -1 {
unsafe {
l.unlink()
}
return true
}
return false
}
pub fn (mut l FileLock) wait_acquire(s int) ?bool {
fin := time.now().add(s)
for time.now() < fin {
if l.try_acquire() {
return true
}
C.usleep(1000)
}
return false
}
fn open_lockfile(f string) int {
mut fd := C.open(&char(f.str), C.O_CREAT, 0o644)
if fd == -1 {
// if stat is too old delete lockfile
fd = C.open(&char(f.str), C.O_RDONLY, 0)
}
return fd
}
pub fn (mut l FileLock) try_acquire() bool {
if l.fd != -1 {
return true
}
fd := open_lockfile('$l.name')
if fd != -1 {
err := C.flock(fd, C.LOCK_EX | C.LOCK_NB)
if err == -1 {
C.close(fd)
return false
}
l.fd = fd
return true
}
return false
}

View File

@ -0,0 +1,75 @@
module filelock
import time
fn C.DeleteFileW(&u16) bool
fn C.CreateFileW(&u16, u32, u32, voidptr, u32, u32, voidptr) voidptr
fn C.CloseHandle(voidptr) bool
pub fn (mut l FileLock) unlink() {
if l.fd != -1 {
C.CloseHandle(l.fd)
l.fd = -1
}
t_wide := l.name.to_wide()
C.DeleteFileW(t_wide)
}
pub fn (mut l FileLock) acquire() ?bool {
if l.fd != -1 {
// lock already acquired by this instance
return false
}
fd := open(l.name)
if fd == -1 {
return error('cannot create lock file $l.name')
}
l.fd = fd
return true
}
pub fn (mut l FileLock) release() bool {
if l.fd != -1 {
C.CloseHandle(l.fd)
l.fd = -1
t_wide := l.name.to_wide()
C.DeleteFileW(t_wide)
return true
}
return false
}
pub fn (mut l FileLock) wait_acquire(s int) ?bool {
fin := time.now().add(s)
for time.now() < fin {
if l.try_acquire() {
return true
}
time.sleep(1 * time.millisecond)
}
return false
}
fn open(f string) voidptr {
f_wide := f.to_wide()
// locking it
fd := C.CreateFileW(f_wide, C.GENERIC_READ | C.GENERIC_WRITE, 0, 0, C.OPEN_ALWAYS,
C.FILE_ATTRIBUTE_NORMAL, 0)
if fd == C.INVALID_HANDLE_VALUE {
fd == -1
}
return fd
}
pub fn (mut l FileLock) try_acquire() bool {
if l.fd != -1 {
// lock already acquired by this instance
return false
}
fd := open(l.name)
if fd == -1 {
return false
}
l.fd = fd
return true
}

View File

@ -139,7 +139,7 @@ pub fn read_file(path string) ?string {
// truncate changes the size of the file located in `path` to `len`. // truncate changes the size of the file located in `path` to `len`.
// Note that changing symbolic links on Windows only works as admin. // Note that changing symbolic links on Windows only works as admin.
pub fn truncate(path string, len u64) ? { pub fn truncate(path string, len u64) ? {
fp := C.open(&char(path.str), o_wronly | o_trunc) fp := C.open(&char(path.str), o_wronly | o_trunc, 0)
defer { defer {
C.close(fp) C.close(fp)
} }
@ -236,7 +236,7 @@ pub fn cp(src string, dst string) ? {
return error_with_code('failed to copy $src to $dst', int(result)) return error_with_code('failed to copy $src to $dst', int(result))
} }
} $else { } $else {
fp_from := C.open(&char(src.str), C.O_RDONLY) fp_from := C.open(&char(src.str), C.O_RDONLY, 0)
if fp_from < 0 { // Check if file opened if fp_from < 0 { // Check if file opened
return error_with_code('cp: failed to open $src', int(fp_from)) return error_with_code('cp: failed to open $src', int(fp_from))
} }