1use std::ops::Deref;
4use std::cell::UnsafeCell;
5use std::rc::Rc;
6use std::mem;
7use std::fmt;
8
9use self::Inner::{Evaluated, EvaluationInProgress, Unevaluated, Redirect};
10
11#[doc(hidden)]
25#[macro_export]
26macro_rules! lazy {
27 ($($e: stmt);*) => {
28 $crate::lazy::Thunk::new(move || { $($e);* })
29 }
30}
31
32#[doc(hidden)]
33#[macro_export]
34macro_rules! lazy_val {
35 ($($e: stmt);*) => {
36 $crate::lazy::Thunk::new(move || { value({$($e);*}) })
37 }
38}
39
40#[doc(hidden)]
41#[macro_export]
42macro_rules! lazy_redirect {
43 ($($e: stmt);*) => {
44 $crate::lazy::Thunk::new(move || { redirect({$($e);*}) })
45 }
46}
47
48pub fn strict<T>(v: T) -> Thunk<T> {
49 Thunk::evaluated(v)
50}
51
52pub fn redirect<T>(t: Thunk<T>) -> ThunkResult<T> {
53 ThunkResult::Redirect(t)
54}
55
56pub fn value<T>(v: T) -> ThunkResult<T> {
57 ThunkResult::Value(v)
58}
59
60pub type Lazy<T> = Thunk<T>;
62
63pub struct Thunk<T> (UnsafeCell<Rc<UnsafeCell<Inner<T>>>>);
65
66impl<T> Thunk<T> {
67 pub fn new<F>(producer: F) -> Thunk<T>
79 where F: FnOnce() -> ThunkResult<T> + 'static {
80 Thunk(UnsafeCell::new(Rc::new(UnsafeCell::new(Unevaluated(Producer::new(producer))))))
81 }
82
83 pub fn evaluated(val: T) -> Thunk<T> {
85 Thunk(UnsafeCell::new(Rc::new(UnsafeCell::new(Evaluated(val)))))
86 }
87
88 pub fn force(&self) {
90 loop {
91 match *self.inner() {
92 Evaluated(_) => return,
93 EvaluationInProgress => {
94 panic!("Thunk::force called recursively. (A Thunk tried to force itself while trying to force itself).")
95 },
96 Redirect(ref t) => {
97 self.redirect(t.clone());
98 continue;
99 },
100 Unevaluated(_) => ()
101 };
102 break;
103 }
104
105 match mem::replace(self.inner(), EvaluationInProgress) {
106 Unevaluated(producer) => {
107 *self.inner() = EvaluationInProgress;
108 match producer.invoke() {
109 ThunkResult::Value(x) =>
110 *self.inner() = Evaluated(x),
111 ThunkResult::Redirect(t) => {
112 t.force();
113 *self.inner() = Redirect(t.clone());
114 self.redirect(t);
115 }
116 }
117 }
118 _ => {
119 let x = 42;
120 println!("thats not good {}",x);
121 }
123 }
124 }
125
126 fn inner(&self) -> &mut Inner<T> {
127 match *self {
128 Thunk(ref cell) => unsafe {
129 &mut *(**cell.get()).get()
130 }
131 }
132 }
133
134 fn rc(&self) -> &mut Rc<UnsafeCell<Inner<T>>> {
135 match *self {
136 Thunk(ref cell) => unsafe {
137 &mut *cell.get()
138 }
139 }
140 }
141
142 fn redirect(&self, t: Thunk<T>) {
143 *self.rc() = t.rc().clone();
144 }
145}
146
147impl<T> Deref for Thunk<T> {
148 type Target = T;
149
150 fn deref(&self) -> &T {
151 self.force();
152 match *self.inner() {
153 Evaluated(ref val) => val,
154 _ => unreachable!(),
155 }
156 }
157}
158
159impl<T> Clone for Thunk<T> {
160 fn clone(&self) -> Thunk<T> {
161 Thunk(UnsafeCell::new(self.rc().clone()))
162 }
163}
164
165impl<T> fmt::Debug for Thunk<T>
166 where T: fmt::Debug
167{
168 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
169 f.debug_tuple("Thunk")
170 .field(self.inner())
171 .finish()
172 }
173}
174
175#[derive(Debug)]
177pub enum ThunkResult<T> {
178 Value(T),
179 Redirect(Thunk<T>)
180}
181
182struct Producer<T> {
183 inner: Box<Invoke<T>>
184}
185
186impl<T> fmt::Debug for Producer<T> {
187 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
188 write!(f, "Producer{{...}}")
189 }
190}
191
192impl<T> Producer<T> {
193 fn new<F: FnOnce() -> T + 'static>(f: F) -> Producer<T> {
194 Producer {
195 inner: Box::new(move || {
196 f()
197 }) as Box<Invoke<T>>
198 }
199 }
200
201 fn invoke(self) -> T {
202 self.inner.invoke()
203 }
204}
205
206#[derive(Debug)]
207enum Inner<T> {
208 Evaluated(T),
209 EvaluationInProgress,
210 Unevaluated(Producer<ThunkResult<T>>),
211 Redirect(Thunk<T>),
212}
213
214#[doc(hidden)]
215pub trait Invoke<T> {
216 fn invoke(self: Box<Self>) -> T;
217}
218
219impl<T, F> Invoke<T> for F
220 where F: FnOnce() -> T
221{
222 fn invoke(self: Box<F>) -> T {
223 let f = *self;
224 f()
225 }
226}
227
228#[cfg(test)]
229mod test {
230 use super::{Thunk, value, redirect, strict};
231 use std::sync::{Arc, Mutex};
232 use std::thread;
233
234 #[test]
235 fn test_thunk_should_evaluate_when_accessed() {
236 let val = lazy!(value(7));
237 assert_eq!(*val, 7);
238 }
239
240 #[test]
241 fn test_thunk_should_evaluate_through_redirect() {
242 let val = lazy!(redirect(lazy!(value(7))));
243 assert_eq!(*val, 7);
244 }
245
246 #[test]
247 fn test_thunk_should_evaluate_just_once() {
248 let counter = Arc::new(Mutex::new(0));
249 let counter_clone = counter.clone();
250 let val = lazy!({
251 let mut data = counter.lock().unwrap();
252 *data += 1;
253 value(())
254 });
255 *val;
256 *val;
257 assert_eq!(*counter_clone.lock().unwrap(), 1);
258 }
259
260 #[test]
261 fn test_thunk_should_not_evaluate_if_not_accessed() {
262 let counter = Arc::new(Mutex::new(0));
263 let counter_clone = counter.clone();
264 let _val = lazy!({
265 let mut data = counter.lock().unwrap();
266 *data += 1;
267 value(())
268 });
269 assert_eq!(*counter_clone.lock().unwrap(), 0);
270 }
271
272 #[test]
273 fn test_strict_should_produce_already_evaluated_thunk() {
274 let x = strict(10);
275 assert_eq!(*x, 10);
276 }
277
278 #[test]
279 fn test_drop_internal_data_just_once() {
280 let counter = Arc::new(Mutex::new(0));
281 let counter_clone = counter.clone();
282 let result = thread::spawn(move || {
283 let value = Dropper(counter_clone);
284 let t = Thunk::<()>::new(move || {
285 let _x = &value;
287
288 panic!("Muahahahah")
289 });
290 t.force();
291 }).join();
292
293 match result {
294 Err(_) => {
295 assert_eq!(*counter.lock().unwrap(), 1);
296 },
297 _ => panic!("Unexpected success in spawned task.")
298 }
299 }
300
301 struct Dropper(Arc<Mutex<u64>>);
302
303 impl Drop for Dropper {
304 fn drop(&mut self) {
305 let Dropper(ref count) = *self;
306 *count.lock().unwrap() += 1;
307 }
308 }
309}