<commit_before>/* diosix virtual CPU scheduler
 *
 * This is, for now, really really simple.
 * Making it fairer and adaptive to workloads is the ultimate goal.
 *
 * (c) Chris Williams, 2018-2021.
 *
 * See LICENSE for usage and copying.
 */

use super::lock::Mutex;
use alloc::collections::vec_deque::VecDeque;
use hashbrown::hash_map::HashMap;
use platform::timer::TimerValue;
use super::error::Cause;
use super::vcore::{VirtualCore, Priority};
use super::pcore::{self, PhysicalCore, PhysicalCoreID};
use super::hardware;
use super::message;
use super::capsule::{self, CapsuleState};

pub type TimesliceCount = u64;

/* prevent physical CPU time starvation: allow a normal virtual core to run after this number of timeslices
have been spent running high priority virtual cores */
const HIGH_PRIO_TIMESLICES_MAX: TimesliceCount = 10;

/* max how long a virtual core is allowed to run before a scheduling decision is made */
const TIMESLICE_LENGTH: TimerValue = TimerValue::Milliseconds(50);

/* define the shortest time between now and another interrupt and rescheduling decision.
this is to stop supervisor kernels spamming the scheduling system with lots of short reschedulings */
const TIMESLICE_MIN_LENGTH: TimerValue = TimerValue::Milliseconds(5);

/* duration a system maintence core (one that can't run supervisor code) must wait
before looking for fixed work to do. also the length in between application cores can
attempt to perform housekeeping */
const MAINTENANCE_LENGTH: TimerValue = TimerValue::Seconds(5);

/* these are the global wait queues. while each physical CPU core gets its own pair
of high-normal wait queues, virtual cores waiting to be assigned to a physical CPU sit in these global queues.
when a physical CPU runs out of queued virtual cores, it pulls one from these global queues.
a physical CPU core can ask fellow CPUs to push virtual cores onto the global queues via messages */
lazy_static!
{
    static ref GLOBAL_QUEUES: Mutex<ScheduleQueues> = Mutex::new(ScheduleQueues::new());
    static ref WORKLOAD: Mutex<HashMap<PhysicalCoreID, usize>> = Mutex::new(HashMap::new());
    static ref LAST_HOUSEKEEP_CHECK: Mutex<TimerValue> = Mutex::new(TimerValue::Exact(0));
}

#[derive(PartialEq, Clone, Copy, Debug)]
pub enum SearchMode
{
    MustFind, /* when searching for something to run, keep looping until successful */
    CheckOnce /* search just once for something else to run, return to environment otherwise */
}

/* queue a virtual core in global wait list */
pub fn queue(to_queue: VirtualCore)
{
    GLOBAL_QUEUES.lock().queue(to_queue);
}

/* activate preemptive multitasking. each physical CPU core should call this
   to start running workloads - be them user/supervisor or management tasks
   <= returns OK, or error code on failure */
pub fn start() -> Result<(), Cause>
{
    hardware::scheduler_timer_start();
    Ok(())
}

/* make a decision on whether to run another virtual core,
   or return to the currently running core (if possible).
   ping() is called when a scheduler timer IRQ comes in */
pub fn ping()
{
    let time_now = hardware::scheduler_get_timer_now();
    let frequency = hardware::scheduler_get_timer_frequency();
    if time_now.is_none() || frequency.is_none()
    {
        /* check to see if anything needs to run and bail out if
        no timer hardware can be found (and yet we're still getting IRQs?) */
        run_next(SearchMode::CheckOnce);
        return;
    }

    /* get down to the exact timer values */
    let frequency = frequency.unwrap();
    let time_now = time_now.unwrap().to_exact(frequency);

    match pcore::PhysicalCore::this().get_timer_sched_last()
    {
        Some(v) =>
        {
            let timeslice_length = TIMESLICE_LENGTH.to_exact(frequency);
            let mut last_scheduled_at = v.to_exact(frequency);

            /* if the capsule we're running in is valid then perform a time slice check.
               if it's not valid, ensure the capsule is torn down or restarted for this
               virtual core. when all vcores are removed from the capsule, its
               resources will be released and the capsule deleted */
            let capsule_state = capsule::get_current_state();
            match capsule_state
            {
                Some(CapsuleState::Valid) =>
                {
                    /* check to see if we've reached the end of this physical CPU core's
                    time slice. a virtual code has the pcore for TIMESLICE_LENGTH of time
                    before a mandatory scheduling decision is made */
                    if time_now - last_scheduled_at >= timeslice_length
                    {
                        /* it's been a while since we last made a decision, so force one now */
                        run_next(SearchMode::CheckOnce);
                        pcore::PhysicalCore::this().set_timer_sched_last(Some(TimerValue::Exact(time_now)));
                        last_scheduled_at = time_now;
                    }
                },
                _ =>
                {
                    /* it is safe to call destroy_current() and restart_current() multiple times
                       per vcore until the capsule is dead or restarted */
                    if let Err(_e) = match capsule_state
                    {
                        Some(CapsuleState::Dying) => capsule::destroy_current(),
                        Some(CapsuleState::Restarting) => capsule::restart_current(),
                        _ => Ok(())
                    }
                    {
                        hvalert!("BUG: Capsule update failure {:?} in scheduler ({:?})", _e, capsule_state)
                    }

                    /* capsule we're running in is no longer valid so force a reschedule */
                    run_next(SearchMode::MustFind);
                    pcore::PhysicalCore::this().set_timer_sched_last(Some(TimerValue::Exact(time_now)));
                    last_scheduled_at = time_now;
                }
            }

            /* check to make sure timer target is correct for whatever virtual core we're about
            to run. run_next() may have set a new timer irq target, or changed the virtual core
            we're running. there may be a supervisor-level timer IRQ upcoming.
            make sure the physical core timer target value is appropriate. */
            if let Some(timer_target) = hardware::scheduler_get_timer_next_at()
            {
                let mut timer_target = timer_target.to_exact(frequency);

                /* avoid skipping over any pending supervisor timer IRQ: reduce latency between
                capsule timer interrupts being raised and capsule cores scheduled to pick up said IRQs */
                if let Some(supervisor_target) = pcore::PhysicalCore::get_virtualcore_timer_target()
                {
                    timer_target = supervisor_target.to_exact(frequency);
                }

                /* if the target is already behind us, discard it and interrupt at end of this timeslice.
                   if the target is too far ahead, curtail it to the end of this timeslice */
                if timer_target <= time_now || timer_target > last_scheduled_at + timeslice_length
                {
                    timer_target = last_scheduled_at + timeslice_length;
                }

                hardware::scheduler_timer_at(TimerValue::Exact(timer_target));
            }
        },

        /* if not we've not scheduled anything yet, find something to run */
        None =>
        {
            run_next(SearchMode::MustFind);
            pcore::PhysicalCore::this().set_timer_sched_last(Some(TimerValue::Exact(time_now)));
        }
    }
}

/* find something else to run, or return to whatever we were running if allowed.
   call this function when a virtual core's timeslice has expired, or it has crashed
   or stopped running and we can't return to it. this function will return regardless
   if this physical CPU core is unable to run virtual cores.
   => search_mode = define whether or not to continue searching for another
   virtual core to run, or check once to see if something else is waiting */
fn run_next(search_mode: SearchMode)
{
    /* check for housekeeping */
    housekeeping();

    /* if this core can run supervisor-level code then find it some work to do */
    if pcore::PhysicalCore::smode_supported()
    {
        loop
        {
            let mut something_found = true;

            /* check to see if there's anything waiting to be picked up for this
            physical CPU from a global queue. if so, then adopt it so it can get a chance to run */
            match GLOBAL_QUEUES.lock().dequeue()
            {
                /* we've found a virtual CPU core to run, so switch to that */
                Some(orphan) =>
                {
                    let mut workloads =  WORKLOAD.lock();

                    /* increment counter of how many virtual cores this physical CPU core
                    has taken from the global queue */
                    if let Some(count) = workloads.get_mut(&PhysicalCore::get_id())
                    {
                        *count = *count + 1;
                    }
                    else
                    {
                        workloads.insert(PhysicalCore::get_id(), 1);
                    }

                    pcore::context_switch(orphan);
                },

                /* otherwise, try to take a virtual CPU core waiting for this physical CPU core and run it */
                _ => match PhysicalCore::dequeue()
                {
                    Some(virtcore) => pcore::context_switch(virtcore), /* waiting virtual CPU core found, queuing now */
                    _ => something_found = false /* nothing else to run */
                }
            }

            /* if we've found something, or only searching once, exit the search loop */
            if something_found == true || search_mode == SearchMode::CheckOnce
            {
                break;
            }
        }

        /* we've got a virtual core to run. tell the timer system to call us back soon */
        hardware::scheduler_timer_next_in(TIMESLICE_LENGTH);
    }
    else
    {
        hardware::scheduler_timer_next_in(MAINTENANCE_LENGTH); /* we'll be back some time later */
    }
}

/* perform any housekeeping duties defined by the various parts of the system */
fn housekeeping()
{
    /* perform integrity checks */
    #[cfg(feature = "integritychecks")]
    {
        if let Err(val) = pcore::PhysicalCore::integrity_check()
        {
            hvalert!("CPU private stack overflowed (0x{:x}). Halting!", val);
            loop {}
        }
    }

    /* avoid blocking on the house keeping lock */
    if LAST_HOUSEKEEP_CHECK.is_locked() == true
    {
        return;
    }

    let mut last_check = LAST_HOUSEKEEP_CHECK.lock();

    /* only perform housekeeping once every MAINTENANCE_LENGTH-long period */
    match (hardware::scheduler_get_timer_now(), hardware::scheduler_get_timer_frequency())
    {
        (Some(time_now), Some(frequency)) =>
        {
            let time_now = time_now.to_exact(frequency);
            let last_check_value = (*last_check).to_exact(frequency);
            let maintence_length = MAINTENANCE_LENGTH.to_exact(frequency);

            /* wait until we're at least MAINTENANCE_LENGTH into boot */
            if time_now > maintence_length
            {
                if time_now - last_check_value < maintence_length
                {
                    /* not enough MAINTENANCE_LENGTH time has passed */
                    return;
                }
                /* mark when we last performed housekeeping */
                *last_check = TimerValue::Exact(time_now);
            }
            else
            {
                /* flush debug and bail out */
                debughousekeeper!();
                return;
            }
        },
        (_, _) =>
        {
            /* no timer. output debug and bail out */
            debughousekeeper!();
            return;
        }
    }

    debughousekeeper!(); /* drain the debug logs to the debug hardware port */
    heaphousekeeper!(); /* return any unused regions of physical memory */
    physmemhousekeeper!(); /* tidy up any physical memory structures */

    /* if the global queues are empty then work out which physical CPU core
    has the most number of virtual cores and is therefore the busiest */
    let global_queue_lock = GLOBAL_QUEUES.lock();
    if global_queue_lock.total_queued() > 0
    {
        let mut highest_count = 0;
        let mut busiest_pcore: Option<PhysicalCoreID> = None;
        for (&pcoreid, &vcore_count) in WORKLOAD.lock().iter()
        {
            if vcore_count > highest_count
            {
                highest_count = vcore_count;
                busiest_pcore = Some(pcoreid);
            }
        }

        /* ask the busiest core to send one virtual core back to the global queue
        but only if it has enough to share: it must have more than one virtual core */
        if highest_count > 1
        {
            if let Some(pid) = busiest_pcore
            {
                let m = message::Message::new(message::Recipient::send_to_pcore(pid),
                                                message::MessageContent::DisownQueuedVirtualCore);
                match message::send(m)
                {
                    Err(e) => hvalert!("Failed to message physical CPU {} during load balancing: {:?}", pid, e),
                    Ok(()) => ()
                };
            }
        }
    }
}

/* maintain a simple two-level round-robin scheduler per physical CPU core. we can make it more fancy later.
the hypervisor tries to dish out physical CPU time fairly among capsules, and let the
capsule supervisors work out how best to allocate their time to userspace code.
picking the next virtual CPU core to run should be O(1) or as close as possible to it. */
pub struct ScheduleQueues
{
    high: VecDeque<VirtualCore>,
    low: VecDeque<VirtualCore>,
    high_timeslices: TimesliceCount
}

impl ScheduleQueues
{
    /* initialize a new set of scheduler queues */
    pub fn new() -> ScheduleQueues
    {
        ScheduleQueues
        {
            high: VecDeque::<VirtualCore>::new(),
            low: VecDeque::<VirtualCore>::new(),
            high_timeslices: 0
        }
    }

    /* run the given virtual core by switching to its supervisor context.
    this also updates NORM_PRIO_TICKS. if the current physical CPU was already running a
    virtual core, that virtual core is queued up in the waiting list by context_switch() */
    pub fn run(&mut self, to_run: VirtualCore)
    {
        /* if we're about to run a normal virtual core, then reset counter since a normal virtual core ran.
        if we're running a non-normal virtual core, then increase the count. */
        match to_run.get_priority()
        {
            Priority::Normal => self.high_timeslices = 0,
            Priority::High => self.high_timeslices = self.high_timeslices + 1
        };

        pcore::context_switch(to_run);
    }

    /* add the given virtual core to the appropriate waiting queue. put it to the back
    so that other virtual cores get a chance to run */
    pub fn queue(&mut self, to_queue: VirtualCore)
    {
        match to_queue.get_priority()
        {
            Priority::High => self.high.push_back(to_queue),
            Priority::Normal => self.low.push_back(to_queue)
        }
    }

    /* remove a virtual core from the waiting list queues, selected by priority with safeguards to
    prevent CPU time starvation. Returns selected virtual core or None for no other virtual cores waiting */
    pub fn dequeue(&mut self) -> Option<VirtualCore>
    {
        /* has a normal virtual core been waiting for ages? */
        if self.high_timeslices > HIGH_PRIO_TIMESLICES_MAX
        {
            match self.low.pop_front()
            {
                Some(t) => return Some(t),
                None => ()
            };
        }

        /* check the high priority queue for anything waiting.
        if not, then try the normal priority queue */
        match self.high.pop_front()
        {
            Some(t) => Some(t),
            None => self.low.pop_front()
        }
    }

    /* return the total number of virtual cores queued */
    pub fn total_queued(&self) -> usize
    {
        self.high.len() + self.low.len()
    }
}
<commit_msg>Use named mutexes * Detect when a virtual CPU core is invalid and force a reschedule to run anoter vcore * Restart capsules awaiting a reboot if a physical CPU core runs out of work to do<commit_after>/* diosix virtual CPU scheduler
 *
 * This is, for now, really really simple.
 * Making it fairer and adaptive to workloads is the ultimate goal.
 *
 * (c) Chris Williams, 2018-2021.
 *
 * See LICENSE for usage and copying.
 */

use super::lock::Mutex;
use alloc::collections::vec_deque::VecDeque;
use hashbrown::hash_map::HashMap;
use platform::timer::TimerValue;
use super::error::Cause;
use super::vcore::{VirtualCore, Priority};
use super::pcore::{self, PhysicalCore, PhysicalCoreID};
use super::hardware;
use super::message;
use super::capsule::{self, CapsuleState};

pub type TimesliceCount = u64;

/* prevent physical CPU time starvation: allow a normal virtual core to run after this number of timeslices
have been spent running high priority virtual cores */
const HIGH_PRIO_TIMESLICES_MAX: TimesliceCount = 10;

/* max how long a virtual core is allowed to run before a scheduling decision is made */
const TIMESLICE_LENGTH: TimerValue = TimerValue::Milliseconds(50);

/* define the shortest time between now and another interrupt and rescheduling decision.
this is to stop supervisor kernels spamming the scheduling system with lots of short reschedulings */
const TIMESLICE_MIN_LENGTH: TimerValue = TimerValue::Milliseconds(5);

/* duration a system maintence core (one that can't run supervisor code) must wait
before looking for fixed work to do. also the length in between application cores can
attempt to perform housekeeping */
const MAINTENANCE_LENGTH: TimerValue = TimerValue::Seconds(5);

/* these are the global wait queues. while each physical CPU core gets its own pair
of high-normal wait queues, virtual cores waiting to be assigned to a physical CPU sit in these global queues.
when a physical CPU runs out of queued virtual cores, it pulls one from these global queues.
a physical CPU core can ask fellow CPUs to push virtual cores onto the global queues via messages */
lazy_static!
{
    static ref GLOBAL_QUEUES: Mutex<ScheduleQueues> = Mutex::new("global scheduler queue", ScheduleQueues::new());
    static ref WORKLOAD: Mutex<HashMap<PhysicalCoreID, usize>> = Mutex::new("workload balancer", HashMap::new());
    static ref LAST_HOUSEKEEP_CHECK: Mutex<TimerValue> = Mutex::new("housekeeper tracking", TimerValue::Exact(0));
}

#[derive(PartialEq, Clone, Copy, Debug)]
pub enum SearchMode
{
    MustFind, /* when searching for something to run, keep looping until successful */
    CheckOnce /* search just once for something else to run, return to environment otherwise */
}

/* queue a virtual core in global wait list */
pub fn queue(to_queue: VirtualCore)
{
    GLOBAL_QUEUES.lock().queue(to_queue);
}

/* activate preemptive multitasking. each physical CPU core should call this
   to start running workloads - be them user/supervisor or management tasks
   <= returns OK, or error code on failure */
pub fn start() -> Result<(), Cause>
{
    hardware::scheduler_timer_start();
    Ok(())
}

/* make a decision on whether to run another virtual core,
   or return to the currently running core (if possible).
   ping() is called when a scheduler timer IRQ comes in */
pub fn ping()
{
    let time_now = hardware::scheduler_get_timer_now();
    let frequency = hardware::scheduler_get_timer_frequency();
    if time_now.is_none() || frequency.is_none()
    {
        /* check to see if anything needs to run and bail out if
        no timer hardware can be found (and yet we're still getting IRQs?) */
        run_next(SearchMode::CheckOnce);
        return;
    }

    /* get down to the exact timer values */
    let frequency = frequency.unwrap();
    let time_now = time_now.unwrap().to_exact(frequency);

    /* if the virtual core we're running is doomed, skip straight
       to forcing a reschedule of another vcore */
    match (pcore::PhysicalCore::this().get_timer_sched_last(),
           pcore::PhysicalCore::this().is_vcore_doomed())
    {
        (Some(v), false) =>
        {
            let timeslice_length = TIMESLICE_LENGTH.to_exact(frequency);
            let mut last_scheduled_at = v.to_exact(frequency);

            /* if the capsule we're running in is valid then perform a time slice check.
               if it's not valid, ensure the capsule is torn down or restarted for this
               virtual core. when all vcores are removed from the capsule, it will either
               be deleted or restarted, depending on its state */
            let capsule_state = capsule::get_current_state();
            match capsule_state
            {
                Some(CapsuleState::Valid) =>
                {
                    /* check to see if we've reached the end of this physical CPU core's
                    time slice. a virtual code has the pcore for TIMESLICE_LENGTH of time
                    before a mandatory scheduling decision is made */
                    if time_now - last_scheduled_at >= timeslice_length
                    {
                        /* it's been a while since we last made a decision, so force one now */
                        run_next(SearchMode::CheckOnce);
                        pcore::PhysicalCore::this().set_timer_sched_last(Some(TimerValue::Exact(time_now)));
                        last_scheduled_at = time_now;
                    }
                },
                _ =>
                {
                    /* it is safe to call destroy_current() and restart_current() multiple times
                       per vcore until the capsule is dead or restarted */
                    if let Err(_e) = match capsule_state
                    {
                        Some(CapsuleState::Dying) => capsule::destroy_current(),
                        Some(CapsuleState::Restarting) => capsule::restart_current(),
                        _ => Ok(())
                    }
                    {
                        hvalert!("BUG: Capsule update failure {:?} in scheduler ({:?})", _e, capsule_state)
                    }

                    /* capsule we're running in is no longer valid so force a reschedule */
                    run_next(SearchMode::MustFind);
                    pcore::PhysicalCore::this().set_timer_sched_last(Some(TimerValue::Exact(time_now)));
                    last_scheduled_at = time_now;
                }
            }

            /* check to make sure timer target is correct for whatever virtual core we're about
            to run. run_next() may have set a new timer irq target, or changed the virtual core
            we're running. there may be a supervisor-level timer IRQ upcoming.
            make sure the physical core timer target value is appropriate. */
            if let Some(timer_target) = hardware::scheduler_get_timer_next_at()
            {
                let mut timer_target = timer_target.to_exact(frequency);

                /* avoid skipping over any pending supervisor timer IRQ: reduce latency between
                capsule timer interrupts being raised and capsule cores scheduled to pick up said IRQs */
                if let Some(supervisor_target) = pcore::PhysicalCore::get_virtualcore_timer_target()
                {
                    timer_target = supervisor_target.to_exact(frequency);
                }

                /* if the target is already behind us, discard it and interrupt at end of this timeslice.
                   if the target is too far ahead, curtail it to the end of this timeslice */
                if timer_target <= time_now || timer_target > last_scheduled_at + timeslice_length
                {
                    timer_target = last_scheduled_at + timeslice_length;
                }

                hardware::scheduler_timer_at(TimerValue::Exact(timer_target));
            }
        },

        /* if not we've not scheduled anything yet, or whatever we were running
           is now invalid, we must find something (else) to run */
        (None, _) | (_, true) =>
        {
            run_next(SearchMode::MustFind);
            pcore::PhysicalCore::this().set_timer_sched_last(Some(TimerValue::Exact(time_now)));
        }
    }
}

/* find something else to run, or return to whatever we were running if allowed.
   call this function when a virtual core's timeslice has expired, or it has crashed
   or stopped running and we can't return to it. this function will return regardless
   if this physical CPU core is unable to run virtual cores.
   => search_mode = define whether or not to continue searching for another
   virtual core to run, or check once to see if something else is waiting */
fn run_next(search_mode: SearchMode)
{
    /* check for housekeeping */
    housekeeping();

    /* if this core can run supervisor-level code then find it some work to do */
    if pcore::PhysicalCore::smode_supported()
    {
        loop
        {
            let mut something_found = true;

            /* check to see if there's anything waiting to be picked up for this
            physical CPU from a global queue. if so, then adopt it so it can get a chance to run */
            match GLOBAL_QUEUES.lock().dequeue()
            {
                /* we've found a virtual CPU core to run, so switch to that */
                Some(orphan) =>
                {
                    let mut workloads = WORKLOAD.lock();
                    let pcore_id = PhysicalCore::get_id();

                    /* increment counter of how many virtual cores this physical CPU core
                    has taken from the global queue */
                    if let Some(count) = workloads.get_mut(&pcore_id)
                    {
                        *count = *count + 1;
                    }
                    else
                    {
                        workloads.insert(pcore_id, 1);
                    }

                    pcore::context_switch(orphan);
                },

                /* otherwise, try to take a virtual CPU core waiting for this physical CPU core and run it */
                _ => match PhysicalCore::dequeue()
                {
                    Some(virtcore) => pcore::context_switch(virtcore), /* waiting virtual CPU core found, queuing now */
                    _ => something_found = false /* nothing else to run */
                }
            }

            /* if we've found something, or only searching once, exit the search loop */
            if something_found == true || search_mode == SearchMode::CheckOnce
            {
                break;
            }

            /* still here? see if there's a capsule waiting to be restarted and give us something to do */
            capsulehousekeeper!();
        }

        /* we've got a virtual core to run. tell the timer system to call us back soon */
        hardware::scheduler_timer_next_in(TIMESLICE_LENGTH);
    }
    else
    {
        hardware::scheduler_timer_next_in(MAINTENANCE_LENGTH); /* we'll be back some time later */
    }
}

/* perform any housekeeping duties defined by the various parts of the system */
fn housekeeping()
{
    /* perform integrity checks */
    #[cfg(feature = "integritychecks")]
    {
        if let Err(val) = pcore::PhysicalCore::integrity_check()
        {
            hvalert!("CPU private stack overflowed (0x{:x}). Halting!", val);
            loop {}
        }
    }

    /* avoid blocking on the house keeping lock */
    if LAST_HOUSEKEEP_CHECK.is_locked() == true
    {
        return;
    }

    let mut last_check = LAST_HOUSEKEEP_CHECK.lock();

    /* only perform housekeeping once every MAINTENANCE_LENGTH-long period */
    match (hardware::scheduler_get_timer_now(), hardware::scheduler_get_timer_frequency())
    {
        (Some(time_now), Some(frequency)) =>
        {
            let time_now = time_now.to_exact(frequency);
            let last_check_value = (*last_check).to_exact(frequency);
            let maintence_length = MAINTENANCE_LENGTH.to_exact(frequency);

            /* wait until we're at least MAINTENANCE_LENGTH into boot */
            if time_now > maintence_length
            {
                if time_now - last_check_value < maintence_length
                {
                    /* not enough MAINTENANCE_LENGTH time has passed */
                    return;
                }
                /* mark when we last performed housekeeping */
                *last_check = TimerValue::Exact(time_now);
            }
            else
            {
                /* flush debug and bail out */
                debughousekeeper!();
                return;
            }
        },
        (_, _) =>
        {
            /* no timer. output debug and bail out */
            debughousekeeper!();
            return;
        }
    }

    debughousekeeper!(); /* drain the debug logs to the debug hardware port */
    heaphousekeeper!(); /* return any unused regions of physical memory */
    physmemhousekeeper!(); /* tidy up any physical memory structures */
    capsulehousekeeper!(); /* restart capsules that crashed or rebooted */

    /* if the global queues are empty then work out which physical CPU core
    has the most number of virtual cores and is therefore the busiest */
    let global_queue_lock = GLOBAL_QUEUES.lock();
    if global_queue_lock.total_queued() > 0
    {
        let mut highest_count = 0;
        let mut busiest_pcore: Option<PhysicalCoreID> = None;
        let workloads = WORKLOAD.lock();
        for (&pcoreid, &vcore_count) in workloads.iter()
        {
            if vcore_count > highest_count
            {
                highest_count = vcore_count;
                busiest_pcore = Some(pcoreid);
            }
        }

        /* ask the busiest core to send one virtual core back to the global queue
        but only if it has enough to share: it must have more than one virtual core */
        if highest_count > 1
        {
            if let Some(pid) = busiest_pcore
            {
                let m = message::Message::new(message::Recipient::send_to_pcore(pid),
                                                message::MessageContent::DisownQueuedVirtualCore);
                match message::send(m)
                {
                    Err(e) => hvalert!("Failed to message physical CPU {} during load balancing: {:?}", pid, e),
                    Ok(()) => ()
                };
            }
        }
    }
}

/* maintain a simple two-level round-robin scheduler per physical CPU core. we can make it more fancy later.
the hypervisor tries to dish out physical CPU time fairly among capsules, and let the
capsule supervisors work out how best to allocate their time to userspace code.
picking the next virtual CPU core to run should be O(1) or as close as possible to it. */
pub struct ScheduleQueues
{
    high: VecDeque<VirtualCore>,
    low: VecDeque<VirtualCore>,
    high_timeslices: TimesliceCount
}

impl ScheduleQueues
{
    /* initialize a new set of scheduler queues */
    pub fn new() -> ScheduleQueues
    {
        ScheduleQueues
        {
            high: VecDeque::<VirtualCore>::new(),
            low: VecDeque::<VirtualCore>::new(),
            high_timeslices: 0
        }
    }

    /* run the given virtual core by switching to its supervisor context.
    this also updates NORM_PRIO_TICKS. if the current physical CPU was already running a
    virtual core, that virtual core is queued up in the waiting list by context_switch() */
    pub fn run(&mut self, to_run: VirtualCore)
    {
        /* if we're about to run a normal virtual core, then reset counter since a normal virtual core ran.
        if we're running a non-normal virtual core, then increase the count. */
        match to_run.get_priority()
        {
            Priority::Normal => self.high_timeslices = 0,
            Priority::High => self.high_timeslices = self.high_timeslices + 1
        };

        pcore::context_switch(to_run);
    }

    /* add the given virtual core to the appropriate waiting queue. put it to the back
    so that other virtual cores get a chance to run */
    pub fn queue(&mut self, to_queue: VirtualCore)
    {
        match to_queue.get_priority()
        {
            Priority::High => self.high.push_back(to_queue),
            Priority::Normal => self.low.push_back(to_queue)
        }
    }

    /* remove a virtual core from the waiting list queues, selected by priority with safeguards to
    prevent CPU time starvation. Returns selected virtual core or None for no other virtual cores waiting */
    pub fn dequeue(&mut self) -> Option<VirtualCore>
    {
        /* has a normal virtual core been waiting for ages? */
        if self.high_timeslices > HIGH_PRIO_TIMESLICES_MAX
        {
            match self.low.pop_front()
            {
                Some(t) => return Some(t),
                None => ()
            };
        }

        /* check the high priority queue for anything waiting.
        if not, then try the normal priority queue */
        match self.high.pop_front()
        {
            Some(t) => Some(t),
            None => self.low.pop_front()
        }
    }

    /* return the total number of virtual cores queued */
    pub fn total_queued(&self) -> usize
    {
        self.high.len() + self.low.len()
    }
}
<|endoftext|>


<commit_before>use redox::{Box, String, Url};
use redox::{cmp, mem, ptr};
use redox::fs::File;
use redox::io::*;
use redox::ops::DerefMut;
use redox::to_num::ToNum;

use orbital::event::Event;
use orbital::Point;
use orbital::Size;

use self::display::Display;
use self::session::Session;
use self::window::Window;

pub mod display;
pub mod package;
pub mod scheduler;
pub mod session;
pub mod window;

pub static mut session_ptr: *mut Session = 0 as *mut Session;

/// A window resource
pub struct Resource {
    /// The window
    pub window: Box<Window>,
    /// Seek point
    pub seek: usize,
}

impl Resource {
    pub fn dup(&self) -> Option<Box<Resource>> {
        Some(box Resource {
            window: Window::new(self.window.point, self.window.size, self.window.title.clone()),
            seek: self.seek,
        })
    }

    /// Return the url of this resource
    pub fn path(&self) -> Option<String> {
        Some(format!("orbital:///{}/{}/{}/{}/{}",
                         self.window.point.x,
                         self.window.point.y,
                         self.window.size.width,
                         self.window.size.height,
                         self.window.title))
    }

    /// Read data to buffer
    pub fn read(&mut self, buf: &mut [u8]) -> Option<usize> {
        //Read events from window
        let mut i = 0;
        while buf.len() - i >= mem::size_of::<Event>() {
            match self.window.poll() {
                Some(event) => {
                    unsafe { ptr::write(buf.as_ptr().offset(i as isize) as *mut Event, event) };
                    i += mem::size_of::<Event>();
                }
                None => break,
            }
        }

        Some(i)
    }

    /// Write to resource
    pub fn write(&mut self, buf: &[u8]) -> Option<usize> {
        let content = &mut self.window.content;

        let size = cmp::min(content.size - self.seek, buf.len());
        unsafe {
            Display::copy_run(buf.as_ptr() as usize,
                              content.offscreen + self.seek,
                              size);
        }
        self.seek += size;

        return Some(size);
    }

    /// Seek
    pub fn seek(&mut self, pos: SeekFrom) -> Option<usize> {
        let end = self.window.content.size;

        self.seek = match pos {
            SeekFrom::Start(offset) => cmp::min(end, cmp::max(0, offset)),
            SeekFrom::Current(offset) => cmp::min(end, cmp::max(0, self.seek as isize + offset) as usize),
            SeekFrom::End(offset) => cmp::min(end, cmp::max(0, end as isize + offset) as usize),
        };

        return Some(self.seek);
    }

    /// Sync the resource, should flip
    pub fn sync(&mut self) -> bool {
        self.window.redraw();
        true
    }
}

/// A window scheme
pub struct Scheme {
    pub session: Box<Session>,
    pub next_x: isize,
    pub next_y: isize,
}

impl Scheme {
    pub fn new() -> Box<Scheme> {
        debugln!("- Starting Orbital");
        debugln!("    Console: Press F1");
        debugln!("    Desktop: Press F2");
        let mut ret = box Scheme {
            session: Session::new(),
            next_x: 0,
            next_y: 0,
        };
        unsafe { session_ptr = ret.session.deref_mut() };
        ret
    }

    pub fn open(&mut self, url_str: &str, _: usize) -> Option<Box<Resource>> {
        //window://host/path/path/path is the path type we're working with.
        let url = Url::from_str(url_str);

        let host = url.host();
        if host.is_empty() {
            let path = url.path_parts();
            let mut pointx = match path.get(0) {
                Some(x) => x.to_num_signed(),
                None => 0,
            };
            let mut pointy = match path.get(1) {
                Some(y) => y.to_num_signed(),
                None => 0,
            };
            let size_width = match path.get(2) {
                Some(w) => w.to_num(),
                None => 100,
            };
            let size_height = match path.get(3) {
                Some(h) => h.to_num(),
                None => 100,
            };

            let mut title = match path.get(4) {
                Some(t) => t.clone(),
                None => String::new(),
            };
            for i in 5..path.len() {
                if let Some(t) = path.get(i) {
                    title = title + "/" + t;
                }
            }

            if pointx <= 0 || pointy <= 0 {
                if self.next_x > self.session.display.width as isize - size_width as isize {
                    self.next_x = 0;
                }
                self.next_x += 32;
                pointx = self.next_x;

                if self.next_y > self.session.display.height as isize - size_height as isize {
                    self.next_y = 0;
                }
                self.next_y += 32;
                pointy = self.next_y;
            }

            Some(box Resource {
                window: Window::new(Point::new(pointx, pointy), Size::new(size_width, size_height), title),
                seek: 0,
            })
        } else if host == "launch" {
            let path = url.path();
            debugln!("Launch: {}", path);

            unsafe {
                let reenable = scheduler::start_no_ints();

                for package in self.session.packages.iter() {
                    let mut accepted = false;
                    for accept in package.accepts.iter() {
                        if (accept.starts_with('*') && path.ends_with(&accept[1..]))
                        || (accept.ends_with('*') && path.starts_with(&accept[..accept.len() - 1]))
                        {
                            accepted = true;
                            break;
                        }
                    }
                    if accepted {
                        debugln!("With: {}", package.binary);

                        File::exec(&package.binary, &[&path]);
                        break;
                    }
                }

                scheduler::end_no_ints(reenable);
            }

            None
        } else {
            None
        }
    }

    pub fn event(&mut self, event: &Event) {
        unsafe {
            let reenable = scheduler::start_no_ints();

            self.session.event(event);

            scheduler::end_no_ints(reenable);

            self.session.redraw();
        }
    }
}

//TODO: This is a hack and it will go away
#[cold]
#[inline(never)]
#[no_mangle]
pub unsafe extern "C" fn _event(scheme: *mut Scheme, event: *const Event) {
    (*scheme).event(&*event);
}
<commit_msg>Remove debug message<commit_after>use redox::{Box, String, Url};
use redox::{cmp, mem, ptr};
use redox::fs::File;
use redox::io::*;
use redox::ops::DerefMut;
use redox::to_num::ToNum;

use orbital::event::Event;
use orbital::Point;
use orbital::Size;

use self::display::Display;
use self::session::Session;
use self::window::Window;

pub mod display;
pub mod package;
pub mod scheduler;
pub mod session;
pub mod window;

pub static mut session_ptr: *mut Session = 0 as *mut Session;

/// A window resource
pub struct Resource {
    /// The window
    pub window: Box<Window>,
    /// Seek point
    pub seek: usize,
}

impl Resource {
    pub fn dup(&self) -> Option<Box<Resource>> {
        Some(box Resource {
            window: Window::new(self.window.point, self.window.size, self.window.title.clone()),
            seek: self.seek,
        })
    }

    /// Return the url of this resource
    pub fn path(&self) -> Option<String> {
        Some(format!("orbital:///{}/{}/{}/{}/{}",
                         self.window.point.x,
                         self.window.point.y,
                         self.window.size.width,
                         self.window.size.height,
                         self.window.title))
    }

    /// Read data to buffer
    pub fn read(&mut self, buf: &mut [u8]) -> Option<usize> {
        //Read events from window
        let mut i = 0;
        while buf.len() - i >= mem::size_of::<Event>() {
            match self.window.poll() {
                Some(event) => {
                    unsafe { ptr::write(buf.as_ptr().offset(i as isize) as *mut Event, event) };
                    i += mem::size_of::<Event>();
                }
                None => break,
            }
        }

        Some(i)
    }

    /// Write to resource
    pub fn write(&mut self, buf: &[u8]) -> Option<usize> {
        let content = &mut self.window.content;

        let size = cmp::min(content.size - self.seek, buf.len());
        unsafe {
            Display::copy_run(buf.as_ptr() as usize,
                              content.offscreen + self.seek,
                              size);
        }
        self.seek += size;

        return Some(size);
    }

    /// Seek
    pub fn seek(&mut self, pos: SeekFrom) -> Option<usize> {
        let end = self.window.content.size;

        self.seek = match pos {
            SeekFrom::Start(offset) => cmp::min(end, cmp::max(0, offset)),
            SeekFrom::Current(offset) => cmp::min(end, cmp::max(0, self.seek as isize + offset) as usize),
            SeekFrom::End(offset) => cmp::min(end, cmp::max(0, end as isize + offset) as usize),
        };

        return Some(self.seek);
    }

    /// Sync the resource, should flip
    pub fn sync(&mut self) -> bool {
        self.window.redraw();
        true
    }
}

/// A window scheme
pub struct Scheme {
    pub session: Box<Session>,
    pub next_x: isize,
    pub next_y: isize,
}

impl Scheme {
    pub fn new() -> Box<Scheme> {
        debugln!("- Starting Orbital");
        debugln!("    Console: Press F1");
        debugln!("    Desktop: Press F2");
        let mut ret = box Scheme {
            session: Session::new(),
            next_x: 0,
            next_y: 0,
        };
        unsafe { session_ptr = ret.session.deref_mut() };
        ret
    }

    pub fn open(&mut self, url_str: &str, _: usize) -> Option<Box<Resource>> {
        //window://host/path/path/path is the path type we're working with.
        let url = Url::from_str(url_str);

        let host = url.host();
        if host.is_empty() {
            let path = url.path_parts();
            let mut pointx = match path.get(0) {
                Some(x) => x.to_num_signed(),
                None => 0,
            };
            let mut pointy = match path.get(1) {
                Some(y) => y.to_num_signed(),
                None => 0,
            };
            let size_width = match path.get(2) {
                Some(w) => w.to_num(),
                None => 100,
            };
            let size_height = match path.get(3) {
                Some(h) => h.to_num(),
                None => 100,
            };

            let mut title = match path.get(4) {
                Some(t) => t.clone(),
                None => String::new(),
            };
            for i in 5..path.len() {
                if let Some(t) = path.get(i) {
                    title = title + "/" + t;
                }
            }

            if pointx <= 0 || pointy <= 0 {
                if self.next_x > self.session.display.width as isize - size_width as isize {
                    self.next_x = 0;
                }
                self.next_x += 32;
                pointx = self.next_x;

                if self.next_y > self.session.display.height as isize - size_height as isize {
                    self.next_y = 0;
                }
                self.next_y += 32;
                pointy = self.next_y;
            }

            Some(box Resource {
                window: Window::new(Point::new(pointx, pointy), Size::new(size_width, size_height), title),
                seek: 0,
            })
        } else if host == "launch" {
            let path = url.path();

            unsafe {
                let reenable = scheduler::start_no_ints();

                for package in self.session.packages.iter() {
                    let mut accepted = false;
                    for accept in package.accepts.iter() {
                        if (accept.starts_with('*') && path.ends_with(&accept[1..]))
                        || (accept.ends_with('*') && path.starts_with(&accept[..accept.len() - 1]))
                        {
                            accepted = true;
                            break;
                        }
                    }
                    if accepted {
                        File::exec(&package.binary, &[&path]);
                        break;
                    }
                }

                scheduler::end_no_ints(reenable);
            }

            None
        } else {
            None
        }
    }

    pub fn event(&mut self, event: &Event) {
        unsafe {
            let reenable = scheduler::start_no_ints();

            self.session.event(event);

            scheduler::end_no_ints(reenable);

            self.session.redraw();
        }
    }
}

//TODO: This is a hack and it will go away
#[cold]
#[inline(never)]
#[no_mangle]
pub unsafe extern "C" fn _event(scheme: *mut Scheme, event: *const Event) {
    (*scheme).event(&*event);
}
<|endoftext|>


<commit_before>use index::btree::internal::InNode;
use index::btree::{NodeCellRef, NodeWriteGuard, write_node};
use index::{EntryKey, Slice};
use std::fmt::Debug;

pub fn is_node_serial<KS, PS>(node: &NodeWriteGuard<KS, PS>) -> bool
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    // check keys
    for i in 0..node.len() {
        if node.keys()[i] <= smallvec!() {
            error!("EMPTY KEY DETECTED !!!");
            return false;
        }
    }
    for i in 1..node.len() {
        if node.keys()[i - 1] >= node.keys()[i] {
            error!("serial check failed for key ordering");
            return false;
        }
    }
    true
}

pub fn is_node_list_serial<KS, PS>(nodes: &Vec<NodeWriteGuard<KS, PS>>) -> bool
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    if nodes.len() == 0 {
        return true;
    }
    for (i, n) in nodes.iter().enumerate() {
        if !is_node_serial(n) {
            error!("node at {} not serial", i);
            return false;
        }
    }

    // check right ref
    for i in 0..nodes.len() - 1 {
        let first_right_bound = nodes[i].right_bound();
        let second_first_node = nodes[i + 1].first_key();
        let second_right_bound = nodes[i + 1].right_bound();
        if first_right_bound >= second_right_bound {
            error!("right bound at {} larger than right right bound", i);
            return false;
        }
        if first_right_bound > second_first_node {
            error!("right bound at {} larger than right first node", i);
            return false;
        }
    }
    return true;
}

pub fn is_node_level_serial<KS, PS>(mut node: NodeWriteGuard<KS, PS>) -> bool
    where
        KS: Slice<EntryKey> + Debug + 'static,
        PS: Slice<NodeCellRef> + 'static,
{
    loop {
        let right_ref = node.right_ref().unwrap();
        let right_bound = node.right_bound();
        if !is_node_serial(&node) {
            error!("Node not serial");
            return false;
        }
        if node.first_key() > right_bound {
            error!("Node have right key smaller than the first");
            return false;
        }
        if node.last_key() > right_bound {
            error!("Node have right key smaller than the last");
            return false;
        }
        let next = write_node(right_ref);
        if next.is_none() {
            debug!("Node level check reached non node");
            return true;
        }
        {
            let next_first = next.first_key();
            if next_first < right_bound {
                error!("next first key smaller than right bound");
                return false;
            }
        }
        node = next;
    }
}<commit_msg>tree validator<commit_after>use index::btree::internal::InNode;
use index::btree::{NodeCellRef, NodeWriteGuard, write_node, BPlusTree, NodeData};
use index::{EntryKey, Slice};
use std::fmt::Debug;

pub fn is_node_serial<KS, PS>(node: &NodeWriteGuard<KS, PS>) -> bool
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    // check keys
    for i in 0..node.len() {
        if node.keys()[i] <= smallvec!() {
            error!("EMPTY KEY DETECTED !!!");
            return false;
        }
    }
    for i in 1..node.len() {
        if node.keys()[i - 1] >= node.keys()[i] {
            error!("serial check failed for key ordering");
            return false;
        }
    }
    true
}

pub fn is_node_list_serial<KS, PS>(nodes: &Vec<NodeWriteGuard<KS, PS>>) -> bool
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    if nodes.len() == 0 {
        return true;
    }
    for (i, n) in nodes.iter().enumerate() {
        if !is_node_serial(n) {
            error!("node at {} not serial", i);
            return false;
        }
    }

    // check right ref
    for i in 0..nodes.len() - 1 {
        let first_right_bound = nodes[i].right_bound();
        let second_first_node = nodes[i + 1].first_key();
        let second_right_bound = nodes[i + 1].right_bound();
        if first_right_bound >= second_right_bound {
            error!("right bound at {} larger than right right bound", i);
            return false;
        }
        if first_right_bound > second_first_node {
            error!("right bound at {} larger than right first node", i);
            return false;
        }
    }
    return true;
}

pub fn is_node_level_serial<KS, PS>(mut node: NodeWriteGuard<KS, PS>) -> bool
    where
        KS: Slice<EntryKey> + Debug + 'static,
        PS: Slice<NodeCellRef> + 'static,
{
    loop {
        let right_ref = node.right_ref().unwrap();
        let right_bound = node.right_bound();
        if !is_node_serial(&node) {
            error!("Node not serial");
            return false;
        }
        if node.first_key() > right_bound {
            error!("Node have right key smaller than the first");
            return false;
        }
        if node.last_key() > right_bound {
            error!("Node have right key smaller than the last");
            return false;
        }
        let next = write_node(right_ref);
        if next.is_none() {
            debug!("Node level check reached non node");
            return true;
        }
        if next.first_key() < right_bound {
            error!("next first key smaller than right bound");
            return false;
        }
        if next.right_bound() < right_bound {
            error!("next right bound key smaller than right bound");
            return false
        }
        node = next;
    }
}

pub fn is_tree_in_order<KS, PS>(tree: &BPlusTree<KS, PS>) -> bool
    where
        KS: Slice<EntryKey> + Debug + 'static,
        PS: Slice<NodeCellRef> + 'static,
{
    return ensure_level_in_order::<KS, PS>(&tree.get_root());
}

fn ensure_level_in_order<KS, PS>(node: &NodeCellRef) -> bool
    where
        KS: Slice<EntryKey> + Debug + 'static,
        PS: Slice<NodeCellRef> + 'static,
{
    let first_node = write_node::<KS, PS>(&node);
    let sub_ref = match &*first_node {
        &NodeData::Internal(ref n) => Some(n.ptrs.as_slice_immute()[0].clone()),
        _ => None
    };
    if !is_node_level_serial(first_node) {
        return false;
    }
    if let Some(sub_level) = sub_ref {
        return ensure_level_in_order::<KS, PS>(&sub_level);
    }
    return true;
}<|endoftext|>


<commit_before>use syntax::ast::{self, AstNode};

use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};

// Assist: apply_demorgan
//
// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law].
// This transforms expressions of the form `!l || !r` into `!(l && r)`.
// This also works with `&&`. This assist can only be applied with the cursor
// on either `||` or `&&`.
//
// ```
// fn main() {
//     if x != 4 ||$0 y < 3.14 {}
// }
// ```
// ->
// ```
// fn main() {
//     if !(x == 4 && !(y < 3.14)) {}
// }
// ```
pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
    let op = expr.op_kind()?;
    let op_range = expr.op_token()?.text_range();
    let opposite_op = opposite_logic_op(op)?;
    let cursor_in_range = op_range.contains_range(ctx.frange.range);
    if !cursor_in_range {
        return None;
    }

    let lhs = expr.lhs()?;
    let lhs_range = lhs.syntax().text_range();
    let not_lhs = invert_boolean_expression(&ctx.sema, lhs);

    let rhs = expr.rhs()?;
    let rhs_range = rhs.syntax().text_range();
    let not_rhs = invert_boolean_expression(&ctx.sema, rhs);

    acc.add(
        AssistId("apply_demorgan", AssistKind::RefactorRewrite),
        "Apply De Morgan's law",
        op_range,
        |edit| {
            let paren_expr = expr.syntax().parent().and_then(|parent| ast::ParenExpr::cast(parent));

            let neg_expr = paren_expr
                .clone()
                .and_then(|paren_expr| paren_expr.syntax().parent())
                .and_then(|parent| ast::PrefixExpr::cast(parent))
                .and_then(|prefix_expr| {
                    if prefix_expr.op_kind().unwrap() == ast::PrefixOp::Not {
                        Some(prefix_expr)
                    } else {
                        None
                    }
                });

            edit.replace(op_range, opposite_op);

            if let Some(paren_expr) = paren_expr {
                edit.replace(lhs_range, not_lhs.syntax().text());
                edit.replace(rhs_range, not_rhs.syntax().text());
                if let Some(neg_expr) = neg_expr {
                    cov_mark::hit!(demorgan_double_negation);
                    edit.replace(neg_expr.op_token().unwrap().text_range(), "");
                } else {
                    cov_mark::hit!(demorgan_double_parens);
                    edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!(");
                }
            } else {
                edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
                edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
            }
        },
    )
}

// Return the opposite text for a given logical operator, if it makes sense
fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> {
    match kind {
        ast::BinOp::BooleanOr => Some("&&"),
        ast::BinOp::BooleanAnd => Some("||"),
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use ide_db::helpers::FamousDefs;

    use super::*;

    use crate::tests::{check_assist, check_assist_not_applicable};

    const ORDABLE_FIXTURE: &'static str = r"
//- /lib.rs deps:core crate:ordable
struct NonOrderable;
struct Orderable;
impl core::cmp::Ord for Orderable {}
";

    fn check(ra_fixture_before: &str, ra_fixture_after: &str) {
        let before = &format!(
            "//- /main.rs crate:main deps:core,ordable\n{}\n{}{}",
            ra_fixture_before,
            FamousDefs::FIXTURE,
            ORDABLE_FIXTURE
        );
        check_assist(apply_demorgan, before, &format!("{}\n", ra_fixture_after));
    }

    #[test]
    fn demorgan_handles_leq() {
        check(
            r"use ordable::Orderable;
fn f() {
    Orderable < Orderable &&$0 Orderable <= Orderable
}",
            r"use ordable::Orderable;
fn f() {
    !(Orderable >= Orderable || Orderable > Orderable)
}",
        );
        check(
            r"use ordable::NonOrderable;
fn f() {
    NonOrderable < NonOrderable &&$0 NonOrderable <= NonOrderable
}",
            r"use ordable::NonOrderable;
fn f() {
    !(!(NonOrderable < NonOrderable) || !(NonOrderable <= NonOrderable))
}",
        );
    }

    #[test]
    fn demorgan_handles_geq() {
        check(
            r"use ordable::Orderable;
fn f() {
    Orderable > Orderable &&$0 Orderable >= Orderable
}",
            r"use ordable::Orderable;
fn f() {
    !(Orderable <= Orderable || Orderable < Orderable)
}",
        );
        check(
            r"use ordable::NonOrderable;
fn f() {
    Orderable > Orderable &&$0 Orderable >= Orderable
}",
            r"use ordable::NonOrderable;
fn f() {
    !(!(Orderable > Orderable) || !(Orderable >= Orderable))
}",
        );
    }

    #[test]
    fn demorgan_turns_and_into_or() {
        check_assist(apply_demorgan, "fn f() { !x &&$0 !x }", "fn f() { !(x || x) }")
    }

    #[test]
    fn demorgan_turns_or_into_and() {
        check_assist(apply_demorgan, "fn f() { !x ||$0 !x }", "fn f() { !(x && x) }")
    }

    #[test]
    fn demorgan_removes_inequality() {
        check_assist(apply_demorgan, "fn f() { x != x ||$0 !x }", "fn f() { !(x == x && x) }")
    }

    #[test]
    fn demorgan_general_case() {
        check_assist(apply_demorgan, "fn f() { x ||$0 x }", "fn f() { !(!x && !x) }")
    }

    #[test]
    fn demorgan_doesnt_apply_with_cursor_not_on_op() {
        check_assist_not_applicable(apply_demorgan, "fn f() { $0 !x || !x }")
    }

    #[test]
    fn demorgan_doesnt_double_negation() {
        cov_mark::check!(demorgan_double_negation);
        check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }")
    }

    #[test]
    fn demorgan_doesnt_double_parens() {
        cov_mark::check!(demorgan_double_parens);
        check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }")
    }
}
<commit_msg>Allow applying De Morgan's law to multiple terms at once<commit_after>use std::collections::VecDeque;

use syntax::ast::{self, AstNode};

use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};

// Assist: apply_demorgan
//
// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law].
// This transforms expressions of the form `!l || !r` into `!(l && r)`.
// This also works with `&&`. This assist can only be applied with the cursor
// on either `||` or `&&`.
//
// ```
// fn main() {
//     if x != 4 ||$0 y < 3.14 {}
// }
// ```
// ->
// ```
// fn main() {
//     if !(x == 4 && !(y < 3.14)) {}
// }
// ```
pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
    let op = expr.op_kind()?;
    let op_range = expr.op_token()?.text_range();
    let opposite_op = opposite_logic_op(op)?;
    let cursor_in_range = op_range.contains_range(ctx.frange.range);
    if !cursor_in_range {
        return None;
    }

    let mut expr = expr;

    // Walk up the tree while we have the same binary operator
    while let Some(parent_expr) = expr.syntax().parent().and_then(ast::BinExpr::cast) {
        if let Some(parent_op) = expr.op_kind() {
            if parent_op == op {
                expr = parent_expr
            }
        }
    }

    let mut expr_stack = vec![expr.clone()];
    let mut terms = Vec::new();
    let mut op_ranges = Vec::new();

    // Find all the children with the same binary operator
    while let Some(expr) = expr_stack.pop() {
        let mut traverse_bin_expr_arm = |expr| {
            if let ast::Expr::BinExpr(bin_expr) = expr {
                if let Some(expr_op) = bin_expr.op_kind() {
                    if expr_op == op {
                        expr_stack.push(bin_expr);
                    } else {
                        terms.push(ast::Expr::BinExpr(bin_expr));
                    }
                } else {
                    terms.push(ast::Expr::BinExpr(bin_expr));
                }
            } else {
                terms.push(expr);
            }
        };

        op_ranges.extend(expr.op_token().map(|t| t.text_range()));
        traverse_bin_expr_arm(expr.lhs()?);
        traverse_bin_expr_arm(expr.rhs()?);
    }

    acc.add(
        AssistId("apply_demorgan", AssistKind::RefactorRewrite),
        "Apply De Morgan's law",
        op_range,
        |edit| {
            terms.sort_by_key(|t| t.syntax().text_range().start());
            let mut terms = VecDeque::from(terms);

            let paren_expr = expr.syntax().parent().and_then(|parent| ast::ParenExpr::cast(parent));

            let neg_expr = paren_expr
                .clone()
                .and_then(|paren_expr| paren_expr.syntax().parent())
                .and_then(|parent| ast::PrefixExpr::cast(parent))
                .and_then(|prefix_expr| {
                    if prefix_expr.op_kind().unwrap() == ast::PrefixOp::Not {
                        Some(prefix_expr)
                    } else {
                        None
                    }
                });

            for op_range in op_ranges {
                edit.replace(op_range, opposite_op);
            }

            if let Some(paren_expr) = paren_expr {
                for term in terms {
                    let range = term.syntax().text_range();
                    let not_term = invert_boolean_expression(&ctx.sema, term);

                    edit.replace(range, not_term.syntax().text());
                }

                if let Some(neg_expr) = neg_expr {
                    cov_mark::hit!(demorgan_double_negation);
                    edit.replace(neg_expr.op_token().unwrap().text_range(), "");
                } else {
                    cov_mark::hit!(demorgan_double_parens);
                    edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!(");
                }
            } else {
                if let Some(lhs) = terms.pop_front() {
                    let lhs_range = lhs.syntax().text_range();
                    let not_lhs = invert_boolean_expression(&ctx.sema, lhs);

                    edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
                }

                if let Some(rhs) = terms.pop_back() {
                    let rhs_range = rhs.syntax().text_range();
                    let not_rhs = invert_boolean_expression(&ctx.sema, rhs);

                    edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
                }

                for term in terms {
                    let term_range = term.syntax().text_range();
                    let not_term = invert_boolean_expression(&ctx.sema, term);
                    edit.replace(term_range, not_term.syntax().text());
                }
            }
        },
    )
}

// Return the opposite text for a given logical operator, if it makes sense
fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> {
    match kind {
        ast::BinOp::BooleanOr => Some("&&"),
        ast::BinOp::BooleanAnd => Some("||"),
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use ide_db::helpers::FamousDefs;

    use super::*;

    use crate::tests::{check_assist, check_assist_not_applicable};

    const ORDABLE_FIXTURE: &'static str = r"
//- /lib.rs deps:core crate:ordable
struct NonOrderable;
struct Orderable;
impl core::cmp::Ord for Orderable {}
";

    fn check(ra_fixture_before: &str, ra_fixture_after: &str) {
        let before = &format!(
            "//- /main.rs crate:main deps:core,ordable\n{}\n{}{}",
            ra_fixture_before,
            FamousDefs::FIXTURE,
            ORDABLE_FIXTURE
        );
        check_assist(apply_demorgan, before, &format!("{}\n", ra_fixture_after));
    }

    #[test]
    fn demorgan_handles_leq() {
        check(
            r"use ordable::Orderable;
fn f() {
    Orderable < Orderable &&$0 Orderable <= Orderable
}",
            r"use ordable::Orderable;
fn f() {
    !(Orderable >= Orderable || Orderable > Orderable)
}",
        );
        check(
            r"use ordable::NonOrderable;
fn f() {
    NonOrderable < NonOrderable &&$0 NonOrderable <= NonOrderable
}",
            r"use ordable::NonOrderable;
fn f() {
    !(!(NonOrderable < NonOrderable) || !(NonOrderable <= NonOrderable))
}",
        );
    }

    #[test]
    fn demorgan_handles_geq() {
        check(
            r"use ordable::Orderable;
fn f() {
    Orderable > Orderable &&$0 Orderable >= Orderable
}",
            r"use ordable::Orderable;
fn f() {
    !(Orderable <= Orderable || Orderable < Orderable)
}",
        );
        check(
            r"use ordable::NonOrderable;
fn f() {
    Orderable > Orderable &&$0 Orderable >= Orderable
}",
            r"use ordable::NonOrderable;
fn f() {
    !(!(Orderable > Orderable) || !(Orderable >= Orderable))
}",
        );
    }

    #[test]
    fn demorgan_turns_and_into_or() {
        check_assist(apply_demorgan, "fn f() { !x &&$0 !x }", "fn f() { !(x || x) }")
    }

    #[test]
    fn demorgan_turns_or_into_and() {
        check_assist(apply_demorgan, "fn f() { !x ||$0 !x }", "fn f() { !(x && x) }")
    }

    #[test]
    fn demorgan_removes_inequality() {
        check_assist(apply_demorgan, "fn f() { x != x ||$0 !x }", "fn f() { !(x == x && x) }")
    }

    #[test]
    fn demorgan_general_case() {
        check_assist(apply_demorgan, "fn f() { x ||$0 x }", "fn f() { !(!x && !x) }")
    }

    #[test]
    fn demorgan_multiple_terms() {
        check_assist(apply_demorgan, "fn f() { x ||$0 y || z }", "fn f() { !(!x && !y && !z) }");
        check_assist(apply_demorgan, "fn f() { x || y ||$0 z }", "fn f() { !(!x && !y && !z) }");
    }

    #[test]
    fn demorgan_doesnt_apply_with_cursor_not_on_op() {
        check_assist_not_applicable(apply_demorgan, "fn f() { $0 !x || !x }")
    }

    #[test]
    fn demorgan_doesnt_double_negation() {
        cov_mark::check!(demorgan_double_negation);
        check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }")
    }

    #[test]
    fn demorgan_doesnt_double_parens() {
        cov_mark::check!(demorgan_double_parens);
        check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }")
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 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.

// The crate store - a central repo for information collected about external
// crates and libraries

use schema;

use rustc::hir::def_id::{CrateNum, DefIndex};
use rustc::hir::map::definitions::DefPathTable;
use rustc::hir::svh::Svh;
use rustc::middle::cstore::{DepKind, ExternCrate, MetadataLoader};
use rustc::session::CrateDisambiguator;
use rustc_target::spec::PanicStrategy;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc::util::nodemap::{FxHashMap, NodeMap};

use rustc_data_structures::sync::{Lrc, RwLock, Lock};
use syntax::{ast, attr};
use syntax::edition::Edition;
use syntax::ext::base::SyntaxExtension;
use syntax::symbol::Symbol;
use syntax_pos;

pub use rustc::middle::cstore::{NativeLibrary, NativeLibraryKind, LinkagePreference};
pub use rustc::middle::cstore::NativeLibraryKind::*;
pub use rustc::middle::cstore::{CrateSource, LibSource, ForeignModule};

pub use cstore_impl::{provide, provide_extern};

// A map from external crate numbers (as decoded from some crate file) to
// local crate numbers (as generated during this session). Each external
// crate may refer to types in other external crates, and each has their
// own crate numbers.
pub type CrateNumMap = IndexVec<CrateNum, CrateNum>;

pub use rustc_data_structures::sync::MetadataRef;

pub struct MetadataBlob(pub MetadataRef);

/// Holds information about a syntax_pos::FileMap imported from another crate.
/// See `imported_filemaps()` for more information.
pub struct ImportedFileMap {
    /// This FileMap's byte-offset within the codemap of its original crate
    pub original_start_pos: syntax_pos::BytePos,
    /// The end of this FileMap within the codemap of its original crate
    pub original_end_pos: syntax_pos::BytePos,
    /// The imported FileMap's representation within the local codemap
    pub translated_filemap: Lrc<syntax_pos::FileMap>,
}

pub struct CrateMetadata {
    pub name: Symbol,

    /// Information about the extern crate that caused this crate to
    /// be loaded. If this is `None`, then the crate was injected
    /// (e.g., by the allocator)
    pub extern_crate: Lock<Option<ExternCrate>>,

    pub blob: MetadataBlob,
    pub cnum_map: CrateNumMap,
    pub cnum: CrateNum,
    pub dependencies: Lock<Vec<CrateNum>>,
    pub codemap_import_info: RwLock<Vec<ImportedFileMap>>,

    pub root: schema::CrateRoot,

    /// For each public item in this crate, we encode a key.  When the
    /// crate is loaded, we read all the keys and put them in this
    /// hashmap, which gives the reverse mapping.  This allows us to
    /// quickly retrace a `DefPath`, which is needed for incremental
    /// compilation support.
    pub def_path_table: Lrc<DefPathTable>,

    pub trait_impls: FxHashMap<(u32, DefIndex), schema::LazySeq<DefIndex>>,

    pub dep_kind: Lock<DepKind>,
    pub source: CrateSource,

    pub proc_macros: Option<Vec<(ast::Name, Lrc<SyntaxExtension>)>>,

    // Booleans derived from attributes
    pub compiler_builtins: bool,
    pub needs_allocator: bool,
    pub needs_panic_runtime: bool,
    pub no_builtins: bool,
    pub panic_runtime: bool,
    pub profiler_runtime: bool,
    pub sanitizer_runtime: bool,
}

pub struct CStore {
    metas: RwLock<IndexVec<CrateNum, Option<Lrc<CrateMetadata>>>>,
    /// Map from NodeId's of local extern crate statements to crate numbers
    extern_mod_crate_map: Lock<NodeMap<CrateNum>>,
    pub metadata_loader: Box<MetadataLoader + Sync>,
}

impl CStore {
    pub fn new(metadata_loader: Box<MetadataLoader + Sync>) -> CStore {
        CStore {
            // We add an empty entry for LOCAL_CRATE (which maps to zero) in
            // order to make array indices in `metas` match with the
            // corresponding `CrateNum`. This first entry will always remain
            // `None`.
            metas: RwLock::new(IndexVec::from_elem_n(None, 1)),
            extern_mod_crate_map: Lock::new(FxHashMap()),
            metadata_loader,
        }
    }

    pub(super) fn alloc_new_crate_num(&self) -> CrateNum {
        let mut metas = self.metas.borrow_mut();
        let cnum = CrateNum::new(metas.len());
        metas.push(None);
        cnum
    }

    pub(super) fn get_crate_data(&self, cnum: CrateNum) -> Lrc<CrateMetadata> {
        self.metas.borrow()[cnum].clone().unwrap()
    }

    pub(super) fn set_crate_data(&self, cnum: CrateNum, data: Lrc<CrateMetadata>) {
        let mut metas = self.metas.borrow_mut();
        assert!(metas[cnum].is_none(), "Overwriting crate metadata entry");
        metas[cnum] = Some(data);
    }

    pub(super) fn iter_crate_data<I>(&self, mut i: I)
        where I: FnMut(CrateNum, &Lrc<CrateMetadata>)
    {
        for (k, v) in self.metas.borrow().iter_enumerated() {
            if let &Some(ref v) = v {
                i(k, v);
            }
        }
    }

    pub(super) fn crate_dependencies_in_rpo(&self, krate: CrateNum) -> Vec<CrateNum> {
        let mut ordering = Vec::new();
        self.push_dependencies_in_postorder(&mut ordering, krate);
        ordering.reverse();
        ordering
    }

    pub(super) fn push_dependencies_in_postorder(&self,
                                                 ordering: &mut Vec<CrateNum>,
                                                 krate: CrateNum) {
        if ordering.contains(&krate) {
            return;
        }

        let data = self.get_crate_data(krate);
        for &dep in data.dependencies.borrow().iter() {
            if dep != krate {
                self.push_dependencies_in_postorder(ordering, dep);
            }
        }

        ordering.push(krate);
    }

    pub(super) fn do_postorder_cnums_untracked(&self) -> Vec<CrateNum> {
        let mut ordering = Vec::new();
        for (num, v) in self.metas.borrow().iter_enumerated() {
            if let &Some(_) = v {
                self.push_dependencies_in_postorder(&mut ordering, num);
            }
        }
        return ordering
    }

    pub(super) fn add_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId, cnum: CrateNum) {
        self.extern_mod_crate_map.borrow_mut().insert(emod_id, cnum);
    }

    pub(super) fn do_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> Option<CrateNum> {
        self.extern_mod_crate_map.borrow().get(&emod_id).cloned()
    }
}

impl CrateMetadata {
    pub fn name(&self) -> Symbol {
        self.root.name
    }

    pub fn hash(&self) -> Svh {
        self.root.hash
    }

    pub fn disambiguator(&self) -> CrateDisambiguator {
        self.root.disambiguator
    }

    pub fn has_global_allocator(&self) -> bool {
        self.root.has_global_allocator
    }

    pub fn has_default_lib_allocator(&self) -> bool {
        self.root.has_default_lib_allocator
    }

    pub fn panic_strategy(&self) -> PanicStrategy {
        self.root.panic_strategy.clone()
    }

    pub fn edition(&self) -> Edition {
        self.root.edition
    }
}
<commit_msg>Remove unnecessary clone call for panic_strategy<commit_after>// Copyright 2012-2014 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.

// The crate store - a central repo for information collected about external
// crates and libraries

use schema;

use rustc::hir::def_id::{CrateNum, DefIndex};
use rustc::hir::map::definitions::DefPathTable;
use rustc::hir::svh::Svh;
use rustc::middle::cstore::{DepKind, ExternCrate, MetadataLoader};
use rustc::session::CrateDisambiguator;
use rustc_target::spec::PanicStrategy;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc::util::nodemap::{FxHashMap, NodeMap};

use rustc_data_structures::sync::{Lrc, RwLock, Lock};
use syntax::{ast, attr};
use syntax::edition::Edition;
use syntax::ext::base::SyntaxExtension;
use syntax::symbol::Symbol;
use syntax_pos;

pub use rustc::middle::cstore::{NativeLibrary, NativeLibraryKind, LinkagePreference};
pub use rustc::middle::cstore::NativeLibraryKind::*;
pub use rustc::middle::cstore::{CrateSource, LibSource, ForeignModule};

pub use cstore_impl::{provide, provide_extern};

// A map from external crate numbers (as decoded from some crate file) to
// local crate numbers (as generated during this session). Each external
// crate may refer to types in other external crates, and each has their
// own crate numbers.
pub type CrateNumMap = IndexVec<CrateNum, CrateNum>;

pub use rustc_data_structures::sync::MetadataRef;

pub struct MetadataBlob(pub MetadataRef);

/// Holds information about a syntax_pos::FileMap imported from another crate.
/// See `imported_filemaps()` for more information.
pub struct ImportedFileMap {
    /// This FileMap's byte-offset within the codemap of its original crate
    pub original_start_pos: syntax_pos::BytePos,
    /// The end of this FileMap within the codemap of its original crate
    pub original_end_pos: syntax_pos::BytePos,
    /// The imported FileMap's representation within the local codemap
    pub translated_filemap: Lrc<syntax_pos::FileMap>,
}

pub struct CrateMetadata {
    pub name: Symbol,

    /// Information about the extern crate that caused this crate to
    /// be loaded. If this is `None`, then the crate was injected
    /// (e.g., by the allocator)
    pub extern_crate: Lock<Option<ExternCrate>>,

    pub blob: MetadataBlob,
    pub cnum_map: CrateNumMap,
    pub cnum: CrateNum,
    pub dependencies: Lock<Vec<CrateNum>>,
    pub codemap_import_info: RwLock<Vec<ImportedFileMap>>,

    pub root: schema::CrateRoot,

    /// For each public item in this crate, we encode a key.  When the
    /// crate is loaded, we read all the keys and put them in this
    /// hashmap, which gives the reverse mapping.  This allows us to
    /// quickly retrace a `DefPath`, which is needed for incremental
    /// compilation support.
    pub def_path_table: Lrc<DefPathTable>,

    pub trait_impls: FxHashMap<(u32, DefIndex), schema::LazySeq<DefIndex>>,

    pub dep_kind: Lock<DepKind>,
    pub source: CrateSource,

    pub proc_macros: Option<Vec<(ast::Name, Lrc<SyntaxExtension>)>>,

    // Booleans derived from attributes
    pub compiler_builtins: bool,
    pub needs_allocator: bool,
    pub needs_panic_runtime: bool,
    pub no_builtins: bool,
    pub panic_runtime: bool,
    pub profiler_runtime: bool,
    pub sanitizer_runtime: bool,
}

pub struct CStore {
    metas: RwLock<IndexVec<CrateNum, Option<Lrc<CrateMetadata>>>>,
    /// Map from NodeId's of local extern crate statements to crate numbers
    extern_mod_crate_map: Lock<NodeMap<CrateNum>>,
    pub metadata_loader: Box<MetadataLoader + Sync>,
}

impl CStore {
    pub fn new(metadata_loader: Box<MetadataLoader + Sync>) -> CStore {
        CStore {
            // We add an empty entry for LOCAL_CRATE (which maps to zero) in
            // order to make array indices in `metas` match with the
            // corresponding `CrateNum`. This first entry will always remain
            // `None`.
            metas: RwLock::new(IndexVec::from_elem_n(None, 1)),
            extern_mod_crate_map: Lock::new(FxHashMap()),
            metadata_loader,
        }
    }

    pub(super) fn alloc_new_crate_num(&self) -> CrateNum {
        let mut metas = self.metas.borrow_mut();
        let cnum = CrateNum::new(metas.len());
        metas.push(None);
        cnum
    }

    pub(super) fn get_crate_data(&self, cnum: CrateNum) -> Lrc<CrateMetadata> {
        self.metas.borrow()[cnum].clone().unwrap()
    }

    pub(super) fn set_crate_data(&self, cnum: CrateNum, data: Lrc<CrateMetadata>) {
        let mut metas = self.metas.borrow_mut();
        assert!(metas[cnum].is_none(), "Overwriting crate metadata entry");
        metas[cnum] = Some(data);
    }

    pub(super) fn iter_crate_data<I>(&self, mut i: I)
        where I: FnMut(CrateNum, &Lrc<CrateMetadata>)
    {
        for (k, v) in self.metas.borrow().iter_enumerated() {
            if let &Some(ref v) = v {
                i(k, v);
            }
        }
    }

    pub(super) fn crate_dependencies_in_rpo(&self, krate: CrateNum) -> Vec<CrateNum> {
        let mut ordering = Vec::new();
        self.push_dependencies_in_postorder(&mut ordering, krate);
        ordering.reverse();
        ordering
    }

    pub(super) fn push_dependencies_in_postorder(&self,
                                                 ordering: &mut Vec<CrateNum>,
                                                 krate: CrateNum) {
        if ordering.contains(&krate) {
            return;
        }

        let data = self.get_crate_data(krate);
        for &dep in data.dependencies.borrow().iter() {
            if dep != krate {
                self.push_dependencies_in_postorder(ordering, dep);
            }
        }

        ordering.push(krate);
    }

    pub(super) fn do_postorder_cnums_untracked(&self) -> Vec<CrateNum> {
        let mut ordering = Vec::new();
        for (num, v) in self.metas.borrow().iter_enumerated() {
            if let &Some(_) = v {
                self.push_dependencies_in_postorder(&mut ordering, num);
            }
        }
        return ordering
    }

    pub(super) fn add_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId, cnum: CrateNum) {
        self.extern_mod_crate_map.borrow_mut().insert(emod_id, cnum);
    }

    pub(super) fn do_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> Option<CrateNum> {
        self.extern_mod_crate_map.borrow().get(&emod_id).cloned()
    }
}

impl CrateMetadata {
    pub fn name(&self) -> Symbol {
        self.root.name
    }

    pub fn hash(&self) -> Svh {
        self.root.hash
    }

    pub fn disambiguator(&self) -> CrateDisambiguator {
        self.root.disambiguator
    }

    pub fn has_global_allocator(&self) -> bool {
        self.root.has_global_allocator
    }

    pub fn has_default_lib_allocator(&self) -> bool {
        self.root.has_default_lib_allocator
    }

    pub fn panic_strategy(&self) -> PanicStrategy {
        self.root.panic_strategy
    }

    pub fn edition(&self) -> Edition {
        self.root.edition
    }
}
<|endoftext|>


<commit_before>/// Figure 3.2: Create a file with a hole in it

/// Takeaway: On OSX the sparse file seems to use the full disk space:
/// > ./f02-file-with-hole
/// > du -h file.hole
/// 20K   file.hole
///
/// Apperently the sparse files are not using disk space but du is not able to spot this,
/// here's some explanation: http://superuser.com/questions/199109

extern crate libc;
#[macro_use(cstr, as_void)]
extern crate apue;

use libc::{mode_t, SEEK_SET, creat, write, lseek};
use apue::LibcResult;
use std::ffi::CString;

const FILE_MODE: mode_t = (libc::S_IRUSR | libc::S_IWUSR | libc::S_IRGRP | libc::S_IROTH);

fn main() {
    unsafe {
        let s1 = CString::new("abcdefghij");
        let s2 = CString::new("ABCDEFGHIJ");
        let fd = creat(cstr!("file.hole"), FILE_MODE).to_option().expect("creat error");
        assert!(write(fd, as_void!(s1.unwrap().as_bytes()), 10) == 10,
                "buffer write error");
        // offset is now 10
        lseek(fd, 16384, SEEK_SET).to_option().expect("lseek error");
        assert!(write(fd, as_void!(s2.unwrap().as_bytes()), 10) == 10,
                "buffer write error");
        // offset is now 16384
    }
}
<commit_msg>document better what happens with sparse files on HFS vs. ext4<commit_after>/// Figure 3.2: Create a file with a hole in it

/// Takeaway: On OSX the sparse file seems to use the full disk space:
///
/// > ./f02-file-with-hole
/// > cat file.hole > file.nohole
/// > ls -ls file.hole file.nohole
/// 40 -rw-r--r--  1 philipp  staff  16394 Nov 14 08:10 file.hole
/// 40 -rw-r--r--  1 philipp  staff  16394 Nov 14 08:10 file.nohole
///
/// Apperently the sparse files are not using disk space but ls -s is not able to spot this,
/// here's some explanation: http://superuser.com/questions/199109
/// Even in Virtual Box on OSX this is applying, so this seems to be a "feature" of HFS.
/// On a Linux host running on ext4 ls -ls shows 8 for file.hole and 40 for file.nohole

extern crate libc;
#[macro_use(cstr, as_void)]
extern crate apue;

use libc::{mode_t, SEEK_SET, creat, write, lseek};
use apue::LibcResult;
use std::ffi::CString;

const FILE_MODE: mode_t = (libc::S_IRUSR | libc::S_IWUSR | libc::S_IRGRP | libc::S_IROTH);

fn main() {
    unsafe {
        let s1 = CString::new("abcdefghij");
        let s2 = CString::new("ABCDEFGHIJ");
        let fd = creat(cstr!("file.hole"), FILE_MODE).to_option().expect("creat error");
        assert!(write(fd, as_void!(s1.unwrap().as_bytes()), 10) == 10,
                "buffer write error");
        // offset is now 10
        lseek(fd, 16384, SEEK_SET).to_option().expect("lseek error");
        assert!(write(fd, as_void!(s2.unwrap().as_bytes()), 10) == 10,
                "buffer write error");
        // offset is now 16384
    }
}
<|endoftext|>


<commit_before>use {Segment, Attempt, TimingMethod, TimeSpan};
use super::ComparisonGenerator;

#[derive(Copy, Clone, Debug)]
pub struct LastFinishedRun;

pub const NAME: &'static str = "Last Finished Run";

fn generate(segments: &mut [Segment], attempts: &[Attempt], method: TimingMethod) {
    let attempt = attempts
        .iter()
        .rev()
        .find(|a| a.time()[method].is_some());

    if let Some(attempt) = attempt {
        let id = attempt.index();
        let mut total_time = TimeSpan::zero();
        for segment in segments {
            let segment_time = segment
                .segment_history()
                .get(id)
                .and_then(|t| t[method]);

            let split_time = if let Some(segment_time) = segment_time {
                total_time += segment_time;
                Some(total_time)
            } else {
                None
            };

            segment.comparison_mut(NAME)[method] = split_time;
        }
    } else {
        for segment in segments {
            segment.comparison_mut(NAME)[method] = None;
        }
    }
}

impl ComparisonGenerator for LastFinishedRun {
    fn name(&self) -> &str {
        NAME
    }

    fn generate(&mut self, segments: &mut [Segment], attempts: &[Attempt]) {
        generate(segments, attempts, TimingMethod::RealTime);
        generate(segments, attempts, TimingMethod::GameTime);
    }
}
<commit_msg>Fix Handling of Segment History Element Missing<commit_after>use {Segment, Attempt, TimingMethod, TimeSpan};
use super::ComparisonGenerator;

#[derive(Copy, Clone, Debug)]
pub struct LastFinishedRun;

pub const NAME: &'static str = "Last Finished Run";

fn generate(segments: &mut [Segment], attempts: &[Attempt], method: TimingMethod) {
    let attempt = attempts
        .iter()
        .rev()
        .find(|a| a.time()[method].is_some());

    let mut remaining_segments = segments.iter_mut();

    if let Some(attempt) = attempt {
        let id = attempt.index();
        let mut total_time = TimeSpan::zero();
        while let Some(segment) = remaining_segments.next() {
            let segment_time = segment.segment_history().get(id).map(|t| t[method]);

            let split_time = match segment_time {
                Some(Some(segment_time)) => {
                    total_time += segment_time;
                    Some(total_time)
                }
                Some(None) => None,
                None => {
                    segment.comparison_mut(NAME)[method] = None;
                    break;
                }
            };

            segment.comparison_mut(NAME)[method] = split_time;
        }
    }

    for segment in remaining_segments {
        segment.comparison_mut(NAME)[method] = None;
    }
}

impl ComparisonGenerator for LastFinishedRun {
    fn name(&self) -> &str {
        NAME
    }

    fn generate(&mut self, segments: &mut [Segment], attempts: &[Attempt]) {
        generate(segments, attempts, TimingMethod::RealTime);
        generate(segments, attempts, TimingMethod::GameTime);
    }
}
<|endoftext|>


<commit_before>//! This is NOT a real example. This is a test designed to see if we can actually run the turtle
//! process

extern crate turtle;

use std::process;

use turtle::Turtle;

fn main() {
    let mut turtle = Turtle::new();

    turtle.set_speed(10);
    turtle.right(90.0);
    turtle.forward(10.0);
    process::exit(0);
}
<commit_msg>Trying to catch a panic<commit_after>//! This is NOT a real example. This is a test designed to see if we can actually run the turtle
//! process

extern crate turtle;

use std::panic;
use std::process;

use turtle::Turtle;

fn main() {
    panic::catch_unwind(|| {
        let mut turtle = Turtle::new();

        turtle.set_speed(10);
        turtle.right(90.0);
        turtle.forward(10.0);
        process::exit(0);
    }).unwrap_or_else(|_| {
        // Need to force the thread to exit with exit code 1 because the renderer has some
        // code that will call process::exit(0)
        eprintln!("panic!");
        process::exit(1);
    });
}
<|endoftext|>


<commit_before>use shiplift::Docker;
use tokio::prelude::Future;

fn main() {
    let docker = Docker::new();
    let volumes = docker.volumes();

    let fut = volumes
        .list()
        .map(|volumes| {
            for v in volumes {
                println!("volume -> {:#?}", v)
            }
        })
        .map_err(|e| eprintln!("Error: {}", e));;

    tokio::run(fut);
}
<commit_msg>Remove extra semicolon to fix cargo fmt --all -- --check (#195)<commit_after>use shiplift::Docker;
use tokio::prelude::Future;

fn main() {
    let docker = Docker::new();
    let volumes = docker.volumes();

    let fut = volumes
        .list()
        .map(|volumes| {
            for v in volumes {
                println!("volume -> {:#?}", v)
            }
        })
        .map_err(|e| eprintln!("Error: {}", e));

    tokio::run(fut);
}
<|endoftext|>


<commit_before>#![crate_type = "lib"]

// this will silence the warning about the fact that this uses unstable
// IO features:
#![feature(io)]

// this silences the warning about the .as_slice() function on line 58,
// which will eventually be replaced by &foo[..] or something.
#![feature(core)]

use std::io::Write;

// The println! macro depends on thread-local storage, which Haskell/C
// have probably not initialized. (Or, rather, the std::io interface
// does, which means that instead I print using this small helper
// function instead.) This seemed inconsistently necessary: on some
// computers, I could use println! without problems, whereas on others,
// I would get segfaults. This worked consistently:
fn print_safe(s: &str) {
    // failing is not really an option if we're calling from Haskell,
    // so we throw away the result in case it doesn't work.
    let _ = std::io::stdout().write(s.as_bytes());
}

// We aren't exposing the internals of this, so I don't bother giving it
// a C representation.
pub struct Point {
    x: u64,
    y: u64,
}

// Rust-land functions...
impl Point {
    fn new(x: u64, y: u64) -> Point {
        Point { x: x, y: y }
    }

    fn add_mut(&mut self, p: &Point) {
        self.x = self.x + p.x;
        self.y = self.y + p.y;
    }
}

// What we're doing here is a little bit unsafe---by exposing
// these elsewhere, we effectively transfer the 'ownership' out
// of Rust-land. In this case, we'll have a corresponding free
// function which takes ownership back again.
#[no_mangle]
pub extern fn mk_point(x: u64, y: u64) -> Box<Point> {
    Box::new(Point::new(x, y))
}

// We free something in Rust by… taking ownership of it and doing nothing.
#[no_mangle]
pub extern fn free_point(_: Box<Point>) {}

// Wrap our add_mut method for exporting.
#[no_mangle]
pub extern fn add_point(p1: &mut Point, p2: &Point) {
    p1.add_mut(p2);
}

// Wrap our print method for exporting.
#[no_mangle]
pub extern fn print_point(p: &Point) {
    print_safe(format!("Point(x={}, y={})\n", p.x, p.y).as_slice());
}
<commit_msg>Updated point.rs for Rust 1.0 release<commit_after>#![crate_type = "lib"]

// We aren't exposing the internals of this, so I don't bother giving it
// a C representation.
pub struct Point {
    x: u64,
    y: u64,
}

// Rust-land functions...
impl Point {
    fn new(x: u64, y: u64) -> Point {
        Point { x: x, y: y }
    }

    fn add_mut(&mut self, p: &Point) {
        self.x = self.x + p.x;
        self.y = self.y + p.y;
    }
}

// What we're doing here is a little bit unsafe---by exposing
// these elsewhere, we effectively transfer the 'ownership' out
// of Rust-land. In this case, we'll have a corresponding free
// function which takes ownership back again.
#[no_mangle]
pub extern fn mk_point(x: u64, y: u64) -> Box<Point> {
    Box::new(Point::new(x, y))
}

// We free something in Rust by… taking ownership of it and doing nothing.
#[no_mangle]
pub extern fn free_point(_: Box<Point>) {}

// Wrap our add_mut method for exporting.
#[no_mangle]
pub extern fn add_point(p1: &mut Point, p2: &Point) {
    p1.add_mut(p2);
}

// Wrap our print method for exporting.
#[no_mangle]
pub extern fn print_point(p: &Point) {
    println!("Point(x={}, y={})", p.x, p.y);
}
<|endoftext|>