mutex.rs 6.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

11 12
//! A simple native mutex implementation. Warning: this API is likely
//! to change soon.
13

14
#![allow(dead_code)]
15

A
Alex Crichton 已提交
16
use core::prelude::*;
17
use alloc::boxed::Box;
A
Alex Crichton 已提交
18
use rustrt::mutex;
19

A
Alex Crichton 已提交
20
pub const LOCKED: uint = 1 << 0;
21
pub const BLOCKED: uint = 1 << 1;
22 23 24 25 26 27 28 29 30

/// A mutual exclusion primitive useful for protecting shared data
///
/// This mutex will properly block tasks waiting for the lock to become
/// available. The mutex can also be statically initialized or created via a
/// `new` constructor.
///
/// # Example
///
A
Aaron Turon 已提交
31 32
/// ```rust,ignore
/// use std::sync::mutex::Mutex;
33
///
34
/// let m = Mutex::new();
35 36 37 38 39
/// let guard = m.lock();
/// // do some work
/// drop(guard); // unlock the lock
/// ```
pub struct Mutex {
A
Alex Crichton 已提交
40 41 42 43 44 45 46 47
    // Note that this static mutex is in a *box*, not inlined into the struct
    // itself. This is done for memory safety reasons with the usage of a
    // StaticNativeMutex inside the static mutex above. Once a native mutex has
    // been used once, its address can never change (it can't be moved). This
    // mutex type can be safely moved at any time, so to ensure that the native
    // mutex is used correctly we box the inner lock to give it a constant
    // address.
    lock: Box<StaticMutex>,
48 49 50 51 52 53 54 55 56 57 58 59
}

/// The static mutex type is provided to allow for static allocation of mutexes.
///
/// Note that this is a separate type because using a Mutex correctly means that
/// it needs to have a destructor run. In Rust, statics are not allowed to have
/// destructors. As a result, a `StaticMutex` has one extra method when compared
/// to a `Mutex`, a `destroy` method. This method is unsafe to call, and
/// documentation can be found directly on the method.
///
/// # Example
///
A
Aaron Turon 已提交
60 61
/// ```rust,ignore
/// use std::sync::mutex::{StaticMutex, MUTEX_INIT};
62
///
A
Alex Crichton 已提交
63
/// static LOCK: StaticMutex = MUTEX_INIT;
64
///
A
Alex Crichton 已提交
65
/// {
66 67 68 69 70 71
///     let _g = LOCK.lock();
///     // do some productive work
/// }
/// // lock is unlocked here.
/// ```
pub struct StaticMutex {
72
    lock: mutex::StaticNativeMutex,
73 74 75 76
}

/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
/// dropped (falls out of scope), the lock will be unlocked.
77
#[must_use]
78
pub struct Guard<'a> {
79 80 81 82 83
    guard: mutex::LockGuard<'a>,
}

fn lift_guard(guard: mutex::LockGuard) -> Guard {
    Guard { guard: guard }
84 85 86 87
}

/// Static initialization of a mutex. This constant can be used to initialize
/// other mutex constants.
A
Alex Crichton 已提交
88
pub const MUTEX_INIT: StaticMutex = StaticMutex {
89
    lock: mutex::NATIVE_MUTEX_INIT
90 91 92 93
};

impl StaticMutex {
    /// Attempts to grab this lock, see `Mutex::try_lock`
94
    pub fn try_lock<'a>(&'a self) -> Option<Guard<'a>> {
95
        unsafe { self.lock.trylock().map(lift_guard) }
96 97 98
    }

    /// Acquires this lock, see `Mutex::lock`
99
    pub fn lock<'a>(&'a self) -> Guard<'a> {
100
        lift_guard(unsafe { self.lock.lock() })
101 102 103 104 105 106 107 108 109 110 111 112
    }

    /// Deallocates resources associated with this static mutex.
    ///
    /// This method is unsafe because it provides no guarantees that there are
    /// no active users of this mutex, and safety is not guaranteed if there are
    /// active users of this mutex.
    ///
    /// This method is required to ensure that there are no memory leaks on
    /// *all* platforms. It may be the case that some platforms do not leak
    /// memory if this method is not called, but this is not guaranteed to be
    /// true on all platforms.
113
    pub unsafe fn destroy(&self) {
114 115 116 117 118 119 120 121
        self.lock.destroy()
    }
}

impl Mutex {
    /// Creates a new mutex in an unlocked state ready for use.
    pub fn new() -> Mutex {
        Mutex {
A
Alex Crichton 已提交
122
            lock: box StaticMutex {
123
                lock: unsafe { mutex::StaticNativeMutex::new() },
124 125 126 127 128 129 130 131 132 133 134
            }
        }
    }

    /// Attempts to acquire this lock.
    ///
    /// If the lock could not be acquired at this time, then `None` is returned.
    /// Otherwise, an RAII guard is returned. The lock will be unlocked when the
    /// guard is dropped.
    ///
    /// This function does not block.
135
    pub fn try_lock<'a>(&'a self) -> Option<Guard<'a>> {
136 137 138 139 140
        self.lock.try_lock()
    }

    /// Acquires a mutex, blocking the current task until it is able to do so.
    ///
H
Huon Wilson 已提交
141
    /// This function will block the local task until it is available to acquire
142 143 144
    /// the mutex. Upon returning, the task is the only task with the mutex
    /// held. An RAII guard is returned to allow scoped unlock of the lock. When
    /// the guard goes out of scope, the mutex will be unlocked.
145
    pub fn lock<'a>(&'a self) -> Guard<'a> { self.lock.lock() }
146 147 148 149 150 151 152 153 154 155 156 157 158
}

impl Drop for Mutex {
    fn drop(&mut self) {
        // This is actually safe b/c we know that there is no further usage of
        // this mutex (it's up to the user to arrange for a mutex to get
        // dropped, that's not our job)
        unsafe { self.lock.destroy() }
    }
}

#[cfg(test)]
mod test {
A
Aaron Turon 已提交
159
    use prelude::*;
160 161 162 163
    use super::{Mutex, StaticMutex, MUTEX_INIT};

    #[test]
    fn smoke() {
164
        let m = Mutex::new();
165 166 167 168 169 170
        drop(m.lock());
        drop(m.lock());
    }

    #[test]
    fn smoke_static() {
N
NODA, Kai 已提交
171
        static M: StaticMutex = MUTEX_INIT;
172
        unsafe {
N
NODA, Kai 已提交
173 174 175
            drop(M.lock());
            drop(M.lock());
            M.destroy();
176 177 178 179 180
        }
    }

    #[test]
    fn lots_and_lots() {
N
NODA, Kai 已提交
181
        static M: StaticMutex = MUTEX_INIT;
182
        static mut CNT: uint = 0;
N
NODA, Kai 已提交
183 184
        static J: uint = 1000;
        static K: uint = 3;
185 186

        fn inc() {
N
NODA, Kai 已提交
187
            for _ in range(0, J) {
188
                unsafe {
N
NODA, Kai 已提交
189
                    let _g = M.lock();
190 191 192 193 194
                    CNT += 1;
                }
            }
        }

195
        let (tx, rx) = channel();
N
NODA, Kai 已提交
196
        for _ in range(0, K) {
197
            let tx2 = tx.clone();
198
            spawn(proc() { inc(); tx2.send(()); });
199 200
            let tx2 = tx.clone();
            spawn(proc() { inc(); tx2.send(()); });
201 202
        }

203
        drop(tx);
N
NODA, Kai 已提交
204
        for _ in range(0, 2 * K) {
205
            rx.recv();
206
        }
N
NODA, Kai 已提交
207
        assert_eq!(unsafe {CNT}, J * K * 2);
208
        unsafe {
N
NODA, Kai 已提交
209
            M.destroy();
210 211 212 213 214
        }
    }

    #[test]
    fn trylock() {
215
        let m = Mutex::new();
216 217 218
        assert!(m.try_lock().is_some());
    }
}