1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
use std::ops::{Deref, DerefMut};

use cfile::{RawFilePtr, CFile};

/// A locked reference to the CFile stream.
pub struct FileLock<'a>(&'a mut CFile);

impl<'a> Drop for FileLock<'a> {
    fn drop(&mut self) {
        unsafe { funlockfile(self.0.stream()) }
    }
}

impl<'a> Deref for FileLock<'a> {
    type Target = CFile;

    fn deref(&self) -> &Self::Target {
        self.0
    }
}

impl<'a> DerefMut for FileLock<'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.0
    }
}

/// Extension methods for `CFile` lock.
pub trait FileLockExt {
    /// acquires an exclusive lock on the specified object.
    ///
    /// If another thread has already locked the object,
    /// will block until the lock is released.
    ///
    /// # Examples
    /// ```
    /// use std::io::Write;
    /// use cfile::{tmpfile, FileLockExt};
    ///
    /// let mut f = tmpfile().unwrap();
    /// let mut l = f.lock();
    ///
    /// assert_eq!(l.write(b"test").unwrap(), 4);
    /// ```
    fn lock(&mut self) -> FileLock;

    /// a non-blocking version of `lock()`;
    ///
    /// if the lock cannot be acquired immediately,
    /// `try_lock()` returns `None` instead of blocking.
    ///
    /// # Examples
    /// ```
    /// use std::io::{Read, Write, BufRead, BufReader, Seek, SeekFrom};
    /// use cfile::{tmpfile, FileLockExt};
    ///
    /// let mut f = tmpfile().unwrap();
    ///
    /// if let Some(mut c) = f.try_lock() {
    ///     assert_eq!(c.write(b"test").unwrap(), 4);
    /// }
    ///
    /// assert_eq!(f.seek(SeekFrom::Start(0)).unwrap(), 0); // seek to the beginning of stream
    ///
    /// let mut r = BufReader::new(f);
    /// let mut s = String::new();
    /// assert_eq!(r.read_line(&mut s).unwrap(), 4); // read back the text
    /// assert_eq!(s, "test");
    /// ```
    fn try_lock(&mut self) -> Option<FileLock>;

    /// releases the lock on an object acquired
    /// by an earlier call to lock() or try_lock().
    ///
    fn unlock(&self);
}

extern "C" {
    fn flockfile(file: RawFilePtr);

    fn ftrylockfile(file: RawFilePtr) -> i32;

    fn funlockfile(file: RawFilePtr);
}

impl<'a> FileLockExt for CFile {
    fn lock(&mut self) -> FileLock {
        unsafe { flockfile(self.stream()) }

        FileLock(self)
    }

    fn try_lock(&mut self) -> Option<FileLock> {
        if unsafe { ftrylockfile(self.stream()) } == 0 {
            Some(FileLock(self))
        } else {
            None
        }
    }

    fn unlock(&self) {
        unsafe { funlockfile(self.stream()) }
    }
}