2013-05-09 17:36:45 -07:00
|
|
|
// Copyright 2013 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.
|
|
|
|
|
|
|
|
use clone::Clone;
|
2013-06-05 17:56:24 -07:00
|
|
|
use kinds::Send;
|
2013-12-12 17:27:37 -08:00
|
|
|
use sync::arc::UnsafeArc;
|
2014-02-15 12:01:52 +11:00
|
|
|
use unstable::mutex::NativeMutex;
|
2013-05-09 17:36:45 -07:00
|
|
|
|
|
|
|
struct ExData<T> {
|
2014-02-15 12:01:52 +11:00
|
|
|
lock: NativeMutex,
|
2013-05-09 17:36:45 -07:00
|
|
|
failed: bool,
|
|
|
|
data: T,
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An arc over mutable data that is protected by a lock. For library use only.
|
2013-07-02 13:37:19 -04:00
|
|
|
*
|
|
|
|
* # Safety note
|
|
|
|
*
|
|
|
|
* This uses a pthread mutex, not one that's aware of the userspace scheduler.
|
2013-07-22 13:57:40 -07:00
|
|
|
* The user of an Exclusive must be careful not to invoke any functions that may
|
2013-07-02 13:37:19 -04:00
|
|
|
* reschedule the task while holding the lock, or deadlock may result. If you
|
2013-08-16 12:49:40 -07:00
|
|
|
* need to block or deschedule while accessing shared state, use extra::sync::RWArc.
|
2013-05-09 17:36:45 -07:00
|
|
|
*/
|
|
|
|
pub struct Exclusive<T> {
|
2014-03-27 15:09:47 -07:00
|
|
|
x: UnsafeArc<ExData<T>>
|
2013-05-09 17:36:45 -07:00
|
|
|
}
|
|
|
|
|
2013-06-05 17:56:24 -07:00
|
|
|
impl<T:Send> Clone for Exclusive<T> {
|
2013-07-22 13:57:40 -07:00
|
|
|
// Duplicate an Exclusive Arc, as std::arc::clone.
|
2013-05-09 17:36:45 -07:00
|
|
|
fn clone(&self) -> Exclusive<T> {
|
2013-05-09 20:27:42 -07:00
|
|
|
Exclusive { x: self.x.clone() }
|
2013-05-09 17:36:45 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-05 17:56:24 -07:00
|
|
|
impl<T:Send> Exclusive<T> {
|
2013-07-22 13:57:40 -07:00
|
|
|
pub fn new(user_data: T) -> Exclusive<T> {
|
|
|
|
let data = ExData {
|
2014-02-15 12:01:52 +11:00
|
|
|
lock: unsafe {NativeMutex::new()},
|
2013-07-22 13:57:40 -07:00
|
|
|
failed: false,
|
|
|
|
data: user_data
|
|
|
|
};
|
|
|
|
Exclusive {
|
2013-08-27 20:00:57 +10:00
|
|
|
x: UnsafeArc::new(data)
|
2013-07-22 13:57:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-15 12:01:52 +11:00
|
|
|
// Exactly like sync::MutexArc.access(). Same reason for being
|
|
|
|
// unsafe.
|
2013-05-09 17:36:45 -07:00
|
|
|
//
|
2013-08-16 12:49:40 -07:00
|
|
|
// Currently, scheduling operations (i.e., descheduling, receiving on a pipe,
|
2013-05-09 17:36:45 -07:00
|
|
|
// accessing the provided condition variable) are prohibited while inside
|
2013-07-22 13:57:40 -07:00
|
|
|
// the Exclusive. Supporting that is a work in progress.
|
2013-06-18 14:45:18 -07:00
|
|
|
#[inline]
|
2013-11-18 21:15:42 -08:00
|
|
|
pub unsafe fn with<U>(&self, f: |x: &mut T| -> U) -> U {
|
2013-05-09 20:27:42 -07:00
|
|
|
let rec = self.x.get();
|
2013-11-25 17:55:41 -08:00
|
|
|
let _l = (*rec).lock.lock();
|
|
|
|
if (*rec).failed {
|
|
|
|
fail!("Poisoned Exclusive::new - another task failed inside!");
|
2013-05-09 17:36:45 -07:00
|
|
|
}
|
2013-11-25 17:55:41 -08:00
|
|
|
(*rec).failed = true;
|
|
|
|
let result = f(&mut (*rec).data);
|
|
|
|
(*rec).failed = false;
|
|
|
|
result
|
2013-05-09 17:36:45 -07:00
|
|
|
}
|
|
|
|
|
2013-06-18 14:45:18 -07:00
|
|
|
#[inline]
|
2013-11-18 21:15:42 -08:00
|
|
|
pub unsafe fn with_imm<U>(&self, f: |x: &T| -> U) -> U {
|
2013-12-01 10:18:47 -05:00
|
|
|
self.with(|x| f(x))
|
2013-05-09 17:36:45 -07:00
|
|
|
}
|
|
|
|
|
2013-10-22 15:00:37 -07:00
|
|
|
#[inline]
|
2013-11-18 21:15:42 -08:00
|
|
|
pub unsafe fn hold_and_signal(&self, f: |x: &mut T|) {
|
2013-10-22 15:00:37 -07:00
|
|
|
let rec = self.x.get();
|
2014-03-22 00:45:41 -07:00
|
|
|
let guard = (*rec).lock.lock();
|
2013-11-25 17:55:41 -08:00
|
|
|
if (*rec).failed {
|
|
|
|
fail!("Poisoned Exclusive::new - another task failed inside!");
|
2013-10-22 15:00:37 -07:00
|
|
|
}
|
2013-11-25 17:55:41 -08:00
|
|
|
(*rec).failed = true;
|
|
|
|
f(&mut (*rec).data);
|
|
|
|
(*rec).failed = false;
|
2014-02-15 12:01:52 +11:00
|
|
|
guard.signal();
|
2013-10-22 15:00:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2013-11-18 21:15:42 -08:00
|
|
|
pub unsafe fn hold_and_wait(&self, f: |x: &T| -> bool) {
|
2013-10-22 15:00:37 -07:00
|
|
|
let rec = self.x.get();
|
2014-03-22 00:45:41 -07:00
|
|
|
let l = (*rec).lock.lock();
|
2013-11-25 17:55:41 -08:00
|
|
|
if (*rec).failed {
|
|
|
|
fail!("Poisoned Exclusive::new - another task failed inside!");
|
|
|
|
}
|
|
|
|
(*rec).failed = true;
|
|
|
|
let result = f(&(*rec).data);
|
|
|
|
(*rec).failed = false;
|
|
|
|
if result {
|
|
|
|
l.wait();
|
2013-10-22 15:00:37 -07:00
|
|
|
}
|
|
|
|
}
|
2013-05-09 17:36:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2013-07-02 13:37:19 -04:00
|
|
|
use option::*;
|
2013-08-01 18:35:46 -04:00
|
|
|
use prelude::*;
|
2013-12-12 21:38:57 -08:00
|
|
|
use super::Exclusive;
|
2013-05-09 17:36:45 -07:00
|
|
|
use task;
|
2013-08-29 20:23:29 -04:00
|
|
|
|
2013-05-09 17:36:45 -07:00
|
|
|
#[test]
|
2013-07-22 13:57:40 -07:00
|
|
|
fn exclusive_new_arc() {
|
2013-05-24 19:35:29 -07:00
|
|
|
unsafe {
|
|
|
|
let mut futures = ~[];
|
2013-05-09 17:36:45 -07:00
|
|
|
|
2013-05-24 19:35:29 -07:00
|
|
|
let num_tasks = 10;
|
|
|
|
let count = 10;
|
2013-05-09 17:36:45 -07:00
|
|
|
|
2013-07-22 13:57:40 -07:00
|
|
|
let total = Exclusive::new(~0);
|
2013-05-09 17:36:45 -07:00
|
|
|
|
2013-08-03 12:45:23 -04:00
|
|
|
for _ in range(0u, num_tasks) {
|
2013-05-24 19:35:29 -07:00
|
|
|
let total = total.clone();
|
2014-03-09 14:58:32 -07:00
|
|
|
let (tx, rx) = channel();
|
|
|
|
futures.push(rx);
|
2013-05-09 17:36:45 -07:00
|
|
|
|
2014-01-26 22:42:26 -05:00
|
|
|
task::spawn(proc() {
|
2013-08-03 12:45:23 -04:00
|
|
|
for _ in range(0u, count) {
|
2013-11-20 14:17:12 -08:00
|
|
|
total.with(|count| **count += 1);
|
2013-05-09 17:36:45 -07:00
|
|
|
}
|
2014-03-09 14:58:32 -07:00
|
|
|
tx.send(());
|
2014-01-26 22:42:26 -05:00
|
|
|
});
|
2013-05-24 19:35:29 -07:00
|
|
|
};
|
2013-05-09 17:36:45 -07:00
|
|
|
|
2013-12-05 18:19:06 -08:00
|
|
|
for f in futures.mut_iter() { f.recv() }
|
2013-05-09 17:36:45 -07:00
|
|
|
|
2013-11-20 14:17:12 -08:00
|
|
|
total.with(|total| assert!(**total == num_tasks * count));
|
2013-05-24 19:35:29 -07:00
|
|
|
}
|
2013-05-09 17:36:45 -07:00
|
|
|
}
|
|
|
|
|
2013-08-19 15:40:37 -07:00
|
|
|
#[test] #[should_fail]
|
2013-07-22 13:57:40 -07:00
|
|
|
fn exclusive_new_poison() {
|
2013-05-24 19:35:29 -07:00
|
|
|
unsafe {
|
2013-07-22 13:57:40 -07:00
|
|
|
// Tests that if one task fails inside of an Exclusive::new, subsequent
|
2013-05-24 19:35:29 -07:00
|
|
|
// accesses will also fail.
|
2013-07-22 13:57:40 -07:00
|
|
|
let x = Exclusive::new(1);
|
2013-05-24 19:35:29 -07:00
|
|
|
let x2 = x.clone();
|
2014-01-30 14:10:53 -08:00
|
|
|
let _ = task::try(proc() {
|
2013-11-20 14:17:12 -08:00
|
|
|
x2.with(|one| assert_eq!(*one, 2))
|
2014-01-26 22:42:26 -05:00
|
|
|
});
|
2013-11-20 14:17:12 -08:00
|
|
|
x.with(|one| assert_eq!(*one, 1));
|
2013-05-09 17:36:45 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|