Skip to content

Commit aa84b40

Browse files
committed
Introduce SyncError
SyncError provides adaption to wrap around error_chain, both where we use it, and if any libraries rustup is using use it. Once introduced we can start incrementally migrating function and error at at time to anyhow without requiring a single big bang commit.
1 parent fdb85c4 commit aa84b40

File tree

1 file changed

+140
-5
lines changed

1 file changed

+140
-5
lines changed

src/errors.rs

+140-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
#![allow(clippy::large_enum_variant)]
22
#![allow(deprecated)] // because of `Error::description` deprecation in `error_chain`
33

4-
use crate::dist::dist::Profile;
5-
use crate::dist::manifest::{Component, Manifest};
6-
use crate::dist::temp;
7-
use crate::{component_for_bin, Toolchain};
8-
use error_chain::error_chain;
94
use std::ffi::OsString;
5+
use std::fmt::{self, Debug, Display};
106
use std::io::{self, Write};
117
use std::path::PathBuf;
8+
use std::sync::{Arc, Mutex, MutexGuard, Weak};
9+
10+
use error_chain::error_chain;
1211
use url::Url;
1312

13+
use crate::dist::dist::Profile;
14+
use crate::dist::manifest::{Component, Manifest};
15+
use crate::dist::temp;
16+
use crate::{component_for_bin, Toolchain};
17+
1418
pub const TOOLSTATE_MSG: &str =
1519
"If you require these components, please install and use the latest successful build version,\n\
1620
which you can find at <https://rust-lang.github.io/rustup-components-history>.\n\nAfter determining \
@@ -415,6 +419,137 @@ error_chain! {
415419
}
416420
}
417421

422+
/// Inspired by failure::SyncFailure, but not identical.
423+
///
424+
/// SyncError does not grant full safety: it will panic when errors are used
425+
/// across threads (e.g. by threaded error logging libraries). This could be
426+
/// fixed, but as we don't do that within rustup, it is not needed. If using
427+
/// this code elsewhere, just hunt down and remove the unchecked unwraps.
428+
pub struct SyncError<T: 'static> {
429+
inner: Arc<Mutex<T>>,
430+
proxy: Option<CauseProxy<T>>,
431+
}
432+
433+
impl<T: std::error::Error + 'static> SyncError<T> {
434+
pub fn new(err: T) -> Self {
435+
let arc = Arc::new(Mutex::new(err));
436+
let proxy = match arc.lock().unwrap().source() {
437+
None => None,
438+
Some(source) => Some(CauseProxy::new(source, Arc::downgrade(&arc), 0)),
439+
};
440+
441+
SyncError { inner: arc, proxy }
442+
}
443+
444+
pub fn maybe<R>(r: std::result::Result<R, T>) -> std::result::Result<R, Self> {
445+
match r {
446+
Ok(v) => Ok(v),
447+
Err(e) => Err(SyncError::new(e)),
448+
}
449+
}
450+
451+
pub fn unwrap(self) -> T {
452+
Arc::try_unwrap(self.inner).unwrap().into_inner().unwrap()
453+
}
454+
455+
pub fn as_ref(&self) -> MutexGuard<'_, T> {
456+
Arc::as_ref(&self.inner).lock().unwrap()
457+
}
458+
}
459+
460+
impl<T: std::error::Error + 'static> std::error::Error for SyncError<T> {
461+
#[cfg(backtrace)]
462+
fn backtrace(&self) -> Option<&Backtrace> {}
463+
464+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
465+
self.proxy.as_ref().map(|x| x as _)
466+
}
467+
}
468+
469+
impl<T> Display for SyncError<T>
470+
where
471+
T: Display,
472+
{
473+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
474+
self.inner.lock().unwrap().fmt(f)
475+
}
476+
}
477+
478+
impl<T> Debug for SyncError<T>
479+
where
480+
T: Debug,
481+
{
482+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
483+
self.inner.lock().unwrap().fmt(f)
484+
}
485+
}
486+
487+
struct CauseProxy<T: 'static> {
488+
inner: Weak<Mutex<T>>,
489+
next: Option<Box<CauseProxy<T>>>,
490+
depth: u32,
491+
}
492+
493+
impl<T: std::error::Error> CauseProxy<T> {
494+
fn new(err: &dyn std::error::Error, weak: Weak<Mutex<T>>, depth: u32) -> Self {
495+
// Can't allocate an object, or mutate the proxy safely during source(),
496+
// so we just take the hit at construction, recursively. We can't hold
497+
// references outside the mutex at all, so instead we remember how many
498+
// steps to get to this proxy. And if some error chain plays tricks, the
499+
// user gets both pieces.
500+
CauseProxy {
501+
inner: weak.clone(),
502+
depth,
503+
next: match err.source() {
504+
None => None,
505+
Some(source) => Some(Box::new(CauseProxy::new(source, weak, depth + 1))),
506+
},
507+
}
508+
}
509+
510+
fn with_instance<R, F>(&self, f: F) -> R
511+
where
512+
F: FnOnce(&(dyn std::error::Error + 'static)) -> R,
513+
{
514+
let arc = self.inner.upgrade().unwrap();
515+
{
516+
let e = arc.lock().unwrap();
517+
let mut source = e.source().unwrap();
518+
for _ in 0..self.depth {
519+
source = source.source().unwrap();
520+
}
521+
f(source)
522+
}
523+
}
524+
}
525+
526+
impl<T: std::error::Error + 'static> std::error::Error for CauseProxy<T> {
527+
#[cfg(backtrace)]
528+
fn backtrace(&self) -> Option<&Backtrace> {}
529+
530+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
531+
self.next.as_ref().map(|x| x as _)
532+
}
533+
}
534+
535+
impl<T> Display for CauseProxy<T>
536+
where
537+
T: Display + std::error::Error,
538+
{
539+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
540+
self.with_instance(|i| std::fmt::Display::fmt(&i, f))
541+
}
542+
}
543+
544+
impl<T> Debug for CauseProxy<T>
545+
where
546+
T: Debug + std::error::Error,
547+
{
548+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
549+
self.with_instance(|i| std::fmt::Debug::fmt(&i, f))
550+
}
551+
}
552+
418553
fn valid_profile_names() -> String {
419554
Profile::names()
420555
.iter()

0 commit comments

Comments
 (0)