|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
11 | 11 | use cast;
|
12 |
| -use cell::Cell; |
13 | 12 | use comm;
|
14 | 13 | use ptr;
|
15 | 14 | use option::{Option,Some,None};
|
@@ -70,6 +69,35 @@ unsafe fn new_inner<T: Send>(data: T, refcount: uint) -> *mut ArcData<T> {
|
70 | 69 | cast::transmute(data)
|
71 | 70 | }
|
72 | 71 |
|
| 72 | +/// A helper object used by `UnsafeArc::unwrap`. |
| 73 | +struct ChannelAndDataGuard<T> { |
| 74 | + channel: Option<comm::ChanOne<bool>>, |
| 75 | + data: Option<~ArcData<T>>, |
| 76 | +} |
| 77 | + |
| 78 | +#[unsafe_destructor] |
| 79 | +impl<T> Drop for ChannelAndDataGuard<T> { |
| 80 | + fn drop(&mut self) { |
| 81 | + if task::failing() { |
| 82 | + // Killed during wait. Because this might happen while |
| 83 | + // someone else still holds a reference, we can't free |
| 84 | + // the data now; the "other" last refcount will free it. |
| 85 | + unsafe { |
| 86 | + let channel = self.channel.take_unwrap(); |
| 87 | + let data = self.data.take_unwrap(); |
| 88 | + channel.send(false); |
| 89 | + cast::forget(data); |
| 90 | + } |
| 91 | + } |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +impl<T> ChannelAndDataGuard<T> { |
| 96 | + fn unwrap(mut self) -> (comm::ChanOne<bool>, ~ArcData<T>) { |
| 97 | + (self.channel.take_unwrap(), self.data.take_unwrap()) |
| 98 | + } |
| 99 | +} |
| 100 | + |
73 | 101 | impl<T: Send> UnsafeArc<T> {
|
74 | 102 | pub fn new(data: T) -> UnsafeArc<T> {
|
75 | 103 | unsafe { UnsafeArc { data: new_inner(data, 1) } }
|
@@ -160,32 +188,19 @@ impl<T: Send> UnsafeArc<T> {
|
160 | 188 | data.data.take_unwrap()
|
161 | 189 | } else {
|
162 | 190 | // The *next* person who sees the refcount hit 0 will wake us.
|
163 |
| - let p1 = Cell::new(p1); // argh |
164 |
| - // Unlike the above one, this cell is necessary. It will get |
165 |
| - // taken either in the do block or in the finally block. |
166 |
| - let c2_and_data = Cell::new((c2,data)); |
167 |
| - (|| { |
168 |
| - p1.take().recv(); |
169 |
| - // Got here. Back in the 'unkillable' without getting killed. |
170 |
| - let (c2, data) = c2_and_data.take(); |
171 |
| - c2.send(true); |
172 |
| - // FIXME(#3224): it should be like this |
173 |
| - // let ~ArcData { data: user_data, _ } = data; |
174 |
| - // user_data |
175 |
| - let mut data = data; |
176 |
| - data.data.take_unwrap() |
177 |
| - }).finally(|| { |
178 |
| - if task::failing() { |
179 |
| - // Killed during wait. Because this might happen while |
180 |
| - // someone else still holds a reference, we can't free |
181 |
| - // the data now; the "other" last refcount will free it. |
182 |
| - let (c2, data) = c2_and_data.take(); |
183 |
| - c2.send(false); |
184 |
| - cast::forget(data); |
185 |
| - } else { |
186 |
| - assert!(c2_and_data.is_empty()); |
187 |
| - } |
188 |
| - }) |
| 191 | + let c2_and_data = ChannelAndDataGuard { |
| 192 | + channel: Some(c2), |
| 193 | + data: Some(data), |
| 194 | + }; |
| 195 | + p1.recv(); |
| 196 | + // Got here. Back in the 'unkillable' without getting killed. |
| 197 | + let (c2, data) = c2_and_data.unwrap(); |
| 198 | + c2.send(true); |
| 199 | + // FIXME(#3224): it should be like this |
| 200 | + // let ~ArcData { data: user_data, _ } = data; |
| 201 | + // user_data |
| 202 | + let mut data = data; |
| 203 | + data.data.take_unwrap() |
189 | 204 | }
|
190 | 205 | } else {
|
191 | 206 | // If 'put' returns the server end back to us, we were rejected;
|
|
0 commit comments