]> git.lizzy.rs Git - rust.git/blob - src/libstd/rt/task.rs
auto merge of #10631 : klutzy/rust/win-fixes, r=alexcrichton
[rust.git] / src / libstd / rt / task.rs
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 //! Language-level runtime services that should reasonably expected
12 //! to be available 'everywhere'. Local heaps, GC, unwinding,
13 //! local storage, and logging. Even a 'freestanding' Rust would likely want
14 //! to implement this.
15
16 use super::local_heap::LocalHeap;
17
18 use prelude::*;
19
20 use borrow;
21 use cast::transmute;
22 use cell::Cell;
23 use cleanup;
24 use libc::{c_void, uintptr_t, c_char, size_t};
25 use local_data;
26 use option::{Option, Some, None};
27 use rt::borrowck::BorrowRecord;
28 use rt::borrowck;
29 use rt::context::Context;
30 use rt::context;
31 use rt::env;
32 use io::Writer;
33 use rt::kill::Death;
34 use rt::local::Local;
35 use rt::logging::StdErrLogger;
36 use rt::sched::{Scheduler, SchedHandle};
37 use rt::stack::{StackSegment, StackPool};
38 use send_str::SendStr;
39 use unstable::finally::Finally;
40
41 // The Task struct represents all state associated with a rust
42 // task. There are at this point two primary "subtypes" of task,
43 // however instead of using a subtype we just have a "task_type" field
44 // in the struct. This contains a pointer to another struct that holds
45 // the type-specific state.
46
47 pub struct Task {
48     heap: LocalHeap,
49     priv gc: GarbageCollector,
50     storage: LocalStorage,
51     logger: Option<StdErrLogger>,
52     unwinder: Unwinder,
53     death: Death,
54     destroyed: bool,
55     name: Option<SendStr>,
56     coroutine: Option<Coroutine>,
57     sched: Option<~Scheduler>,
58     task_type: TaskType,
59     // Dynamic borrowck debugging info
60     borrow_list: Option<~[BorrowRecord]>,
61     stdout_handle: Option<~Writer>,
62 }
63
64 pub enum TaskType {
65     GreenTask(Option<SchedHome>),
66     SchedTask
67 }
68
69 /// A coroutine is nothing more than a (register context, stack) pair.
70 pub struct Coroutine {
71     /// The segment of stack on which the task is currently running or
72     /// if the task is blocked, on which the task will resume
73     /// execution.
74     ///
75     /// Servo needs this to be public in order to tell SpiderMonkey
76     /// about the stack bounds.
77     current_stack_segment: StackSegment,
78     /// Always valid if the task is alive and not running.
79     saved_context: Context
80 }
81
82 /// Some tasks have a dedicated home scheduler that they must run on.
83 pub enum SchedHome {
84     AnySched,
85     Sched(SchedHandle)
86 }
87
88 pub struct GarbageCollector;
89 pub struct LocalStorage(Option<local_data::Map>);
90
91 /// Represents the reason for the current unwinding process
92 pub enum UnwindResult {
93     /// The task is ending successfully
94     Success,
95
96     /// The Task is failing with reason `~Any`
97     Failure(~Any),
98 }
99
100 impl UnwindResult {
101     /// Returns `true` if this `UnwindResult` is a failure
102     #[inline]
103     pub fn is_failure(&self) -> bool {
104         match *self {
105             Success => false,
106             Failure(_) => true
107         }
108     }
109
110     /// Returns `true` if this `UnwindResult` is a success
111     #[inline]
112     pub fn is_success(&self) -> bool {
113         match *self {
114             Success => true,
115             Failure(_) => false
116         }
117     }
118 }
119
120 pub struct Unwinder {
121     unwinding: bool,
122     cause: Option<~Any>
123 }
124
125 impl Unwinder {
126     fn to_unwind_result(&mut self) -> UnwindResult {
127         if self.unwinding {
128             Failure(self.cause.take().unwrap())
129         } else {
130             Success
131         }
132     }
133 }
134
135 impl Task {
136
137     // A helper to build a new task using the dynamically found
138     // scheduler and task. Only works in GreenTask context.
139     pub fn build_homed_child(stack_size: Option<uint>,
140                              f: proc(),
141                              home: SchedHome)
142                              -> ~Task {
143         let f = Cell::new(f);
144         let home = Cell::new(home);
145         do Local::borrow |running_task: &mut Task| {
146             let mut sched = running_task.sched.take_unwrap();
147             let new_task = ~running_task.new_child_homed(&mut sched.stack_pool,
148                                                          stack_size,
149                                                          home.take(),
150                                                          f.take());
151             running_task.sched = Some(sched);
152             new_task
153         }
154     }
155
156     pub fn build_child(stack_size: Option<uint>, f: proc()) -> ~Task {
157         Task::build_homed_child(stack_size, f, AnySched)
158     }
159
160     pub fn build_homed_root(stack_size: Option<uint>,
161                             f: proc(),
162                             home: SchedHome)
163                             -> ~Task {
164         let f = Cell::new(f);
165         let home = Cell::new(home);
166         do Local::borrow |running_task: &mut Task| {
167             let mut sched = running_task.sched.take_unwrap();
168             let new_task = ~Task::new_root_homed(&mut sched.stack_pool,
169                                                  stack_size,
170                                                  home.take(),
171                                                  f.take());
172             running_task.sched = Some(sched);
173             new_task
174         }
175     }
176
177     pub fn build_root(stack_size: Option<uint>, f: proc()) -> ~Task {
178         Task::build_homed_root(stack_size, f, AnySched)
179     }
180
181     pub fn new_sched_task() -> Task {
182         Task {
183             heap: LocalHeap::new(),
184             gc: GarbageCollector,
185             storage: LocalStorage(None),
186             logger: None,
187             unwinder: Unwinder { unwinding: false, cause: None },
188             death: Death::new(),
189             destroyed: false,
190             coroutine: Some(Coroutine::empty()),
191             name: None,
192             sched: None,
193             task_type: SchedTask,
194             borrow_list: None,
195             stdout_handle: None,
196         }
197     }
198
199     pub fn new_root(stack_pool: &mut StackPool,
200                     stack_size: Option<uint>,
201                     start: proc()) -> Task {
202         Task::new_root_homed(stack_pool, stack_size, AnySched, start)
203     }
204
205     pub fn new_child(&mut self,
206                      stack_pool: &mut StackPool,
207                      stack_size: Option<uint>,
208                      start: proc()) -> Task {
209         self.new_child_homed(stack_pool, stack_size, AnySched, start)
210     }
211
212     pub fn new_root_homed(stack_pool: &mut StackPool,
213                           stack_size: Option<uint>,
214                           home: SchedHome,
215                           start: proc()) -> Task {
216         Task {
217             heap: LocalHeap::new(),
218             gc: GarbageCollector,
219             storage: LocalStorage(None),
220             logger: None,
221             unwinder: Unwinder { unwinding: false, cause: None },
222             death: Death::new(),
223             destroyed: false,
224             name: None,
225             coroutine: Some(Coroutine::new(stack_pool, stack_size, start)),
226             sched: None,
227             task_type: GreenTask(Some(home)),
228             borrow_list: None,
229             stdout_handle: None,
230         }
231     }
232
233     pub fn new_child_homed(&mut self,
234                            stack_pool: &mut StackPool,
235                            stack_size: Option<uint>,
236                            home: SchedHome,
237                            start: proc()) -> Task {
238         Task {
239             heap: LocalHeap::new(),
240             gc: GarbageCollector,
241             storage: LocalStorage(None),
242             logger: None,
243             unwinder: Unwinder { unwinding: false, cause: None },
244             death: Death::new(),
245             destroyed: false,
246             name: None,
247             coroutine: Some(Coroutine::new(stack_pool, stack_size, start)),
248             sched: None,
249             task_type: GreenTask(Some(home)),
250             borrow_list: None,
251             stdout_handle: None,
252         }
253     }
254
255     pub fn give_home(&mut self, new_home: SchedHome) {
256         match self.task_type {
257             GreenTask(ref mut home) => {
258                 *home = Some(new_home);
259             }
260             SchedTask => {
261                 rtabort!("type error: used SchedTask as GreenTask");
262             }
263         }
264     }
265
266     pub fn take_unwrap_home(&mut self) -> SchedHome {
267         match self.task_type {
268             GreenTask(ref mut home) => {
269                 let out = home.take_unwrap();
270                 return out;
271             }
272             SchedTask => {
273                 rtabort!("type error: used SchedTask as GreenTask");
274             }
275         }
276     }
277
278     pub fn run(&mut self, f: ||) {
279         rtdebug!("run called on task: {}", borrow::to_uint(self));
280
281         // The only try/catch block in the world. Attempt to run the task's
282         // client-specified code and catch any failures.
283         do self.unwinder.try {
284
285             // Run the task main function, then do some cleanup.
286             do f.finally {
287
288                 // First, destroy task-local storage. This may run user dtors.
289                 //
290                 // FIXME #8302: Dear diary. I'm so tired and confused.
291                 // There's some interaction in rustc between the box
292                 // annihilator and the TLS dtor by which TLS is
293                 // accessed from annihilated box dtors *after* TLS is
294                 // destroyed. Somehow setting TLS back to null, as the
295                 // old runtime did, makes this work, but I don't currently
296                 // understand how. I would expect that, if the annihilator
297                 // reinvokes TLS while TLS is uninitialized, that
298                 // TLS would be reinitialized but never destroyed,
299                 // but somehow this works. I have no idea what's going
300                 // on but this seems to make things magically work. FML.
301                 //
302                 // (added after initial comment) A possible interaction here is
303                 // that the destructors for the objects in TLS themselves invoke
304                 // TLS, or possibly some destructors for those objects being
305                 // annihilated invoke TLS. Sadly these two operations seemed to
306                 // be intertwined, and miraculously work for now...
307                 self.storage.take();
308
309                 // Destroy remaining boxes. Also may run user dtors.
310                 unsafe { cleanup::annihilate(); }
311
312                 // Finally flush and destroy any output handles which the task
313                 // owns. There are no boxes here, and no user destructors should
314                 // run after this any more.
315                 match self.stdout_handle.take() {
316                     Some(handle) => {
317                         let mut handle = handle;
318                         handle.flush();
319                     }
320                     None => {}
321                 }
322                 self.logger.take();
323             }
324         }
325
326         // Cleanup the dynamic borrowck debugging info
327         borrowck::clear_task_borrow_list();
328
329         self.death.collect_failure(self.unwinder.to_unwind_result());
330         self.destroyed = true;
331     }
332
333     // New utility functions for homes.
334
335     pub fn is_home_no_tls(&self, sched: &~Scheduler) -> bool {
336         match self.task_type {
337             GreenTask(Some(AnySched)) => { false }
338             GreenTask(Some(Sched(SchedHandle { sched_id: ref id, _}))) => {
339                 *id == sched.sched_id()
340             }
341             GreenTask(None) => {
342                 rtabort!("task without home");
343             }
344             SchedTask => {
345                 // Awe yea
346                 rtabort!("type error: expected: GreenTask, found: SchedTask");
347             }
348         }
349     }
350
351     pub fn homed(&self) -> bool {
352         match self.task_type {
353             GreenTask(Some(AnySched)) => { false }
354             GreenTask(Some(Sched(SchedHandle { _ }))) => { true }
355             GreenTask(None) => {
356                 rtabort!("task without home");
357             }
358             SchedTask => {
359                 rtabort!("type error: expected: GreenTask, found: SchedTask");
360             }
361         }
362     }
363
364     // Grab both the scheduler and the task from TLS and check if the
365     // task is executing on an appropriate scheduler.
366     pub fn on_appropriate_sched() -> bool {
367         do Local::borrow |task: &mut Task| {
368             let sched_id = task.sched.get_ref().sched_id();
369             let sched_run_anything = task.sched.get_ref().run_anything;
370             match task.task_type {
371                 GreenTask(Some(AnySched)) => {
372                     rtdebug!("anysched task in sched check ****");
373                     sched_run_anything
374                 }
375                 GreenTask(Some(Sched(SchedHandle { sched_id: ref id, _ }))) => {
376                     rtdebug!("homed task in sched check ****");
377                     *id == sched_id
378                 }
379                 GreenTask(None) => {
380                     rtabort!("task without home");
381                 }
382                 SchedTask => {
383                     rtabort!("type error: expected: GreenTask, found: SchedTask");
384                 }
385             }
386         }
387     }
388 }
389
390 impl Drop for Task {
391     fn drop(&mut self) {
392         rtdebug!("called drop for a task: {}", borrow::to_uint(self));
393         rtassert!(self.destroyed);
394     }
395 }
396
397 // Coroutines represent nothing more than a context and a stack
398 // segment.
399
400 impl Coroutine {
401
402     pub fn new(stack_pool: &mut StackPool,
403                stack_size: Option<uint>,
404                start: proc())
405                -> Coroutine {
406         let stack_size = match stack_size {
407             Some(size) => size,
408             None => env::min_stack()
409         };
410         let start = Coroutine::build_start_wrapper(start);
411         let mut stack = stack_pool.take_segment(stack_size);
412         let initial_context = Context::new(start, &mut stack);
413         Coroutine {
414             current_stack_segment: stack,
415             saved_context: initial_context
416         }
417     }
418
419     pub fn empty() -> Coroutine {
420         Coroutine {
421             current_stack_segment: StackSegment::new(0),
422             saved_context: Context::empty()
423         }
424     }
425
426     fn build_start_wrapper(start: proc()) -> proc() {
427         let start_cell = Cell::new(start);
428         let wrapper: proc() = || {
429             // First code after swap to this new context. Run our
430             // cleanup job.
431             unsafe {
432
433                 // Again - might work while safe, or it might not.
434                 do Local::borrow |sched: &mut Scheduler| {
435                     sched.run_cleanup_job();
436                 }
437
438                 // To call the run method on a task we need a direct
439                 // reference to it. The task is in TLS, so we can
440                 // simply unsafe_borrow it to get this reference. We
441                 // need to still have the task in TLS though, so we
442                 // need to unsafe_borrow.
443                 let task: *mut Task = Local::unsafe_borrow();
444
445                 do (*task).run {
446                     // N.B. Removing `start` from the start wrapper
447                     // closure by emptying a cell is critical for
448                     // correctness. The ~Task pointer, and in turn the
449                     // closure used to initialize the first call
450                     // frame, is destroyed in the scheduler context,
451                     // not task context. So any captured closures must
452                     // not contain user-definable dtors that expect to
453                     // be in task context. By moving `start` out of
454                     // the closure, all the user code goes our of
455                     // scope while the task is still running.
456                     let start = start_cell.take();
457                     start();
458                 };
459             }
460
461             // We remove the sched from the Task in TLS right now.
462             let sched: ~Scheduler = Local::take();
463             // ... allowing us to give it away when performing a
464             // scheduling operation.
465             sched.terminate_current_task()
466         };
467         return wrapper;
468     }
469
470     /// Destroy coroutine and try to reuse stack segment.
471     pub fn recycle(self, stack_pool: &mut StackPool) {
472         match self {
473             Coroutine { current_stack_segment, _ } => {
474                 stack_pool.give_segment(current_stack_segment);
475             }
476         }
477     }
478
479 }
480
481
482 // Just a sanity check to make sure we are catching a Rust-thrown exception
483 static UNWIND_TOKEN: uintptr_t = 839147;
484
485 impl Unwinder {
486     pub fn try(&mut self, f: ||) {
487         use unstable::raw::Closure;
488
489         unsafe {
490             let closure: Closure = transmute(f);
491             let code = transmute(closure.code);
492             let env = transmute(closure.env);
493
494             let token = rust_try(try_fn, code, env);
495             assert!(token == 0 || token == UNWIND_TOKEN);
496         }
497
498         extern fn try_fn(code: *c_void, env: *c_void) {
499             unsafe {
500                 let closure: Closure = Closure {
501                     code: transmute(code),
502                     env: transmute(env),
503                 };
504                 let closure: || = transmute(closure);
505                 closure();
506             }
507         }
508
509         extern {
510             fn rust_try(f: extern "C" fn(*c_void, *c_void),
511                         code: *c_void,
512                         data: *c_void) -> uintptr_t;
513         }
514     }
515
516     pub fn begin_unwind(&mut self, cause: ~Any) -> ! {
517         self.unwinding = true;
518         self.cause = Some(cause);
519         unsafe {
520             rust_begin_unwind(UNWIND_TOKEN);
521             return transmute(());
522         }
523         extern {
524             fn rust_begin_unwind(token: uintptr_t);
525         }
526     }
527 }
528
529 /// This function is invoked from rust's current __morestack function. Segmented
530 /// stacks are currently not enabled as segmented stacks, but rather one giant
531 /// stack segment. This means that whenever we run out of stack, we want to
532 /// truly consider it to be stack overflow rather than allocating a new stack.
533 #[no_mangle]      // - this is called from C code
534 #[no_split_stack] // - it would be sad for this function to trigger __morestack
535 #[doc(hidden)]    // - Function must be `pub` to get exported, but it's
536                   //   irrelevant for documentation purposes.
537 pub extern "C" fn rust_stack_exhausted() {
538     use rt::in_green_task_context;
539     use rt::task::Task;
540     use rt::local::Local;
541     use unstable::intrinsics;
542
543     unsafe {
544         // We're calling this function because the stack just ran out. We need
545         // to call some other rust functions, but if we invoke the functions
546         // right now it'll just trigger this handler being called again. In
547         // order to alleviate this, we move the stack limit to be inside of the
548         // red zone that was allocated for exactly this reason.
549         let limit = context::get_sp_limit();
550         context::record_sp_limit(limit - context::RED_ZONE / 2);
551
552         // This probably isn't the best course of action. Ideally one would want
553         // to unwind the stack here instead of just aborting the entire process.
554         // This is a tricky problem, however. There's a few things which need to
555         // be considered:
556         //
557         //  1. We're here because of a stack overflow, yet unwinding will run
558         //     destructors and hence arbitrary code. What if that code overflows
559         //     the stack? One possibility is to use the above allocation of an
560         //     extra 10k to hope that we don't hit the limit, and if we do then
561         //     abort the whole program. Not the best, but kind of hard to deal
562         //     with unless we want to switch stacks.
563         //
564         //  2. LLVM will optimize functions based on whether they can unwind or
565         //     not. It will flag functions with 'nounwind' if it believes that
566         //     the function cannot trigger unwinding, but if we do unwind on
567         //     stack overflow then it means that we could unwind in any function
568         //     anywhere. We would have to make sure that LLVM only places the
569         //     nounwind flag on functions which don't call any other functions.
570         //
571         //  3. The function that overflowed may have owned arguments. These
572         //     arguments need to have their destructors run, but we haven't even
573         //     begun executing the function yet, so unwinding will not run the
574         //     any landing pads for these functions. If this is ignored, then
575         //     the arguments will just be leaked.
576         //
577         // Exactly what to do here is a very delicate topic, and is possibly
578         // still up in the air for what exactly to do. Some relevant issues:
579         //
580         //  #3555 - out-of-stack failure leaks arguments
581         //  #3695 - should there be a stack limit?
582         //  #9855 - possible strategies which could be taken
583         //  #9854 - unwinding on windows through __morestack has never worked
584         //  #2361 - possible implementation of not using landing pads
585
586         if in_green_task_context() {
587             do Local::borrow |task: &mut Task| {
588                 let n = task.name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
589
590                 // See the message below for why this is not emitted to the
591                 // task's logger. This has the additional conundrum of the
592                 // logger may not be initialized just yet, meaning that an FFI
593                 // call would happen to initialized it (calling out to libuv),
594                 // and the FFI call needs 2MB of stack when we just ran out.
595                 rterrln!("task '{}' has overflowed its stack", n);
596             }
597         } else {
598             rterrln!("stack overflow in non-task context");
599         }
600
601         intrinsics::abort();
602     }
603 }
604
605 /// This is the entry point of unwinding for things like lang items and such.
606 /// The arguments are normally generated by the compiler, and need to
607 /// have static lifetimes.
608 pub fn begin_unwind_raw(msg: *c_char, file: *c_char, line: size_t) -> ! {
609     use c_str::CString;
610     use cast::transmute;
611
612     #[inline]
613     fn static_char_ptr(p: *c_char) -> &'static str {
614         let s = unsafe { CString::new(p, false) };
615         match s.as_str() {
616             Some(s) => unsafe { transmute::<&str, &'static str>(s) },
617             None => rtabort!("message wasn't utf8?")
618         }
619     }
620
621     let msg = static_char_ptr(msg);
622     let file = static_char_ptr(file);
623
624     begin_unwind(msg, file, line as uint)
625 }
626
627 /// This is the entry point of unwinding for fail!() and assert!().
628 pub fn begin_unwind<M: Any + Send>(msg: M, file: &'static str, line: uint) -> ! {
629     use any::AnyRefExt;
630     use rt::in_green_task_context;
631     use rt::local::Local;
632     use rt::task::Task;
633     use str::Str;
634     use unstable::intrinsics;
635
636     unsafe {
637         let task: *mut Task;
638         // Note that this should be the only allocation performed in this block.
639         // Currently this means that fail!() on OOM will invoke this code path,
640         // but then again we're not really ready for failing on OOM anyway. If
641         // we do start doing this, then we should propagate this allocation to
642         // be performed in the parent of this task instead of the task that's
643         // failing.
644         let msg = ~msg as ~Any;
645
646         {
647             //let msg: &Any = msg;
648             let msg_s = match msg.as_ref::<&'static str>() {
649                 Some(s) => *s,
650                 None => match msg.as_ref::<~str>() {
651                     Some(s) => s.as_slice(),
652                     None => "~Any",
653                 }
654             };
655
656             if !in_green_task_context() {
657                 rterrln!("failed in non-task context at '{}', {}:{}",
658                          msg_s, file, line);
659                 intrinsics::abort();
660             }
661
662             task = Local::unsafe_borrow();
663             let n = (*task).name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
664
665             // XXX: this should no get forcibly printed to the console, this should
666             //      either be sent to the parent task (ideally), or get printed to
667             //      the task's logger. Right now the logger is actually a uvio
668             //      instance, which uses unkillable blocks internally for various
669             //      reasons. This will cause serious trouble if the task is failing
670             //      due to mismanagment of its own kill flag, so calling our own
671             //      logger in its current state is a bit of a problem.
672
673             rterrln!("task '{}' failed at '{}', {}:{}", n, msg_s, file, line);
674
675             if (*task).unwinder.unwinding {
676                 rtabort!("unwinding again");
677             }
678         }
679
680         (*task).unwinder.begin_unwind(msg);
681     }
682 }
683
684 #[cfg(test)]
685 mod test {
686     use super::*;
687     use rt::test::*;
688
689     #[test]
690     fn local_heap() {
691         do run_in_newsched_task() {
692             let a = @5;
693             let b = a;
694             assert!(*a == 5);
695             assert!(*b == 5);
696         }
697     }
698
699     #[test]
700     fn tls() {
701         use local_data;
702         do run_in_newsched_task() {
703             local_data_key!(key: @~str)
704             local_data::set(key, @~"data");
705             assert!(*local_data::get(key, |k| k.map(|k| *k)).unwrap() == ~"data");
706             local_data_key!(key2: @~str)
707             local_data::set(key2, @~"data");
708             assert!(*local_data::get(key2, |k| k.map(|k| *k)).unwrap() == ~"data");
709         }
710     }
711
712     #[test]
713     fn unwind() {
714         do run_in_newsched_task() {
715             let result = spawntask_try(||());
716             rtdebug!("trying first assert");
717             assert!(result.is_ok());
718             let result = spawntask_try(|| fail!());
719             rtdebug!("trying second assert");
720             assert!(result.is_err());
721         }
722     }
723
724     #[test]
725     fn rng() {
726         do run_in_uv_task() {
727             use rand::{rng, Rng};
728             let mut r = rng();
729             let _ = r.next_u32();
730         }
731     }
732
733     #[test]
734     fn logging() {
735         do run_in_uv_task() {
736             info!("here i am. logging in a newsched task");
737         }
738     }
739
740     #[test]
741     fn comm_oneshot() {
742         use comm::*;
743
744         do run_in_newsched_task {
745             let (port, chan) = oneshot();
746             chan.send(10);
747             assert!(port.recv() == 10);
748         }
749     }
750
751     #[test]
752     fn comm_stream() {
753         use comm::*;
754
755         do run_in_newsched_task() {
756             let (port, chan) = stream();
757             chan.send(10);
758             assert!(port.recv() == 10);
759         }
760     }
761
762     #[test]
763     fn comm_shared_chan() {
764         use comm::*;
765
766         do run_in_newsched_task() {
767             let (port, chan) = stream();
768             let chan = SharedChan::new(chan);
769             chan.send(10);
770             assert!(port.recv() == 10);
771         }
772     }
773
774     #[test]
775     fn heap_cycles() {
776         use option::{Option, Some, None};
777
778         do run_in_newsched_task {
779             struct List {
780                 next: Option<@mut List>,
781             }
782
783             let a = @mut List { next: None };
784             let b = @mut List { next: Some(a) };
785
786             a.next = Some(b);
787         }
788     }
789
790     #[test]
791     #[should_fail]
792     fn test_begin_unwind() { begin_unwind("cause", file!(), line!()) }
793 }