os: filesystem level locking api (#11191)
parent
8521e227b4
commit
90b25e7a4b
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -139,7 +139,7 @@ pub fn read_file(path string) ?string {
|
|||
// truncate changes the size of the file located in `path` to `len`.
|
||||
// Note that changing symbolic links on Windows only works as admin.
|
||||
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 {
|
||||
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))
|
||||
}
|
||||
} $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
|
||||
return error_with_code('cp: failed to open $src', int(fp_from))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue