rustc-ap-rustc_data_structures 429.0.0

Automatically published version of the package `rustc_data_structures` in the rust-lang/rust repository from commit 314a79cd80ed905f80d24b79fd7acb4c8c72789b The publishing script for this crate lives at: https://github.com/alexcrichton/rustc-auto-publish
use jobserver_crate::{Client, HelperThread, Acquired};
use lazy_static::lazy_static;
use std::sync::{Condvar, Arc, Mutex};
use std::mem;

#[derive(Default)]
struct LockedProxyData {
    /// The number of free thread tokens, this may include the implicit token given to the process
    free: usize,

    /// The number of threads waiting for a token
    waiters: usize,

    /// The number of tokens we requested from the server
    requested: usize,

    /// Stored tokens which will be dropped when we no longer need them
    tokens: Vec<Acquired>,
}

impl LockedProxyData {
    fn request_token(&mut self, thread: &Mutex<HelperThread>) {
        self.requested += 1;
        thread.lock().unwrap().request_token();
    }

    fn release_token(&mut self, cond_var: &Condvar) {
        if self.waiters > 0 {
            self.free += 1;
            cond_var.notify_one();
        } else {
            if self.tokens.is_empty() {
                // We are returning the implicit token
                self.free += 1;
            } else {
                // Return a real token to the server
                self.tokens.pop().unwrap();
            }
        }
    }

    fn take_token(&mut self, thread: &Mutex<HelperThread>) -> bool {
        if self.free > 0 {
            self.free -= 1;
            self.waiters -= 1;

            // We stole some token reqested by someone else
            // Request another one
            if self.requested + self.free < self.waiters {
                self.request_token(thread);
            }

            true
        } else {
            false
        }
    }

    fn new_requested_token(&mut self, token: Acquired, cond_var: &Condvar) {
        self.requested -= 1;

        // Does anything need this token?
        if self.waiters > 0 {
            self.free += 1;
            self.tokens.push(token);
            cond_var.notify_one();
        } else {
            // Otherwise we'll just drop it
            mem::drop(token);
        }
    }
}

#[derive(Default)]
struct ProxyData {
    lock: Mutex<LockedProxyData>,
    cond_var: Condvar,
}

/// A helper type which makes managing jobserver tokens easier.
/// It also allows you to treat the implicit token given to the process
/// in the same manner as requested tokens.
struct Proxy {
    thread: Mutex<HelperThread>,
    data: Arc<ProxyData>,
}

lazy_static! {
    // We can only call `from_env` once per process

    // Note that this is unsafe because it may misinterpret file descriptors
    // on Unix as jobserver file descriptors. We hopefully execute this near
    // the beginning of the process though to ensure we don't get false
    // positives, or in other words we try to execute this before we open
    // any file descriptors ourselves.
    //
    // Pick a "reasonable maximum" if we don't otherwise have
    // a jobserver in our environment, capping out at 32 so we
    // don't take everything down by hogging the process run queue.
    // The fixed number is used to have deterministic compilation
    // across machines.
    //
    // Also note that we stick this in a global because there could be
    // multiple rustc instances in this process, and the jobserver is
    // per-process.
    static ref GLOBAL_CLIENT: Client = unsafe {
        Client::from_env().unwrap_or_else(|| {
            Client::new(32).expect("failed to create jobserver")
        })
    };

    static ref GLOBAL_PROXY: Proxy = {
        let data = Arc::new(ProxyData::default());

        Proxy {
            data: data.clone(),
            thread: Mutex::new(client().into_helper_thread(move |token| {
                data.lock.lock().unwrap().new_requested_token(token.unwrap(), &data.cond_var);
            }).unwrap()),
        }
    };
}

pub fn client() -> Client {
    GLOBAL_CLIENT.clone()
}

pub fn acquire_thread() {
    GLOBAL_PROXY.acquire_token();
}

pub fn release_thread() {
    GLOBAL_PROXY.release_token();
}

impl Proxy {
    fn release_token(&self) {
        self.data.lock.lock().unwrap().release_token(&self.data.cond_var);
    }

    fn acquire_token(&self) {
        let mut data = self.data.lock.lock().unwrap();
        data.waiters += 1;
        if data.take_token(&self.thread) {
            return;
        }
        // Request a token for us
        data.request_token(&self.thread);
        loop {
            data = self.data.cond_var.wait(data).unwrap();
            if data.take_token(&self.thread) {
                return;
            }
        }
    }
}