}
match write!(&mut self.dst, "\n") {
Err(e) => panic!("failed to emit error: {}", e),
- _ => ()
+ _ => match self.dst.flush() {
+ Err(e) => panic!("failed to emit error: {}", e),
+ _ => ()
+ }
}
}
}
fn emit_to_destination(rendered_buffer: &Vec<Vec<StyledString>>,
lvl: &Level,
dst: &mut Destination) -> io::Result<()> {
+ use lock;
+
+ // In order to prevent error message interleaving, where multiple error lines get intermixed
+ // when multiple compiler processes error simultaneously, we emit errors with additional
+ // steps.
+ //
+ // On Unix systems, we write into a buffered terminal rather than directly to a terminal. When
+ // the .flush() is called we take the buffer created from the buffered writes and write it at
+ // one shot. Because the Unix systems use ANSI for the colors, which is a text-based styling
+ // scheme, this buffered approach works and maintains the styling.
+ //
+ // On Windows, styling happens through calls to a terminal API. This prevents us from using the
+ // same buffering approach. Instead, we use a global Windows mutex, which we acquire long
+ // enough to output the full error message, then we release.
+ let _buffer_lock = lock::acquire_global_lock("rustc_errors");
for line in rendered_buffer {
for part in line {
dst.apply_style(lvl.clone(), part.style)?;
}
write!(dst, "\n")?;
}
+ dst.flush()?;
Ok(())
}
}
}
+pub type BufferedStderr = term::Terminal<Output = BufferedWriter> + Send;
+
pub enum Destination {
Terminal(Box<term::StderrTerminal>),
+ BufferedTerminal(Box<BufferedStderr>),
Raw(Box<Write + Send>),
}
+/// Buffered writer gives us a way on Unix to buffer up an entire error message before we output
+/// it. This helps to prevent interleaving of multiple error messages when multiple compiler
+/// processes error simultaneously
+pub struct BufferedWriter {
+ buffer: Vec<u8>,
+}
+
+impl BufferedWriter {
+ // note: we use _new because the conditional compilation at its use site may make this
+ // this function unused on some platforms
+ fn _new() -> BufferedWriter {
+ BufferedWriter {
+ buffer: vec![]
+ }
+ }
+}
+
+impl Write for BufferedWriter {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ for b in buf {
+ self.buffer.push(*b);
+ }
+ Ok(buf.len())
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ let mut stderr = io::stderr();
+ let result = (|| {
+ stderr.write_all(&self.buffer)?;
+ stderr.flush()
+ })();
+ self.buffer.clear();
+ result
+ }
+}
+
impl Destination {
+ #[cfg(not(windows))]
+ /// When not on Windows, prefer the buffered terminal so that we can buffer an entire error
+ /// to be emitted at one time.
+ fn from_stderr() -> Destination {
+ let stderr: Option<Box<BufferedStderr>> =
+ term::TerminfoTerminal::new(BufferedWriter::_new())
+ .map(|t| Box::new(t) as Box<BufferedStderr>);
+
+ match stderr {
+ Some(t) => BufferedTerminal(t),
+ None => Raw(Box::new(io::stderr())),
+ }
+ }
+
+ #[cfg(windows)]
+ /// Return a normal, unbuffered terminal when on Windows.
fn from_stderr() -> Destination {
- match term::stderr() {
+ let stderr: Option<Box<term::StderrTerminal>> =
+ term::TerminfoTerminal::new(io::stderr())
+ .map(|t| Box::new(t) as Box<term::StderrTerminal>)
+ .or_else(|| term::WinConsole::new(io::stderr()).ok()
+ .map(|t| Box::new(t) as Box<term::StderrTerminal>));
+
+ match stderr {
Some(t) => Terminal(t),
None => Raw(Box::new(io::stderr())),
}
fn start_attr(&mut self, attr: term::Attr) -> io::Result<()> {
match *self {
Terminal(ref mut t) => { t.attr(attr)?; }
+ BufferedTerminal(ref mut t) => { t.attr(attr)?; }
Raw(_) => { }
}
Ok(())
fn reset_attrs(&mut self) -> io::Result<()> {
match *self {
Terminal(ref mut t) => { t.reset()?; }
+ BufferedTerminal(ref mut t) => { t.reset()?; }
Raw(_) => { }
}
Ok(())
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
match *self {
Terminal(ref mut t) => t.write(bytes),
+ BufferedTerminal(ref mut t) => t.write(bytes),
Raw(ref mut w) => w.write(bytes),
}
}
fn flush(&mut self) -> io::Result<()> {
match *self {
Terminal(ref mut t) => t.flush(),
+ BufferedTerminal(ref mut t) => t.flush(),
Raw(ref mut w) => w.flush(),
}
}
--- /dev/null
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Bindings to acquire a global named lock.
+//!
+//! This is intended to be used to synchronize multiple compiler processes to
+//! ensure that we can output complete errors without interleaving on Windows.
+//! Note that this is currently only needed for allowing only one 32-bit MSVC
+//! linker to execute at once on MSVC hosts, so this is only implemented for
+//! `cfg(windows)`. Also note that this may not always be used on Windows,
+//! only when targeting 32-bit MSVC.
+//!
+//! For more information about why this is necessary, see where this is called.
+
+use std::any::Any;
+
+#[cfg(windows)]
+#[allow(bad_style)]
+pub fn acquire_global_lock(name: &str) -> Box<Any> {
+ use std::ffi::CString;
+ use std::io;
+
+ type LPSECURITY_ATTRIBUTES = *mut u8;
+ type BOOL = i32;
+ type LPCSTR = *const u8;
+ type HANDLE = *mut u8;
+ type DWORD = u32;
+
+ const INFINITE: DWORD = !0;
+ const WAIT_OBJECT_0: DWORD = 0;
+ const WAIT_ABANDONED: DWORD = 0x00000080;
+
+ extern "system" {
+ fn CreateMutexA(lpMutexAttributes: LPSECURITY_ATTRIBUTES,
+ bInitialOwner: BOOL,
+ lpName: LPCSTR) -> HANDLE;
+ fn WaitForSingleObject(hHandle: HANDLE,
+ dwMilliseconds: DWORD) -> DWORD;
+ fn ReleaseMutex(hMutex: HANDLE) -> BOOL;
+ fn CloseHandle(hObject: HANDLE) -> BOOL;
+ }
+
+ struct Handle(HANDLE);
+
+ impl Drop for Handle {
+ fn drop(&mut self) {
+ unsafe {
+ CloseHandle(self.0);
+ }
+ }
+ }
+
+ struct Guard(Handle);
+
+ impl Drop for Guard {
+ fn drop(&mut self) {
+ unsafe {
+ ReleaseMutex((self.0).0);
+ }
+ }
+ }
+
+ let cname = CString::new(name).unwrap();
+ unsafe {
+ // Create a named mutex, with no security attributes and also not
+ // acquired when we create it.
+ //
+ // This will silently create one if it doesn't already exist, or it'll
+ // open up a handle to one if it already exists.
+ let mutex = CreateMutexA(0 as *mut _, 0, cname.as_ptr() as *const u8);
+ if mutex.is_null() {
+ panic!("failed to create global mutex named `{}`: {}", name,
+ io::Error::last_os_error());
+ }
+ let mutex = Handle(mutex);
+
+ // Acquire the lock through `WaitForSingleObject`.
+ //
+ // A return value of `WAIT_OBJECT_0` means we successfully acquired it.
+ //
+ // A return value of `WAIT_ABANDONED` means that the previous holder of
+ // the thread exited without calling `ReleaseMutex`. This can happen,
+ // for example, when the compiler crashes or is interrupted via ctrl-c
+ // or the like. In this case, however, we are still transferred
+ // ownership of the lock so we continue.
+ //
+ // If an error happens.. well... that's surprising!
+ match WaitForSingleObject(mutex.0, INFINITE) {
+ WAIT_OBJECT_0 | WAIT_ABANDONED => {}
+ code => {
+ panic!("WaitForSingleObject failed on global mutex named \
+ `{}`: {} (ret={:x})", name,
+ io::Error::last_os_error(), code);
+ }
+ }
+
+ // Return a guard which will call `ReleaseMutex` when dropped.
+ Box::new(Guard(mutex))
+ }
+}
+
+#[cfg(unix)]
+pub fn acquire_global_lock(_name: &str) -> Box<Any> {
+ Box::new(())
+}