changeset 478:591a2285c83c

Use flock(2) instead of fcntl(2) for locking databases. In an attempt to fix fcntl errors on Mac OS X (bitbucket issue #4).
author Lewin Bormann <lbo@spheniscida.de>
date Thu, 11 Jul 2019 19:42:15 +0200
parents 30c7871a4a9e
children c18f25f601e8
files src/disk_env.rs
diffstat 1 files changed, 10 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/src/disk_env.rs	Thu Jul 11 17:22:40 2019 +0200
+++ b/src/disk_env.rs	Thu Jul 11 19:42:15 2019 +0200
@@ -6,7 +6,6 @@
 use std::fs;
 use std::io::{self, Read, Write};
 use std::iter::FromIterator;
-use std::mem;
 use std::os::unix::io::IntoRawFd;
 use std::path::{Path, PathBuf};
 use std::sync::{Arc, Mutex};
@@ -128,26 +127,18 @@
                 .open(p)
                 .map_err(|e| map_err_with_name("lock", p, e))?;
 
-            let flock_arg = libc::flock {
-                l_type: F_WRLCK,
-                l_whence: libc::SEEK_SET as libc::c_short,
-                l_start: 0,
-                l_len: 0,
-                l_pid: 0,
-            };
             let fd = f.into_raw_fd();
             let result = unsafe {
-                libc::fcntl(
-                    fd,
-                    libc::F_SETLK,
-                    mem::transmute::<&libc::flock, *const libc::flock>(&&flock_arg),
-                )
+                libc::flock(fd as libc::c_int, libc::LOCK_EX | libc::LOCK_NB)
             };
 
             if result < 0 {
+                if errno::errno() == errno::Errno(libc::EWOULDBLOCK) {
+                    return Err(Status::new(StatusCode::LockError, "lock on database is already held by different process"))
+                }
                 return Err(Status::new(
                     StatusCode::Errno(errno::errno()),
-                    &format!("fcntl error on fd {} (file {})", fd, p.display()),
+                    &format!("unknown lock error on fd {} (file {})", fd, p.display()),
                 ));
             }
 
@@ -160,7 +151,6 @@
     }
     fn unlock(&self, l: FileLock) -> Result<()> {
         let mut locks = self.locks.lock().unwrap();
-
         if !locks.contains_key(&l.id) {
             return err(
                 StatusCode::LockError,
@@ -168,19 +158,12 @@
             );
         } else {
             let fd = locks.remove(&l.id).unwrap();
-            let flock_arg = libc::flock {
-                l_type: F_UNLCK,
-                l_whence: libc::SEEK_SET as libc::c_short,
-                l_start: 0,
-                l_len: 0,
-                l_pid: 0,
-            };
             let result = unsafe {
-                libc::fcntl(
-                    fd,
-                    libc::F_SETLK,
-                    mem::transmute::<&libc::flock, *const libc::flock>(&&flock_arg),
-                )
+                let ok = libc::fcntl(fd, libc::F_GETFD);
+                if ok < 0 { // Likely EBADF when already closed. In that case, the lock is released and all is fine.
+                    return Ok(())
+                }
+                libc::flock(fd, libc::LOCK_UN)
             };
             if result < 0 {
                 return err(StatusCode::LockError, &format!("unlock failed: {}", l.id));