1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use clone::Clone;
12 use kinds::Send;
13 use sync::arc::UnsafeArc;
14 use unstable::mutex::NativeMutex;
15
16 struct ExData<T> {
17 lock: NativeMutex,
18 failed: bool,
19 data: T,
20 }
21
22 /**
23 * An arc over mutable data that is protected by a lock. For library use only.
24 *
25 * # Safety note
26 *
27 * This uses a pthread mutex, not one that's aware of the userspace scheduler.
28 * The user of an Exclusive must be careful not to invoke any functions that may
29 * reschedule the task while holding the lock, or deadlock may result. If you
30 * need to block or deschedule while accessing shared state, use extra::sync::RWArc.
31 */
32 pub struct Exclusive<T> {
33 x: UnsafeArc<ExData<T>>
34 }
35
36 impl<T:Send> Clone for Exclusive<T> {
37 // Duplicate an Exclusive Arc, as std::arc::clone.
38 fn clone(&self) -> Exclusive<T> {
39 Exclusive { x: self.x.clone() }
40 }
41 }
42
43 impl<T:Send> Exclusive<T> {
44 pub fn new(user_data: T) -> Exclusive<T> {
45 let data = ExData {
46 lock: unsafe {NativeMutex::new()},
47 failed: false,
48 data: user_data
49 };
50 Exclusive {
51 x: UnsafeArc::new(data)
52 }
53 }
54
55 // Exactly like sync::MutexArc.access(). Same reason for being
56 // unsafe.
57 //
58 // Currently, scheduling operations (i.e., descheduling, receiving on a pipe,
59 // accessing the provided condition variable) are prohibited while inside
60 // the Exclusive. Supporting that is a work in progress.
61 #[inline]
62 pub unsafe fn with<U>(&self, f: |x: &mut T| -> U) -> U {
63 let rec = self.x.get();
64 let _l = (*rec).lock.lock();
65 if (*rec).failed {
66 fail!("Poisoned Exclusive::new - another task failed inside!");
67 }
68 (*rec).failed = true;
69 let result = f(&mut (*rec).data);
70 (*rec).failed = false;
71 result
72 }
73
74 #[inline]
75 pub unsafe fn with_imm<U>(&self, f: |x: &T| -> U) -> U {
76 self.with(|x| f(x))
77 }
78
79 #[inline]
80 pub unsafe fn hold_and_signal(&self, f: |x: &mut T|) {
81 let rec = self.x.get();
82 let guard = (*rec).lock.lock();
83 if (*rec).failed {
84 fail!("Poisoned Exclusive::new - another task failed inside!");
85 }
86 (*rec).failed = true;
87 f(&mut (*rec).data);
88 (*rec).failed = false;
89 guard.signal();
90 }
91
92 #[inline]
93 pub unsafe fn hold_and_wait(&self, f: |x: &T| -> bool) {
94 let rec = self.x.get();
95 let l = (*rec).lock.lock();
96 if (*rec).failed {
97 fail!("Poisoned Exclusive::new - another task failed inside!");
98 }
99 (*rec).failed = true;
100 let result = f(&(*rec).data);
101 (*rec).failed = false;
102 if result {
103 l.wait();
104 }
105 }
106 }
107
108 #[cfg(test)]
109 mod tests {
110 use option::*;
111 use prelude::*;
112 use super::Exclusive;
113 use task;
114
115 #[test]
116 fn exclusive_new_arc() {
117 unsafe {
118 let mut futures = Vec::new();
119
120 let num_tasks = 10;
121 let count = 10;
122
123 let total = Exclusive::new(box 0);
124
125 for _ in range(0u, num_tasks) {
126 let total = total.clone();
127 let (tx, rx) = channel();
128 futures.push(rx);
129
130 task::spawn(proc() {
131 for _ in range(0u, count) {
132 total.with(|count| **count += 1);
133 }
134 tx.send(());
135 });
136 };
137
138 for f in futures.mut_iter() { f.recv() }
139
140 total.with(|total| assert!(**total == num_tasks * count));
141 }
142 }
143
144 #[test] #[should_fail]
145 fn exclusive_new_poison() {
146 unsafe {
147 // Tests that if one task fails inside of an Exclusive::new, subsequent
148 // accesses will also fail.
149 let x = Exclusive::new(1);
150 let x2 = x.clone();
151 let _ = task::try(proc() {
152 x2.with(|one| assert_eq!(*one, 2))
153 });
154 x.with(|one| assert_eq!(*one, 1));
155 }
156 }
157 }