os.filelock is not reliable for an existing file
os.filelock.FileLock manages is own file which is not safe to use when process locking an existing file (which is what flock is often used for).
I can accept that having a separate lock file is useful for cases where processes do not try to acquire locks at the same time, but there should be an API for locking an existing file (partial or full, read or write) using the proper flock API.
Now, this issue also is exiting V 0.4.10. The method filelock.new() panic when this lock hold by another process but the V std library returns "FileLock" instead of "!FileLock". This can lead to application crash.
V 0.4.10 dbc4071.
The test case
main.v
module main
import time
import os
import os.filelock
fn main() {
filename := 'test.lock'
mut another := os.new_process('./another')
another.set_args([filename]);
another.run()
println('0) exec another ok');
time.sleep(time.Duration(1 * time.second))
// filelock.new() panic when this lock hold by another process
// but the V std library returns "FileLock" instead of "!FileLock".
mut flock := filelock.new(filename)
flock.acquire()!
println('1) lock file');
flock.acquire() or { // not reentrant
eprintln('2.1) failed to lock file again in the same thread');
}
_ := filelock.new(filename)
println('2.2) create filelock again in the same thread')
task := spawn new_lock_in_thread(filename)
task.wait()
println('3) wait for you entering ..')
os.get_line()
flock.release()
println('4) unlock file')
flock.acquire()!
println('5) lock file again');
flock.release()
println('6) unlock file again')
println('7) ok')
}
fn new_lock_in_thread(filename string) {
mut flock := filelock.new(filename)
println('2.3) create filelock again in the another thread')
if flock.try_acquire() {
println('2.4) acquire filelock in another thread!')
flock.release()
}
}
another.v
import os.filelock
import os
import time
fn main() {
println('another: running ..')
filename := os.args[1]
mut flock := filelock.new(filename)
flock.acquire()!
println('another: lock file and sleep for a while')
defer { flock.release() }
time.sleep(time.Duration(2 * time.second))
println('another: exit')
}
Seems to run fine on latest V with below output:
0) exec another ok
another: running ..
another: lock file and sleep for a while
another: exit
1) lock file
2.1) failed to lock file again in the same thread
2.2) create filelock again in the same thread
2.3) create filelock again in the another thread
2.4) acquire filelock in another thread!
3) wait for you entering ..
4) unlock file
5) lock file again
6) unlock file again
7) ok