| 1 | module filelock |
| 2 | |
| 3 | #insert "@VEXEROOT/vlib/os/filelock/filelock_helpers.h" |
| 4 | |
| 5 | fn C.DeleteFileW(&u16) bool |
| 6 | fn C.CreateFileW(&u16, u32, u32, voidptr, u32, u32, voidptr) voidptr |
| 7 | fn C.CloseHandle(voidptr) bool |
| 8 | fn C.v_filelock_lock(voidptr, int, int, u64, u64) int |
| 9 | fn C.v_filelock_unlock(voidptr, u64, u64) int |
| 10 | |
| 11 | fn (l &FileLock) open_lock() i64 { |
| 12 | if l.target == .file { |
| 13 | return open_existing_file(l.name, l.mode) |
| 14 | } |
| 15 | return open_lockfile(l.name) |
| 16 | } |
| 17 | |
| 18 | fn open_lockfile(path string) i64 { |
| 19 | path_wide := path.to_wide() |
| 20 | handle := C.CreateFileW(path_wide, C.GENERIC_READ | C.GENERIC_WRITE, 0, 0, C.OPEN_ALWAYS, |
| 21 | C.FILE_ATTRIBUTE_NORMAL, 0) |
| 22 | return file_handle(handle) |
| 23 | } |
| 24 | |
| 25 | fn open_existing_file(path string, mode LockMode) i64 { |
| 26 | path_wide := path.to_wide() |
| 27 | access := if mode == .shared { C.GENERIC_READ } else { C.GENERIC_READ | C.GENERIC_WRITE } |
| 28 | share_mode := C.FILE_SHARE_READ | C.FILE_SHARE_WRITE | C.FILE_SHARE_DELETE |
| 29 | handle := C.CreateFileW(path_wide, access, share_mode, 0, C.OPEN_EXISTING, |
| 30 | C.FILE_ATTRIBUTE_NORMAL, 0) |
| 31 | return file_handle(handle) |
| 32 | } |
| 33 | |
| 34 | fn file_handle(handle voidptr) i64 { |
| 35 | return if handle == voidptr(-1) { -1 } else { i64(handle) } |
| 36 | } |
| 37 | |
| 38 | fn (l &FileLock) lock_handle(handle voidptr, immediate bool) bool { |
| 39 | if l.target != .file { |
| 40 | return true |
| 41 | } |
| 42 | return C.v_filelock_lock(handle, int(l.mode == .exclusive), int(immediate), l.start, l.len) == 0 |
| 43 | } |
| 44 | |
| 45 | fn (mut l FileLock) close_lock() { |
| 46 | if l.fd == -1 { |
| 47 | return |
| 48 | } |
| 49 | handle := voidptr(l.fd) |
| 50 | if l.target == .file { |
| 51 | C.v_filelock_unlock(handle, l.start, l.len) |
| 52 | C.CloseHandle(handle) |
| 53 | } else { |
| 54 | C.CloseHandle(handle) |
| 55 | path_wide := l.name.to_wide() |
| 56 | C.DeleteFileW(path_wide) |
| 57 | } |
| 58 | l.fd = -1 |
| 59 | } |
| 60 | |
| 61 | // acquire blocks until the lock is acquired. |
| 62 | pub fn (mut l FileLock) acquire() ! { |
| 63 | if l.fd != -1 { |
| 64 | return error_with_code('lock already acquired by this instance', 1) |
| 65 | } |
| 66 | fd := l.open_lock() |
| 67 | if fd == -1 { |
| 68 | msg := if l.target == .file { |
| 69 | 'cannot open file ${l.name}' |
| 70 | } else { |
| 71 | 'cannot create lock file ${l.name}' |
| 72 | } |
| 73 | return error_with_code(msg, -1) |
| 74 | } |
| 75 | handle := voidptr(fd) |
| 76 | if !l.lock_handle(handle, false) { |
| 77 | C.CloseHandle(handle) |
| 78 | return error_with_code('cannot lock ${l.name}', -2) |
| 79 | } |
| 80 | l.fd = fd |
| 81 | } |
| 82 | |
| 83 | // try_acquire tries to acquire the lock without blocking. |
| 84 | pub fn (mut l FileLock) try_acquire() bool { |
| 85 | if l.fd != -1 { |
| 86 | return true |
| 87 | } |
| 88 | fd := l.open_lock() |
| 89 | if fd == -1 { |
| 90 | return false |
| 91 | } |
| 92 | handle := voidptr(fd) |
| 93 | if !l.lock_handle(handle, true) { |
| 94 | C.CloseHandle(handle) |
| 95 | return false |
| 96 | } |
| 97 | l.fd = fd |
| 98 | return true |
| 99 | } |
| 100 | |