about summary refs log tree commit diff
path: root/src/libstd/rt/task.rs
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-10-25 10:36:09 -0700
committerbors <bors@rust-lang.org>2013-10-25 10:36:09 -0700
commitbaeed886aa64943ad48121cc3b57dabec12bc835 (patch)
tree28ba7cc4b0c36636e884dd6c45fcd612b25e2b37 /src/libstd/rt/task.rs
parentdeeca5d586bfaa4aa60246f671a8d611d38f6248 (diff)
parente8f72c38f4bf74e7291043917fdd0bae1404b407 (diff)
downloadrust-baeed886aa64943ad48121cc3b57dabec12bc835.tar.gz
rust-baeed886aa64943ad48121cc3b57dabec12bc835.zip
auto merge of #10060 : alexcrichton/rust/cached-stdout, r=brson
Almost all languages provide some form of buffering of the stdout stream, and
this commit adds this feature for rust. A handle to stdout is lazily initialized
in the Task structure as a buffered owned Writer trait object. The buffer
behavior depends on where stdout is directed to. Like C, this line-buffers the
stream when the output goes to a terminal (flushes on newlines), and also like C
this uses a fixed-size buffer when output is not directed at a terminal.

We may decide the fixed-size buffering is overkill, but it certainly does reduce
write syscall counts when piping output elsewhere. This is a *huge* benefit to
any code using logging macros or the printing macros. Formatting emits calls to
`write` very frequently, and to have each of them backed by a write syscall was
very expensive.

In a local benchmark of printing 10000 lines of "what" to stdout, I got the
following timings:

  when |  terminal   |  redirected
----------|---------------|--------
before |  0.575s     |   0.525s
after  |  0.197s     |   0.013s
  C    |  0.019s     |   0.004s

I can also confirm that we're buffering the output appropriately in both
situtations. We're still far slower than C, but I believe much of that has to do
with the "homing" that all tasks due, we're still performing an order of
magnitude more write syscalls than C does.
Diffstat (limited to 'src/libstd/rt/task.rs')
-rw-r--r--src/libstd/rt/task.rs27
1 files changed, 22 insertions, 5 deletions
diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs
index 7bf124ad312..f82eb929a39 100644
--- a/src/libstd/rt/task.rs
+++ b/src/libstd/rt/task.rs
@@ -23,6 +23,7 @@ use option::{Option, Some, None};
 use rt::borrowck;
 use rt::borrowck::BorrowRecord;
 use rt::env;
+use rt::io::Writer;
 use rt::kill::Death;
 use rt::local::Local;
 use rt::logging::StdErrLogger;
@@ -56,7 +57,8 @@ pub struct Task {
     sched: Option<~Scheduler>,
     task_type: TaskType,
     // Dynamic borrowck debugging info
-    borrow_list: Option<~[BorrowRecord]>
+    borrow_list: Option<~[BorrowRecord]>,
+    stdout_handle: Option<~Writer>,
 }
 
 pub enum TaskType {
@@ -141,7 +143,8 @@ impl Task {
             name: None,
             sched: None,
             task_type: SchedTask,
-            borrow_list: None
+            borrow_list: None,
+            stdout_handle: None,
         }
     }
 
@@ -175,7 +178,8 @@ impl Task {
             coroutine: Some(Coroutine::new(stack_pool, stack_size, start)),
             sched: None,
             task_type: GreenTask(Some(home)),
-            borrow_list: None
+            borrow_list: None,
+            stdout_handle: None,
         }
     }
 
@@ -198,7 +202,8 @@ impl Task {
             coroutine: Some(Coroutine::new(stack_pool, stack_size, start)),
             sched: None,
             task_type: GreenTask(Some(home)),
-            borrow_list: None
+            borrow_list: None,
+            stdout_handle: None,
         }
     }
 
@@ -234,6 +239,7 @@ impl Task {
 
             // Run the task main function, then do some cleanup.
             do f.finally {
+
                 // First, destroy task-local storage. This may run user dtors.
                 //
                 // FIXME #8302: Dear diary. I'm so tired and confused.
@@ -257,6 +263,17 @@ impl Task {
 
                 // Destroy remaining boxes. Also may run user dtors.
                 unsafe { cleanup::annihilate(); }
+
+                // Finally flush and destroy any output handles which the task
+                // owns. There are no boxes here, and no user destructors should
+                // run after this any more.
+                match self.stdout_handle.take() {
+                    Some(handle) => {
+                        let mut handle = handle;
+                        handle.flush();
+                    }
+                    None => {}
+                }
             }
         }
 
@@ -331,7 +348,7 @@ impl Task {
 impl Drop for Task {
     fn drop(&mut self) {
         rtdebug!("called drop for a task: {}", borrow::to_uint(self));
-        rtassert!(self.destroyed)
+        rtassert!(self.destroyed);
     }
 }