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`.
|
// 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))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue