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

use libc::{c_void, c_char, size_t, uintptr_t, free, malloc, realloc};
use unstable::intrinsics::TyDesc;
use unstable::raw;
use mem::size_of;

extern {
    fn abort();
}

#[inline]
pub fn get_box_size(body_size: uint, body_align: uint) -> uint {
    let header_size = size_of::<raw::Box<()>>();
    // FIXME (#2699): This alignment calculation is suspicious. Is it right?
    let total_size = align_to(header_size, body_align) + body_size;
    total_size
}

// Rounds |size| to the nearest |alignment|. Invariant: |alignment| is a power
// of two.
#[inline]
fn align_to(size: uint, align: uint) -> uint {
    assert!(align != 0);
    (size + align - 1) & !(align - 1)
}

/// A wrapper around libc::malloc, aborting on out-of-memory
pub unsafe fn malloc_raw(size: uint) -> *c_void {
    let p = malloc(size as size_t);
    if p.is_null() {
        // we need a non-allocating way to print an error here
        abort();
    }
    p
}

/// A wrapper around libc::realloc, aborting on out-of-memory
pub unsafe fn realloc_raw(ptr: *mut c_void, size: uint) -> *mut c_void {
    let p = realloc(ptr, size as size_t);
    if p.is_null() {
        // we need a non-allocating way to print an error here
        abort();
    }
    p
}

/// The allocator for unique pointers without contained managed pointers.
#[cfg(not(test))]
#[lang="exchange_malloc"]
#[inline]
pub unsafe fn exchange_malloc(size: uintptr_t) -> *c_char {
    malloc_raw(size as uint) as *c_char
}

// FIXME: #7496
#[cfg(not(test))]
#[lang="closure_exchange_malloc"]
#[inline]
pub unsafe fn closure_exchange_malloc_(td: *c_char, size: uintptr_t) -> *c_char {
    closure_exchange_malloc(td, size)
}

#[inline]
pub unsafe fn closure_exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    let td = td as *TyDesc;
    let size = size as uint;

    assert!(td.is_not_null());

    let total_size = get_box_size(size, (*td).align);
    let p = malloc_raw(total_size as uint);

    let box = p as *mut raw::Box<()>;
    (*box).type_desc = td;

    box as *c_char
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
// inside a landing pad may corrupt the state of the exception handler.
#[cfg(not(test))]
#[lang="exchange_free"]
#[inline]
pub unsafe fn exchange_free_(ptr: *c_char) {
    exchange_free(ptr)
}

pub unsafe fn exchange_free(ptr: *c_char) {
    free(ptr as *c_void);
}

#[cfg(test)]
mod bench {
    use extra::test::BenchHarness;

    #[bench]
    fn alloc_owned_small(bh: &mut BenchHarness) {
        do bh.iter {
            ~10;
        }
    }

    #[bench]
    fn alloc_owned_big(bh: &mut BenchHarness) {
        do bh.iter {
            ~[10, ..1000];
        }
    }
}
<commit_msg>The original fixme #2699 was removed back in PR #6053.<commit_after>// Copyright 2012 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.

use libc::{c_void, c_char, size_t, uintptr_t, free, malloc, realloc};
use unstable::intrinsics::TyDesc;
use unstable::raw;
use mem::size_of;

extern {
    fn abort();
}

#[inline]
pub fn get_box_size(body_size: uint, body_align: uint) -> uint {
    let header_size = size_of::<raw::Box<()>>();
    let total_size = align_to(header_size, body_align) + body_size;
    total_size
}

// Rounds |size| to the nearest |alignment|. Invariant: |alignment| is a power
// of two.
#[inline]
fn align_to(size: uint, align: uint) -> uint {
    assert!(align != 0);
    (size + align - 1) & !(align - 1)
}

/// A wrapper around libc::malloc, aborting on out-of-memory
pub unsafe fn malloc_raw(size: uint) -> *c_void {
    let p = malloc(size as size_t);
    if p.is_null() {
        // we need a non-allocating way to print an error here
        abort();
    }
    p
}

/// A wrapper around libc::realloc, aborting on out-of-memory
pub unsafe fn realloc_raw(ptr: *mut c_void, size: uint) -> *mut c_void {
    let p = realloc(ptr, size as size_t);
    if p.is_null() {
        // we need a non-allocating way to print an error here
        abort();
    }
    p
}

/// The allocator for unique pointers without contained managed pointers.
#[cfg(not(test))]
#[lang="exchange_malloc"]
#[inline]
pub unsafe fn exchange_malloc(size: uintptr_t) -> *c_char {
    malloc_raw(size as uint) as *c_char
}

// FIXME: #7496
#[cfg(not(test))]
#[lang="closure_exchange_malloc"]
#[inline]
pub unsafe fn closure_exchange_malloc_(td: *c_char, size: uintptr_t) -> *c_char {
    closure_exchange_malloc(td, size)
}

#[inline]
pub unsafe fn closure_exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
    let td = td as *TyDesc;
    let size = size as uint;

    assert!(td.is_not_null());

    let total_size = get_box_size(size, (*td).align);
    let p = malloc_raw(total_size as uint);

    let box = p as *mut raw::Box<()>;
    (*box).type_desc = td;

    box as *c_char
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
// inside a landing pad may corrupt the state of the exception handler.
#[cfg(not(test))]
#[lang="exchange_free"]
#[inline]
pub unsafe fn exchange_free_(ptr: *c_char) {
    exchange_free(ptr)
}

pub unsafe fn exchange_free(ptr: *c_char) {
    free(ptr as *c_void);
}

#[cfg(test)]
mod bench {
    use extra::test::BenchHarness;

    #[bench]
    fn alloc_owned_small(bh: &mut BenchHarness) {
        do bh.iter {
            ~10;
        }
    }

    #[bench]
    fn alloc_owned_big(bh: &mut BenchHarness) {
        do bh.iter {
            ~[10, ..1000];
        }
    }
}
<|endoftext|>


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

//! A native mutex and condition variable type
//!
//! This module contains bindings to the platform's native mutex/condition
//! variable primitives. It provides a single type, `Mutex`, which can be
//! statically initialized via the `MUTEX_INIT` value. This object serves as both a
//! mutex and a condition variable simultaneously.
//!
//! The lock is lazily initialized, but it can only be unsafely destroyed. A
//! statically initialized lock doesn't necessarily have a time at which it can
//! get deallocated. For this reason, there is no `Drop` implementation of the
//! mutex, but rather the `destroy()` method must be invoked manually if
//! destruction of the mutex is desired.
//!
//! It is not recommended to use this type for idiomatic rust use. This type is
//! appropriate where no other options are available, but other rust concurrency
//! primitives should be used before this type.
//!
//! # Example
//!
//!     use std::unstable::mutex::{Mutex, MUTEX_INIT};
//!
//!     // Use a statically initialized mutex
//!     static mut lock: Mutex = MUTEX_INIT;
//!
//!     unsafe {
//!         lock.lock();
//!         lock.unlock();
//!     }
//!
//!     // Use a normally initialized mutex
//!     let mut lock = Mutex::new();
//!     unsafe {
//!         lock.lock();
//!         lock.unlock();
//!         lock.destroy();
//!     }

#[allow(non_camel_case_types)];

pub struct Mutex {
    priv inner: imp::Mutex,
}

pub static MUTEX_INIT: Mutex = Mutex {
    inner: imp::MUTEX_INIT,
};

impl Mutex {
    /// Creates a new mutex
    pub unsafe fn new() -> Mutex {
        Mutex { inner: imp::Mutex::new() }
    }

    /// Acquires this lock. This assumes that the current thread does not
    /// already hold the lock.
    pub unsafe fn lock(&mut self) { self.inner.lock() }

    /// Attempts to acquire the lock. The value returned is whether the lock was
    /// acquired or not
    pub unsafe fn trylock(&mut self) -> bool { self.inner.trylock() }

    /// Unlocks the lock. This assumes that the current thread already holds the
    /// lock.
    pub unsafe fn unlock(&mut self) { self.inner.unlock() }

    /// Block on the internal condition variable.
    ///
    /// This function assumes that the lock is already held
    pub unsafe fn wait(&mut self) { self.inner.wait() }

    /// Signals a thread in `wait` to wake up
    pub unsafe fn signal(&mut self) { self.inner.signal() }

    /// This function is especially unsafe because there are no guarantees made
    /// that no other thread is currently holding the lock or waiting on the
    /// condition variable contained inside.
    pub unsafe fn destroy(&mut self) { self.inner.destroy() }
}

#[cfg(unix)]
mod imp {
    use libc;
    use self::os::{PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER,
                   pthread_mutex_t, pthread_cond_t};
    use unstable::intrinsics;

    type pthread_mutexattr_t = libc::c_void;
    type pthread_condattr_t = libc::c_void;

    #[cfg(target_os = "freebsd")]
    mod os {
        use libc;

        pub type pthread_mutex_t = *libc::c_void;
        pub type pthread_cond_t = *libc::c_void;

        pub static PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t =
            0 as pthread_mutex_t;
        pub static PTHREAD_COND_INITIALIZER: pthread_cond_t =
            0 as pthread_cond_t;
    }

    #[cfg(target_os = "macos")]
    mod os {
        use libc;

        #[cfg(target_arch = "x86_64")]
        static __PTHREAD_MUTEX_SIZE__: uint = 56;
        #[cfg(target_arch = "x86_64")]
        static __PTHREAD_COND_SIZE__: uint = 40;
        #[cfg(target_arch = "x86")]
        static __PTHREAD_MUTEX_SIZE__: uint = 40;
        #[cfg(target_arch = "x86")]
        static __PTHREAD_COND_SIZE__: uint = 24;
        static _PTHREAD_MUTEX_SIG_init: libc::c_long = 0x32AAABA7;
        static _PTHREAD_COND_SIG_init: libc::c_long = 0x3CB0B1BB;

        pub struct pthread_mutex_t {
            __sig: libc::c_long,
            __opaque: [u8, ..__PTHREAD_MUTEX_SIZE__],
        }
        pub struct pthread_cond_t {
            __sig: libc::c_long,
            __opaque: [u8, ..__PTHREAD_COND_SIZE__],
        }

        pub static PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
            __sig: _PTHREAD_MUTEX_SIG_init,
            __opaque: [0, ..__PTHREAD_MUTEX_SIZE__],
        };
        pub static PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
            __sig: _PTHREAD_COND_SIG_init,
            __opaque: [0, ..__PTHREAD_COND_SIZE__],
        };
    }

    #[cfg(target_os = "linux")]
    mod os {
        use libc;

        // minus 8 because we have an 'align' field
        #[cfg(target_arch = "x86_64")]
        static __SIZEOF_PTHREAD_MUTEX_T: uint = 40 - 8;
        #[cfg(target_arch = "x86")]
        static __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8;
        #[cfg(target_arch = "x86_64")]
        static __SIZEOF_PTHREAD_COND_T: uint = 48 - 8;
        #[cfg(target_arch = "x86")]
        static __SIZEOF_PTHREAD_COND_T: uint = 48 - 8;

        pub struct pthread_mutex_t {
            __align: libc::c_long,
            size: [u8, ..__SIZEOF_PTHREAD_MUTEX_T],
        }
        pub struct pthread_cond_t {
            __align: libc::c_longlong,
            size: [u8, ..__SIZEOF_PTHREAD_COND_T],
        }

        pub static PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
            __align: 0,
            size: [0, ..__SIZEOF_PTHREAD_MUTEX_T],
        };
        pub static PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
            __align: 0,
            size: [0, ..__SIZEOF_PTHREAD_COND_T],
        };
    }
    #[cfg(target_os = "android")]
    mod os {
        use libc;

        pub struct pthread_mutex_t { value: libc::c_int }
        pub struct pthread_cond_t { value: libc::c_int }

        pub static PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
            value: 0,
        };
        pub static PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
            value: 0,
        };
    }

    pub struct Mutex {
        priv lock: pthread_mutex_t,
        priv cond: pthread_cond_t,
    }

    pub static MUTEX_INIT: Mutex = Mutex {
        lock: PTHREAD_MUTEX_INITIALIZER,
        cond: PTHREAD_COND_INITIALIZER,
    };

    impl Mutex {
        pub unsafe fn new() -> Mutex {
            let mut m = Mutex {
                lock: intrinsics::init(),
                cond: intrinsics::init(),
            };

            pthread_mutex_init(&mut m.lock, 0 as *libc::c_void);
            pthread_cond_init(&mut m.cond, 0 as *libc::c_void);

            return m;
        }

        pub unsafe fn lock(&mut self) { pthread_mutex_lock(&mut self.lock); }
        pub unsafe fn unlock(&mut self) { pthread_mutex_unlock(&mut self.lock); }
        pub unsafe fn signal(&mut self) { pthread_cond_signal(&mut self.cond); }
        pub unsafe fn wait(&mut self) {
            pthread_cond_wait(&mut self.cond, &mut self.lock);
        }
        pub unsafe fn trylock(&mut self) -> bool {
            pthread_mutex_trylock(&mut self.lock) == 0
        }
        pub unsafe fn destroy(&mut self) {
            pthread_mutex_destroy(&mut self.lock);
            pthread_cond_destroy(&mut self.cond);
        }
    }

    extern {
        fn pthread_mutex_init(lock: *mut pthread_mutex_t,
                              attr: *pthread_mutexattr_t) -> libc::c_int;
        fn pthread_mutex_destroy(lock: *mut pthread_mutex_t) -> libc::c_int;
        fn pthread_cond_init(cond: *mut pthread_cond_t,
                              attr: *pthread_condattr_t) -> libc::c_int;
        fn pthread_cond_destroy(cond: *mut pthread_cond_t) -> libc::c_int;
        fn pthread_mutex_lock(lock: *mut pthread_mutex_t) -> libc::c_int;
        fn pthread_mutex_trylock(lock: *mut pthread_mutex_t) -> libc::c_int;
        fn pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> libc::c_int;

        fn pthread_cond_wait(cond: *mut pthread_cond_t,
                             lock: *mut pthread_mutex_t) -> libc::c_int;
        fn pthread_cond_signal(cond: *mut pthread_cond_t) -> libc::c_int;
    }
}

#[cfg(windows)]
mod imp {
    use rt::global_heap::malloc_raw;
    use libc::{HANDLE, BOOL, LPSECURITY_ATTRIBUTES, c_void, DWORD, LPCSTR};
    use libc;
    use ptr;
    use sync::atomics;

    type LPCRITICAL_SECTION = *mut c_void;
    static SPIN_COUNT: DWORD = 4000;
    #[cfg(target_arch = "x86")]
    static CRIT_SECTION_SIZE: uint = 24;

    pub struct Mutex {
        // pointers for the lock/cond handles, atomically updated
        priv lock: atomics::AtomicUint,
        priv cond: atomics::AtomicUint,
    }

    pub static MUTEX_INIT: Mutex = Mutex {
        lock: atomics::INIT_ATOMIC_UINT,
        cond: atomics::INIT_ATOMIC_UINT,
    };

    impl Mutex {
        pub unsafe fn new() -> Mutex {
            Mutex {
                lock: atomics::AtomicUint::new(init_lock()),
                cond: atomics::AtomicUint::new(init_cond()),
            }
        }
        pub unsafe fn lock(&mut self) {
            EnterCriticalSection(self.getlock() as LPCRITICAL_SECTION)
        }
        pub unsafe fn trylock(&mut self) -> bool {
            TryEnterCriticalSection(self.getlock() as LPCRITICAL_SECTION) != 0
        }
        pub unsafe fn unlock(&mut self) {
            LeaveCriticalSection(self.getlock() as LPCRITICAL_SECTION)
        }

        pub unsafe fn wait(&mut self) {
            self.unlock();
            WaitForSingleObject(self.getcond() as HANDLE, libc::INFINITE);
            self.lock();
        }

        pub unsafe fn signal(&mut self) {
            assert!(SetEvent(self.getcond() as HANDLE) != 0);
        }

        /// This function is especially unsafe because there are no guarantees made
        /// that no other thread is currently holding the lock or waiting on the
        /// condition variable contained inside.
        pub unsafe fn destroy(&mut self) {
            let lock = self.lock.swap(0, atomics::SeqCst);
            let cond = self.cond.swap(0, atomics::SeqCst);
            if lock != 0 { free_lock(lock) }
            if cond != 0 { free_cond(cond) }
        }

        unsafe fn getlock(&mut self) -> *mut c_void {
            match self.lock.load(atomics::SeqCst) {
                0 => {}
                n => return n as *mut c_void
            }
            let lock = init_lock();
            match self.lock.compare_and_swap(0, lock, atomics::SeqCst) {
                0 => return lock as *mut c_void,
                _ => {}
            }
            free_lock(lock);
            return self.lock.load(atomics::SeqCst) as *mut c_void;
        }

        unsafe fn getcond(&mut self) -> *mut c_void {
            match self.cond.load(atomics::SeqCst) {
                0 => {}
                n => return n as *mut c_void
            }
            let cond = init_cond();
            match self.cond.compare_and_swap(0, cond, atomics::SeqCst) {
                0 => return cond as *mut c_void,
                _ => {}
            }
            free_cond(cond);
            return self.cond.load(atomics::SeqCst) as *mut c_void;
        }
    }

    pub unsafe fn init_lock() -> uint {
        let block = malloc_raw(CRIT_SECTION_SIZE as uint) as *mut c_void;
        InitializeCriticalSectionAndSpinCount(block, SPIN_COUNT);
        return block as uint;
    }

    pub unsafe fn init_cond() -> uint {
        return CreateEventA(ptr::mut_null(), libc::FALSE, libc::FALSE,
                            ptr::null()) as uint;
    }

    pub unsafe fn free_lock(h: uint) {
        DeleteCriticalSection(h as LPCRITICAL_SECTION);
        libc::free(h as *mut c_void);
    }

    pub unsafe fn free_cond(h: uint) {
        let block = h as HANDLE;
        libc::CloseHandle(block);
    }

    extern "system" {
        fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
                        bManualReset: BOOL,
                        bInitialState: BOOL,
                        lpName: LPCSTR) -> HANDLE;
        fn InitializeCriticalSectionAndSpinCount(
                        lpCriticalSection: LPCRITICAL_SECTION,
                        dwSpinCount: DWORD) -> BOOL;
        fn DeleteCriticalSection(lpCriticalSection: LPCRITICAL_SECTION);
        fn EnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION);
        fn LeaveCriticalSection(lpCriticalSection: LPCRITICAL_SECTION);
        fn TryEnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION) -> BOOL;
        fn SetEvent(hEvent: HANDLE) -> BOOL;
        fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
    }
}

#[cfg(test)]
mod test {
    use prelude::*;

    use super::{Mutex, MUTEX_INIT};
    use rt::thread::Thread;

    #[test]
    fn somke_lock() {
        static mut lock: Mutex = MUTEX_INIT;
        unsafe {
            lock.lock();
            lock.unlock();
        }
    }

    #[test]
    fn somke_cond() {
        static mut lock: Mutex = MUTEX_INIT;
        unsafe {
            lock.lock();
            let t = Thread::start(proc() {
                lock.lock();
                lock.signal();
                lock.unlock();
            });
            lock.wait();
            lock.unlock();
            t.join();
        }
    }

    #[test]
    fn destroy_immediately() {
        unsafe {
            let mut m = Mutex::new();
            m.destroy();
        }
    }
}
<commit_msg>libstd: Add missing constants for arm/linux.<commit_after>// Copyright 2013-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.

//! A native mutex and condition variable type
//!
//! This module contains bindings to the platform's native mutex/condition
//! variable primitives. It provides a single type, `Mutex`, which can be
//! statically initialized via the `MUTEX_INIT` value. This object serves as both a
//! mutex and a condition variable simultaneously.
//!
//! The lock is lazily initialized, but it can only be unsafely destroyed. A
//! statically initialized lock doesn't necessarily have a time at which it can
//! get deallocated. For this reason, there is no `Drop` implementation of the
//! mutex, but rather the `destroy()` method must be invoked manually if
//! destruction of the mutex is desired.
//!
//! It is not recommended to use this type for idiomatic rust use. This type is
//! appropriate where no other options are available, but other rust concurrency
//! primitives should be used before this type.
//!
//! # Example
//!
//!     use std::unstable::mutex::{Mutex, MUTEX_INIT};
//!
//!     // Use a statically initialized mutex
//!     static mut lock: Mutex = MUTEX_INIT;
//!
//!     unsafe {
//!         lock.lock();
//!         lock.unlock();
//!     }
//!
//!     // Use a normally initialized mutex
//!     let mut lock = Mutex::new();
//!     unsafe {
//!         lock.lock();
//!         lock.unlock();
//!         lock.destroy();
//!     }

#[allow(non_camel_case_types)];

pub struct Mutex {
    priv inner: imp::Mutex,
}

pub static MUTEX_INIT: Mutex = Mutex {
    inner: imp::MUTEX_INIT,
};

impl Mutex {
    /// Creates a new mutex
    pub unsafe fn new() -> Mutex {
        Mutex { inner: imp::Mutex::new() }
    }

    /// Acquires this lock. This assumes that the current thread does not
    /// already hold the lock.
    pub unsafe fn lock(&mut self) { self.inner.lock() }

    /// Attempts to acquire the lock. The value returned is whether the lock was
    /// acquired or not
    pub unsafe fn trylock(&mut self) -> bool { self.inner.trylock() }

    /// Unlocks the lock. This assumes that the current thread already holds the
    /// lock.
    pub unsafe fn unlock(&mut self) { self.inner.unlock() }

    /// Block on the internal condition variable.
    ///
    /// This function assumes that the lock is already held
    pub unsafe fn wait(&mut self) { self.inner.wait() }

    /// Signals a thread in `wait` to wake up
    pub unsafe fn signal(&mut self) { self.inner.signal() }

    /// This function is especially unsafe because there are no guarantees made
    /// that no other thread is currently holding the lock or waiting on the
    /// condition variable contained inside.
    pub unsafe fn destroy(&mut self) { self.inner.destroy() }
}

#[cfg(unix)]
mod imp {
    use libc;
    use self::os::{PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER,
                   pthread_mutex_t, pthread_cond_t};
    use unstable::intrinsics;

    type pthread_mutexattr_t = libc::c_void;
    type pthread_condattr_t = libc::c_void;

    #[cfg(target_os = "freebsd")]
    mod os {
        use libc;

        pub type pthread_mutex_t = *libc::c_void;
        pub type pthread_cond_t = *libc::c_void;

        pub static PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t =
            0 as pthread_mutex_t;
        pub static PTHREAD_COND_INITIALIZER: pthread_cond_t =
            0 as pthread_cond_t;
    }

    #[cfg(target_os = "macos")]
    mod os {
        use libc;

        #[cfg(target_arch = "x86_64")]
        static __PTHREAD_MUTEX_SIZE__: uint = 56;
        #[cfg(target_arch = "x86_64")]
        static __PTHREAD_COND_SIZE__: uint = 40;
        #[cfg(target_arch = "x86")]
        static __PTHREAD_MUTEX_SIZE__: uint = 40;
        #[cfg(target_arch = "x86")]
        static __PTHREAD_COND_SIZE__: uint = 24;
        static _PTHREAD_MUTEX_SIG_init: libc::c_long = 0x32AAABA7;
        static _PTHREAD_COND_SIG_init: libc::c_long = 0x3CB0B1BB;

        pub struct pthread_mutex_t {
            __sig: libc::c_long,
            __opaque: [u8, ..__PTHREAD_MUTEX_SIZE__],
        }
        pub struct pthread_cond_t {
            __sig: libc::c_long,
            __opaque: [u8, ..__PTHREAD_COND_SIZE__],
        }

        pub static PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
            __sig: _PTHREAD_MUTEX_SIG_init,
            __opaque: [0, ..__PTHREAD_MUTEX_SIZE__],
        };
        pub static PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
            __sig: _PTHREAD_COND_SIG_init,
            __opaque: [0, ..__PTHREAD_COND_SIZE__],
        };
    }

    #[cfg(target_os = "linux")]
    mod os {
        use libc;

        // minus 8 because we have an 'align' field
        #[cfg(target_arch = "x86_64")]
        static __SIZEOF_PTHREAD_MUTEX_T: uint = 40 - 8;
        #[cfg(target_arch = "x86")]
        static __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8;
        #[cfg(target_arch = "arm")]
        static __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8;
        #[cfg(target_arch = "x86_64")]
        static __SIZEOF_PTHREAD_COND_T: uint = 48 - 8;
        #[cfg(target_arch = "x86")]
        static __SIZEOF_PTHREAD_COND_T: uint = 48 - 8;
        #[cfg(target_arch = "arm")]
        static __SIZEOF_PTHREAD_COND_T: uint = 48 - 8;

        pub struct pthread_mutex_t {
            __align: libc::c_longlong,
            size: [u8, ..__SIZEOF_PTHREAD_MUTEX_T],
        }
        pub struct pthread_cond_t {
            __align: libc::c_longlong,
            size: [u8, ..__SIZEOF_PTHREAD_COND_T],
        }

        pub static PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
            __align: 0,
            size: [0, ..__SIZEOF_PTHREAD_MUTEX_T],
        };
        pub static PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
            __align: 0,
            size: [0, ..__SIZEOF_PTHREAD_COND_T],
        };
    }
    #[cfg(target_os = "android")]
    mod os {
        use libc;

        pub struct pthread_mutex_t { value: libc::c_int }
        pub struct pthread_cond_t { value: libc::c_int }

        pub static PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t {
            value: 0,
        };
        pub static PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t {
            value: 0,
        };
    }

    pub struct Mutex {
        priv lock: pthread_mutex_t,
        priv cond: pthread_cond_t,
    }

    pub static MUTEX_INIT: Mutex = Mutex {
        lock: PTHREAD_MUTEX_INITIALIZER,
        cond: PTHREAD_COND_INITIALIZER,
    };

    impl Mutex {
        pub unsafe fn new() -> Mutex {
            let mut m = Mutex {
                lock: intrinsics::init(),
                cond: intrinsics::init(),
            };

            pthread_mutex_init(&mut m.lock, 0 as *libc::c_void);
            pthread_cond_init(&mut m.cond, 0 as *libc::c_void);

            return m;
        }

        pub unsafe fn lock(&mut self) { pthread_mutex_lock(&mut self.lock); }
        pub unsafe fn unlock(&mut self) { pthread_mutex_unlock(&mut self.lock); }
        pub unsafe fn signal(&mut self) { pthread_cond_signal(&mut self.cond); }
        pub unsafe fn wait(&mut self) {
            pthread_cond_wait(&mut self.cond, &mut self.lock);
        }
        pub unsafe fn trylock(&mut self) -> bool {
            pthread_mutex_trylock(&mut self.lock) == 0
        }
        pub unsafe fn destroy(&mut self) {
            pthread_mutex_destroy(&mut self.lock);
            pthread_cond_destroy(&mut self.cond);
        }
    }

    extern {
        fn pthread_mutex_init(lock: *mut pthread_mutex_t,
                              attr: *pthread_mutexattr_t) -> libc::c_int;
        fn pthread_mutex_destroy(lock: *mut pthread_mutex_t) -> libc::c_int;
        fn pthread_cond_init(cond: *mut pthread_cond_t,
                              attr: *pthread_condattr_t) -> libc::c_int;
        fn pthread_cond_destroy(cond: *mut pthread_cond_t) -> libc::c_int;
        fn pthread_mutex_lock(lock: *mut pthread_mutex_t) -> libc::c_int;
        fn pthread_mutex_trylock(lock: *mut pthread_mutex_t) -> libc::c_int;
        fn pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> libc::c_int;

        fn pthread_cond_wait(cond: *mut pthread_cond_t,
                             lock: *mut pthread_mutex_t) -> libc::c_int;
        fn pthread_cond_signal(cond: *mut pthread_cond_t) -> libc::c_int;
    }
}

#[cfg(windows)]
mod imp {
    use rt::global_heap::malloc_raw;
    use libc::{HANDLE, BOOL, LPSECURITY_ATTRIBUTES, c_void, DWORD, LPCSTR};
    use libc;
    use ptr;
    use sync::atomics;

    type LPCRITICAL_SECTION = *mut c_void;
    static SPIN_COUNT: DWORD = 4000;
    #[cfg(target_arch = "x86")]
    static CRIT_SECTION_SIZE: uint = 24;

    pub struct Mutex {
        // pointers for the lock/cond handles, atomically updated
        priv lock: atomics::AtomicUint,
        priv cond: atomics::AtomicUint,
    }

    pub static MUTEX_INIT: Mutex = Mutex {
        lock: atomics::INIT_ATOMIC_UINT,
        cond: atomics::INIT_ATOMIC_UINT,
    };

    impl Mutex {
        pub unsafe fn new() -> Mutex {
            Mutex {
                lock: atomics::AtomicUint::new(init_lock()),
                cond: atomics::AtomicUint::new(init_cond()),
            }
        }
        pub unsafe fn lock(&mut self) {
            EnterCriticalSection(self.getlock() as LPCRITICAL_SECTION)
        }
        pub unsafe fn trylock(&mut self) -> bool {
            TryEnterCriticalSection(self.getlock() as LPCRITICAL_SECTION) != 0
        }
        pub unsafe fn unlock(&mut self) {
            LeaveCriticalSection(self.getlock() as LPCRITICAL_SECTION)
        }

        pub unsafe fn wait(&mut self) {
            self.unlock();
            WaitForSingleObject(self.getcond() as HANDLE, libc::INFINITE);
            self.lock();
        }

        pub unsafe fn signal(&mut self) {
            assert!(SetEvent(self.getcond() as HANDLE) != 0);
        }

        /// This function is especially unsafe because there are no guarantees made
        /// that no other thread is currently holding the lock or waiting on the
        /// condition variable contained inside.
        pub unsafe fn destroy(&mut self) {
            let lock = self.lock.swap(0, atomics::SeqCst);
            let cond = self.cond.swap(0, atomics::SeqCst);
            if lock != 0 { free_lock(lock) }
            if cond != 0 { free_cond(cond) }
        }

        unsafe fn getlock(&mut self) -> *mut c_void {
            match self.lock.load(atomics::SeqCst) {
                0 => {}
                n => return n as *mut c_void
            }
            let lock = init_lock();
            match self.lock.compare_and_swap(0, lock, atomics::SeqCst) {
                0 => return lock as *mut c_void,
                _ => {}
            }
            free_lock(lock);
            return self.lock.load(atomics::SeqCst) as *mut c_void;
        }

        unsafe fn getcond(&mut self) -> *mut c_void {
            match self.cond.load(atomics::SeqCst) {
                0 => {}
                n => return n as *mut c_void
            }
            let cond = init_cond();
            match self.cond.compare_and_swap(0, cond, atomics::SeqCst) {
                0 => return cond as *mut c_void,
                _ => {}
            }
            free_cond(cond);
            return self.cond.load(atomics::SeqCst) as *mut c_void;
        }
    }

    pub unsafe fn init_lock() -> uint {
        let block = malloc_raw(CRIT_SECTION_SIZE as uint) as *mut c_void;
        InitializeCriticalSectionAndSpinCount(block, SPIN_COUNT);
        return block as uint;
    }

    pub unsafe fn init_cond() -> uint {
        return CreateEventA(ptr::mut_null(), libc::FALSE, libc::FALSE,
                            ptr::null()) as uint;
    }

    pub unsafe fn free_lock(h: uint) {
        DeleteCriticalSection(h as LPCRITICAL_SECTION);
        libc::free(h as *mut c_void);
    }

    pub unsafe fn free_cond(h: uint) {
        let block = h as HANDLE;
        libc::CloseHandle(block);
    }

    extern "system" {
        fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
                        bManualReset: BOOL,
                        bInitialState: BOOL,
                        lpName: LPCSTR) -> HANDLE;
        fn InitializeCriticalSectionAndSpinCount(
                        lpCriticalSection: LPCRITICAL_SECTION,
                        dwSpinCount: DWORD) -> BOOL;
        fn DeleteCriticalSection(lpCriticalSection: LPCRITICAL_SECTION);
        fn EnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION);
        fn LeaveCriticalSection(lpCriticalSection: LPCRITICAL_SECTION);
        fn TryEnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION) -> BOOL;
        fn SetEvent(hEvent: HANDLE) -> BOOL;
        fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
    }
}

#[cfg(test)]
mod test {
    use prelude::*;

    use super::{Mutex, MUTEX_INIT};
    use rt::thread::Thread;

    #[test]
    fn somke_lock() {
        static mut lock: Mutex = MUTEX_INIT;
        unsafe {
            lock.lock();
            lock.unlock();
        }
    }

    #[test]
    fn somke_cond() {
        static mut lock: Mutex = MUTEX_INIT;
        unsafe {
            lock.lock();
            let t = Thread::start(proc() {
                lock.lock();
                lock.signal();
                lock.unlock();
            });
            lock.wait();
            lock.unlock();
            t.join();
        }
    }

    #[test]
    fn destroy_immediately() {
        unsafe {
            let mut m = Mutex::new();
            m.destroy();
        }
    }
}
<|endoftext|>


<commit_before>use mem_map::*;
use device::Device;

#[allow(dead_code)] // TODO - remove when the rendering is written
const COLOUR_MAP: [u32; 4] = [0xff7e8429, 0xff527a4b, 0xff315d4b, 0xff29473e];
const WIDTH: usize = 160;
const HEIGHT: usize = 144;

pub struct Gpu {
    vram: Box<[u8]>, // VRAM - mapped to 0x8000 - 0x9FFF
    obj_attr_table: Box<[u8]>, // Obj/Sprite Attribute Table (OAM) - mapped to 0xfe00 - 0xfea0
    frame_buffer: Box<[u32]>,

    lcd_control: LcdControlReg, // 0xff40 - LCDC
    lcdc_status: LcdcStatusReg, // 0xff41 - STAT
    scy: u8, // 0xff42 - SCY scroll background Y position
    scx: u8, // 0xff43 - SCX scroll background X position
    ly: u8, // 0xff44 - LY scanline being sent to LCD driver, reset on write
    lyc: u8, // 0xff45 - LYC if equal to LY STAT.coincident is set
    dma_transfer: u8, // 0xff46 - when set initiates DMA transfer (~160 microseconds)
    bg_palette_data: PaletteDataReg, // 0xff47 - BG & Window palette data
    obj0_palette_data: PaletteDataReg, // 0xff48 OBJ0 palette data
    obj1_palette_data: PaletteDataReg, // 0xff49 OBJ1 palette data
    wy: u8, // 0xff4a - window Y position
    wx: u8, // 0xff4b - window X position, offset from screen coords by 7

    cycles: u16,
}

impl Gpu {
    pub fn new() -> Self {
        Gpu {
            vram: vec![0; VRAM_LENGTH as usize].into_boxed_slice(),
            obj_attr_table: vec![0; OAM_LENGTH as usize].into_boxed_slice(),
            frame_buffer: vec![COLOUR_MAP[0]; WIDTH * HEIGHT].into_boxed_slice(),

            lcd_control: LcdControlReg::default(),
            lcdc_status: LcdcStatusReg::default(),
            scy: 0,
            scx: 0,
            ly: 0,
            lyc: 0,
            wy: 0,
            wx: 0,
            bg_palette_data: PaletteDataReg::default(),
            obj0_palette_data: PaletteDataReg::default(),
            obj1_palette_data: PaletteDataReg::default(),
            dma_transfer: 0,

            cycles: 0,
        }
    }

    pub fn read_vram(&self, addr: u16) -> u8 {
        self.vram[addr as usize]
    }

    pub fn write_vram(&mut self, addr: u16, val: u8) {
        self.vram[addr as usize] = val;
    }

    pub fn read_oam(&self, addr: u16) -> u8 {
        self.obj_attr_table[addr as usize]
    }

    pub fn write_oam(&mut self, addr: u16, val: u8) {
        self.obj_attr_table[addr as usize] = val;
    }

    pub fn read_reg(&self, addr: u16) -> u8 {
        match addr {
            0xff40 => self.lcd_control.into(),
            0xff41 => self.lcdc_status.into(),
            0xff42 => self.scy,
            0xff43 => self.scx,
            0xff44 => self.ly,
            0xff45 => self.lyc,
            0xff46 => self.dma_transfer,
            0xff47 => self.bg_palette_data.into(),
            0xff48 => self.obj0_palette_data.into(),
            0xff49 => self.obj1_palette_data.into(),
            0xff4a => self.wy,
            0xff4b => self.wx,
            _ => panic!("Read from non-gpu register in gpu {:04x}", addr),
        }
    }

    pub fn write_reg(&mut self, addr: u16, val: u8) {
        match addr {
            0xff40 => self.lcd_control = val.into(),
            0xff41 => self.lcdc_status = val.into(),
            0xff42 => self.scy = val,
            0xff43 => self.scx = val,
            0xff44 => self.ly = val,
            0xff45 => self.lyc = val,
            0xff46 => self.dma_transfer = val,
            0xff47 => self.bg_palette_data = val.into(),
            0xff48 => self.obj0_palette_data = val.into(),
            0xff49 => self.obj1_palette_data = val.into(),
            0xff4a => self.wy = val,
            0xff4b => self.wx = val,
            _ => {
                panic!("Write to non-gpu register in gpu {:04x} = {:02x}",
                       addr,
                       val)
            }
        }
    }

    pub fn step(&mut self, cycles: u16, device: &mut Device) -> u8 {
        if !self.lcd_control.lcd_control_op {
            return 0;
        }

        let mut ret = 0;
        for _ in 0..cycles {
            ret |= self.inner_step(device);
        }

        ret
    }

    pub fn get_width(&self) -> usize {
        WIDTH
    }

    pub fn get_height(&self) -> usize {
        HEIGHT
    }

    fn inner_step(&mut self, device: &mut Device) -> u8 {
        let mut ret = 0;
        self.cycles += 1;

        if self.lcdc_status.mode == 0 && self.cycles == 4 {
            if self.ly < 144 {
                self.lcdc_status.mode = 2;
            } else {
                self.lcdc_status.mode = 1;

                ret |= 0x01;

                device.set_frame_buffer(&self.frame_buffer);
            }
        } else if self.lcdc_status.mode == 0 && self.cycles == 456 {
            self.ly += 1;
            self.cycles = 0;
        } else if self.lcdc_status.mode == 1 {
            if self.cycles == 456 {
                self.cycles = 0;

                if self.ly == 0 {
                    self.lcdc_status.mode = 0;
                } else {
                    self.ly += 1;
                }
            } else if self.ly == 153 && self.cycles == 5 {
                self.ly = 0;
            }
        } else if self.lcdc_status.mode == 2 && self.cycles == 85 {
            self.lcdc_status.mode = 3;

            self.render_background();
        } else if self.lcdc_status.mode == 3 && self.cycles == 260 {
            // TODO - this mode should have variable length
            self.lcdc_status.mode = 0
        }

        ret
    }

    fn render_background(&mut self) {
        let background_row = self.ly.wrapping_add(self.scy);

        for i in 0..WIDTH as u8 {
            let background_col = self.scx.wrapping_add(i);

            let tile_offset = self.get_background_tile_offset(background_row / 8,
                                                              background_col / 8);
            let colour = self.get_tile_pixel(tile_offset, background_row % 8, background_col % 8);

            self.frame_buffer[(self.ly as usize * WIDTH) + i as usize] = colour;
        }
    }

    // Returns the offset in self.vram of the background tile
    fn get_background_tile_offset(&self, row: u8, col: u8) -> usize {
        let tile_idx_base = if self.lcd_control.bg_tile_map_display {
            0x1c00
        } else {
            0x1800
        };
        let tile_idx_offset = (row as usize * 32) + col as usize;
        let tile_index = self.vram[tile_idx_base + tile_idx_offset];

        if self.lcd_control.bg_win_tile_data {
            tile_index as usize * 16
        } else {
            (((tile_index as i8) as isize) + 256) as usize * 16
        }
    }

    fn get_tile_pixel(&self, tile_offset: usize, row: u8, col: u8) -> u32 {
        let offset = tile_offset + (row as usize * 2);

        let upper_col = self.vram[offset + 1] >> (7 - col) & 1;
        let lower_col = self.vram[offset] >> (7 - col) & 1;
        let tile_colour = upper_col << 1 | lower_col;

        // TODO - lookup actual colour palette
        COLOUR_MAP[tile_colour as usize]
    }
}

#[derive(Default, Copy, Clone)]
pub struct LcdControlReg {
    bg_window_display: bool,
    obj_display: bool,
    obj_size: bool,
    bg_tile_map_display: bool,
    bg_win_tile_data: bool,
    window_display: bool,
    win_tile_map_display: bool,
    lcd_control_op: bool,
}

impl From<u8> for LcdControlReg {
    fn from(val: u8) -> Self {
        LcdControlReg {
            bg_window_display: val & 1 != 0,
            obj_display: val & (1 << 1) != 0,
            obj_size: val & (1 << 2) != 0,
            bg_tile_map_display: val & (1 << 3) != 0,
            bg_win_tile_data: val & (1 << 4) != 0,
            window_display: val & (1 << 5) != 0,
            win_tile_map_display: val & (1 << 6) != 0,
            lcd_control_op: val & (1 << 7) != 0,
        }
    }
}

impl Into<u8> for LcdControlReg {
    fn into(self) -> u8 {
        let mut ret: u8 = 0;
        if self.bg_window_display {
            ret |= 1;
        }
        if self.obj_display {
            ret |= 1 << 1;
        }
        if self.obj_size {
            ret |= 1 << 2;
        }
        if self.bg_tile_map_display {
            ret |= 1 << 3;
        }
        if self.bg_win_tile_data {
            ret |= 1 << 4;
        }
        if self.window_display {
            ret |= 1 << 5;
        }
        if self.win_tile_map_display {
            ret |= 1 << 6;
        }
        if self.lcd_control_op {
            ret |= 1 << 7;
        }
        ret
    }
}

#[derive(Default, Copy, Clone)]
pub struct LcdcStatusReg {
    coincidence_interrupt_enable: bool,
    oam_interrupt_enable: bool,
    vblank_interrupt_enable: bool,
    hblank_interrupt_enable: bool,
    coincidence_flag: bool,
    mode: u8,
}

impl From<u8> for LcdcStatusReg {
    fn from(val: u8) -> Self {
        LcdcStatusReg {
            coincidence_interrupt_enable: val & (1 << 6) != 0,
            oam_interrupt_enable: val & (1 << 5) != 0,
            vblank_interrupt_enable: val & (1 << 4) != 0,
            hblank_interrupt_enable: val & (1 << 3) != 0,
            coincidence_flag: val & (1 << 2) != 0,
            mode: val & 0x3,
        }
    }
}

impl Into<u8> for LcdcStatusReg {
    fn into(self) -> u8 {
        let mut ret = self.mode;

        if self.coincidence_interrupt_enable {
            ret |= 1 << 6;
        }
        if self.oam_interrupt_enable {
            ret |= 1 << 5;
        }
        if self.vblank_interrupt_enable {
            ret |= 1 << 4;
        }
        if self.hblank_interrupt_enable {
            ret |= 1 << 3;
        }
        if self.coincidence_flag {
            ret |= 1 << 2;
        }
        ret
    }
}

#[derive(Default, Copy, Clone)]
pub struct PaletteDataReg {
    col0_shade: u8,
    col1_shade: u8,
    col2_shade: u8,
    col3_shade: u8,
}

impl From<u8> for PaletteDataReg {
    fn from(val: u8) -> Self {
        PaletteDataReg {
            col0_shade: val & 0x3,
            col1_shade: (val >> 2) & 0x3,
            col2_shade: (val >> 4) & 0x3,
            col3_shade: (val >> 6) & 0x3,
        }
    }
}

impl Into<u8> for PaletteDataReg {
    fn into(self) -> u8 {
        self.col0_shade | (self.col1_shade << 2) | (self.col2_shade << 4) | (self.col3_shade << 6)
    }
}
<commit_msg>Implement palette lookup for background<commit_after>use mem_map::*;
use device::Device;

#[allow(dead_code)] // TODO - remove when the rendering is written
const COLOUR_MAP: [u32; 4] = [0xff7e8429, 0xff527a4b, 0xff315d4b, 0xff29473e];
const WIDTH: usize = 160;
const HEIGHT: usize = 144;

pub struct Gpu {
    vram: Box<[u8]>, // VRAM - mapped to 0x8000 - 0x9FFF
    obj_attr_table: Box<[u8]>, // Obj/Sprite Attribute Table (OAM) - mapped to 0xfe00 - 0xfea0
    frame_buffer: Box<[u32]>,

    lcd_control: LcdControlReg, // 0xff40 - LCDC
    lcdc_status: LcdcStatusReg, // 0xff41 - STAT
    scy: u8, // 0xff42 - SCY scroll background Y position
    scx: u8, // 0xff43 - SCX scroll background X position
    ly: u8, // 0xff44 - LY scanline being sent to LCD driver, reset on write
    lyc: u8, // 0xff45 - LYC if equal to LY STAT.coincident is set
    dma_transfer: u8, // 0xff46 - when set initiates DMA transfer (~160 microseconds)
    bg_palette_data: PaletteDataReg, // 0xff47 - BG & Window palette data
    obj0_palette_data: PaletteDataReg, // 0xff48 OBJ0 palette data
    obj1_palette_data: PaletteDataReg, // 0xff49 OBJ1 palette data
    wy: u8, // 0xff4a - window Y position
    wx: u8, // 0xff4b - window X position, offset from screen coords by 7

    cycles: u16,
}

impl Gpu {
    pub fn new() -> Self {
        Gpu {
            vram: vec![0; VRAM_LENGTH as usize].into_boxed_slice(),
            obj_attr_table: vec![0; OAM_LENGTH as usize].into_boxed_slice(),
            frame_buffer: vec![COLOUR_MAP[0]; WIDTH * HEIGHT].into_boxed_slice(),

            lcd_control: LcdControlReg::default(),
            lcdc_status: LcdcStatusReg::default(),
            scy: 0,
            scx: 0,
            ly: 0,
            lyc: 0,
            wy: 0,
            wx: 0,
            bg_palette_data: PaletteDataReg::default(),
            obj0_palette_data: PaletteDataReg::default(),
            obj1_palette_data: PaletteDataReg::default(),
            dma_transfer: 0,

            cycles: 0,
        }
    }

    pub fn read_vram(&self, addr: u16) -> u8 {
        self.vram[addr as usize]
    }

    pub fn write_vram(&mut self, addr: u16, val: u8) {
        self.vram[addr as usize] = val;
    }

    pub fn read_oam(&self, addr: u16) -> u8 {
        self.obj_attr_table[addr as usize]
    }

    pub fn write_oam(&mut self, addr: u16, val: u8) {
        self.obj_attr_table[addr as usize] = val;
    }

    pub fn read_reg(&self, addr: u16) -> u8 {
        match addr {
            0xff40 => self.lcd_control.into(),
            0xff41 => self.lcdc_status.into(),
            0xff42 => self.scy,
            0xff43 => self.scx,
            0xff44 => self.ly,
            0xff45 => self.lyc,
            0xff46 => self.dma_transfer,
            0xff47 => self.bg_palette_data.into(),
            0xff48 => self.obj0_palette_data.into(),
            0xff49 => self.obj1_palette_data.into(),
            0xff4a => self.wy,
            0xff4b => self.wx,
            _ => panic!("Read from non-gpu register in gpu {:04x}", addr),
        }
    }

    pub fn write_reg(&mut self, addr: u16, val: u8) {
        match addr {
            0xff40 => self.lcd_control = val.into(),
            0xff41 => self.lcdc_status = val.into(),
            0xff42 => self.scy = val,
            0xff43 => self.scx = val,
            0xff44 => self.ly = val,
            0xff45 => self.lyc = val,
            0xff46 => self.dma_transfer = val,
            0xff47 => self.bg_palette_data = val.into(),
            0xff48 => self.obj0_palette_data = val.into(),
            0xff49 => self.obj1_palette_data = val.into(),
            0xff4a => self.wy = val,
            0xff4b => self.wx = val,
            _ => {
                panic!("Write to non-gpu register in gpu {:04x} = {:02x}",
                       addr,
                       val)
            }
        }
    }

    pub fn step(&mut self, cycles: u16, device: &mut Device) -> u8 {
        if !self.lcd_control.lcd_control_op {
            return 0;
        }

        let mut ret = 0;
        for _ in 0..cycles {
            ret |= self.inner_step(device);
        }

        ret
    }

    pub fn get_width(&self) -> usize {
        WIDTH
    }

    pub fn get_height(&self) -> usize {
        HEIGHT
    }

    fn inner_step(&mut self, device: &mut Device) -> u8 {
        let mut ret = 0;
        self.cycles += 1;

        if self.lcdc_status.mode == 0 && self.cycles == 4 {
            if self.ly < 144 {
                self.lcdc_status.mode = 2;
            } else {
                self.lcdc_status.mode = 1;

                ret |= 0x01;

                device.set_frame_buffer(&self.frame_buffer);
            }
        } else if self.lcdc_status.mode == 0 && self.cycles == 456 {
            self.ly += 1;
            self.cycles = 0;
        } else if self.lcdc_status.mode == 1 {
            if self.cycles == 456 {
                self.cycles = 0;

                if self.ly == 0 {
                    self.lcdc_status.mode = 0;
                } else {
                    self.ly += 1;
                }
            } else if self.ly == 153 && self.cycles == 5 {
                self.ly = 0;
            }
        } else if self.lcdc_status.mode == 2 && self.cycles == 85 {
            self.lcdc_status.mode = 3;

            self.render_background();
        } else if self.lcdc_status.mode == 3 && self.cycles == 260 {
            // TODO - this mode should have variable length
            self.lcdc_status.mode = 0
        }

        ret
    }

    fn render_background(&mut self) {
        let background_row = self.ly.wrapping_add(self.scy);

        for i in 0..WIDTH as u8 {
            let background_col = self.scx.wrapping_add(i);

            let tile_offset = self.get_background_tile_offset(background_row / 8,
                                                              background_col / 8);
            let colour = self.get_tile_pixel(tile_offset, background_row % 8, background_col % 8);

            self.frame_buffer[(self.ly as usize * WIDTH) + i as usize] = colour;
        }
    }

    // Returns the offset in self.vram of the background tile
    fn get_background_tile_offset(&self, row: u8, col: u8) -> usize {
        let tile_idx_base = if self.lcd_control.bg_tile_map_display {
            0x1c00
        } else {
            0x1800
        };
        let tile_idx_offset = (row as usize * 32) + col as usize;
        let tile_index = self.vram[tile_idx_base + tile_idx_offset];

        if self.lcd_control.bg_win_tile_data {
            tile_index as usize * 16
        } else {
            (((tile_index as i8) as isize) + 256) as usize * 16
        }
    }

    fn get_tile_pixel(&self, tile_offset: usize, row: u8, col: u8) -> u32 {
        let offset = tile_offset + (row as usize * 2);

        let upper_col = self.vram[offset + 1] >> (7 - col) & 1;
        let lower_col = self.vram[offset] >> (7 - col) & 1;
        let tile_colour = upper_col << 1 | lower_col;

        let col_index = match tile_colour {
            0 => self.bg_palette_data.col0_shade,
            1 => self.bg_palette_data.col1_shade,
            2 => self.bg_palette_data.col2_shade,
            3 => self.bg_palette_data.col3_shade,
            _ => unreachable!(),
        };
        COLOUR_MAP[col_index]
    }
}

#[derive(Default, Copy, Clone)]
pub struct LcdControlReg {
    bg_window_display: bool,
    obj_display: bool,
    obj_size: bool,
    bg_tile_map_display: bool,
    bg_win_tile_data: bool,
    window_display: bool,
    win_tile_map_display: bool,
    lcd_control_op: bool,
}

impl From<u8> for LcdControlReg {
    fn from(val: u8) -> Self {
        LcdControlReg {
            bg_window_display: val & 1 != 0,
            obj_display: val & (1 << 1) != 0,
            obj_size: val & (1 << 2) != 0,
            bg_tile_map_display: val & (1 << 3) != 0,
            bg_win_tile_data: val & (1 << 4) != 0,
            window_display: val & (1 << 5) != 0,
            win_tile_map_display: val & (1 << 6) != 0,
            lcd_control_op: val & (1 << 7) != 0,
        }
    }
}

impl Into<u8> for LcdControlReg {
    fn into(self) -> u8 {
        let mut ret: u8 = 0;
        if self.bg_window_display {
            ret |= 1;
        }
        if self.obj_display {
            ret |= 1 << 1;
        }
        if self.obj_size {
            ret |= 1 << 2;
        }
        if self.bg_tile_map_display {
            ret |= 1 << 3;
        }
        if self.bg_win_tile_data {
            ret |= 1 << 4;
        }
        if self.window_display {
            ret |= 1 << 5;
        }
        if self.win_tile_map_display {
            ret |= 1 << 6;
        }
        if self.lcd_control_op {
            ret |= 1 << 7;
        }
        ret
    }
}

#[derive(Default, Copy, Clone)]
pub struct LcdcStatusReg {
    coincidence_interrupt_enable: bool,
    oam_interrupt_enable: bool,
    vblank_interrupt_enable: bool,
    hblank_interrupt_enable: bool,
    coincidence_flag: bool,
    mode: u8,
}

impl From<u8> for LcdcStatusReg {
    fn from(val: u8) -> Self {
        LcdcStatusReg {
            coincidence_interrupt_enable: val & (1 << 6) != 0,
            oam_interrupt_enable: val & (1 << 5) != 0,
            vblank_interrupt_enable: val & (1 << 4) != 0,
            hblank_interrupt_enable: val & (1 << 3) != 0,
            coincidence_flag: val & (1 << 2) != 0,
            mode: val & 0x3,
        }
    }
}

impl Into<u8> for LcdcStatusReg {
    fn into(self) -> u8 {
        let mut ret = self.mode;

        if self.coincidence_interrupt_enable {
            ret |= 1 << 6;
        }
        if self.oam_interrupt_enable {
            ret |= 1 << 5;
        }
        if self.vblank_interrupt_enable {
            ret |= 1 << 4;
        }
        if self.hblank_interrupt_enable {
            ret |= 1 << 3;
        }
        if self.coincidence_flag {
            ret |= 1 << 2;
        }
        ret
    }
}

#[derive(Default, Copy, Clone)]
pub struct PaletteDataReg {
    col0_shade: usize,
    col1_shade: usize,
    col2_shade: usize,
    col3_shade: usize,
}

impl From<u8> for PaletteDataReg {
    fn from(val: u8) -> Self {
        let val = val as usize;
        PaletteDataReg {
            col0_shade: val & 0x3,
            col1_shade: (val >> 2) & 0x3,
            col2_shade: (val >> 4) & 0x3,
            col3_shade: (val >> 6) & 0x3,
        }
    }
}

impl Into<u8> for PaletteDataReg {
    fn into(self) -> u8 {
        (self.col0_shade | (self.col1_shade << 2) | (self.col2_shade << 4) | (self.col3_shade << 6)) as u8
    }
}
<|endoftext|>


<commit_before>#![feature(phase, macro_rules, if_let)]

extern crate serialize;
extern crate libc;
#[phase(plugin, link)] extern crate log;
extern crate regex;
#[phase(plugin)] extern crate regex_macros;
extern crate time;
extern crate debug;

extern crate argparse;
extern crate quire;
#[phase(plugin, link)] extern crate lithos;

use std::os::{getenv, setenv};
use std::os::args;
use std::io::fs::readdir;
use std::os::{set_exit_status, self_exe_path};
use std::io::fs::PathExtensions;
use std::default::Default;

use argparse::{ArgumentParser, Store, StoreOption, StoreTrue};
use quire::parse_config;

use lithos::signal;
use lithos::utils::{in_range, in_mapping, check_mapping};
use lithos::master_config::MasterConfig;
use lithos::tree_config::TreeConfig;
use lithos::container_config::ContainerConfig;
use lithos::child_config::ChildConfig;
use lithos::network::{get_host_name, get_host_ip};

fn check_master_config(master: &MasterConfig, verbose: bool) {
    // TODO(tailhook) maybe check host only if we need it for hosts file
    match get_host_name() {
        Ok(hostname) => {
            if verbose {
                println!("Hostname is {}", hostname);
            }
        }
        Err(e) => {
            warn!("Can't get hostname: {}", e);
            set_exit_status(1);
        }
    }
    match get_host_ip() {
        Ok(ipaddr) => {
            if verbose {
                println!("IPAddr is {}", ipaddr);
            }
        }
        Err(e) => {
            warn!("Can't get IPAddress: {}", e);
            set_exit_status(1);
        }
    }

    if !master.devfs_dir.exists() {
        error!("Devfs dir ({}) must exist and contain device nodes",
            master.devfs_dir.display());
        set_exit_status(1);
    }
}

fn check_tree_config(tree: &TreeConfig) {
    if tree.allow_users.len() == 0 {
        error!("No allowed users range. Please add `allow-users: [1-1000]`");
        set_exit_status(1);
    }
    if tree.allow_groups.len() == 0 {
        error!("No allowed groups range. Please add `allow-groups: [1-1000]`");
        set_exit_status(1);
    }
}

fn check(config_file: Path, verbose: bool) {
    let master: MasterConfig = match parse_config(&config_file,
        &*MasterConfig::validator(), Default::default()) {
        Ok(cfg) => cfg,
        Err(e) => {
            error!("Can't parse config: {}", e);
            set_exit_status(1);
            return;
        }
    };

    check_master_config(&master, verbose);

    let master_list = match readdir(&master.config_dir) {
        Ok(list) => list,
        Err(e) => {
            error!("Can't open config directory {}: {}",
                master.config_dir.display(), e);
            set_exit_status(1);
            return;
        }
    };

    for tree_fn in master_list.into_iter() {
        let tree: TreeConfig = match parse_config(&tree_fn,
            &*TreeConfig::validator(), Default::default()) {
            Ok(cfg) => cfg,
            Err(e) => {
                error!("Can't parse config: {}", e);
                set_exit_status(1);
                continue;
            }
        };
        check_tree_config(&tree);

        let config_dir = tree.config_dir;

        debug!("Checking child dir {}", config_dir.display());
        let dirlist = match readdir(&config_dir) {
            Ok(dirlist) => dirlist,
            Err(e) => {
                error!("Can't open config directory {}: {}",
                    config_dir.display(), e);
                set_exit_status(1);
                return;
            }
        };
        for child_fn in dirlist.into_iter() {
            match (child_fn.filestem_str(), child_fn.extension_str()) {
                (Some(""), _) => continue,  // Hidden files
                (_, Some("yaml")) => {}
                _ => continue,  // Non-yaml, old, whatever, files
            }
            debug!("Checking {}", child_fn.display());
            let child_cfg: ChildConfig = match parse_config(&child_fn,
                &*ChildConfig::validator(), Default::default()) {
                Ok(cfg) => cfg,
                Err(e) => {
                    error!("Can't read child config {}: {}",
                        child_fn.display(), e);
                    set_exit_status(1);
                    continue;
                }
            };
            let cfg_path = Path::new(child_cfg.config);
            if !cfg_path.is_absolute() {
                error!("Config path must be absolute");
                set_exit_status(1);
                continue;
            }
            debug!("Opening config {}", child_fn.display());
            let config: ContainerConfig = match parse_config(
                &tree.image_dir
                    .join(child_cfg.image)
                    .join(cfg_path.path_relative_from(
                        &Path::new("/")).unwrap()),
                &*ContainerConfig::validator(), Default::default()) {
                Ok(cfg) => cfg,
                Err(e) => {
                    error!(concat!("Can't read child config {}: {}.",
                        "Sometimes the reason is absolute symlinks for config, ",
                        "in that case it may work in real daemon, but better ",
                        "fix it."), child_fn.display(), e);
                    set_exit_status(1);
                    continue;
                }
            };
            if config.uid_map.len() > 0 {
                if !in_mapping(&config.uid_map, config.user_id) {
                    error!("User is not in mapped range (uid: {})",
                        config.user_id);
                    set_exit_status(1);
                }
            } else {
                if !in_range(&tree.allow_users, config.user_id) {
                    error!("User is not in allowed range (uid: {})",
                        config.user_id);
                    set_exit_status(1);
                }
            }
            if config.gid_map.len() > 0 {
                if !in_mapping(&config.gid_map, config.group_id) {
                    error!("Group is not in mapped range (gid: {})",
                        config.user_id);
                    set_exit_status(1);
                }
            } else {
                if !in_range(&tree.allow_groups, config.group_id) {
                    error!("Group is not in allowed range (gid: {})",
                        config.group_id);
                    set_exit_status(1);
                }
            }
            if !check_mapping(&tree.allow_users, &config.uid_map) {
                error!("Bad uid mapping (probably doesn't match allow_users)");
                set_exit_status(1);
            }
            if !check_mapping(&tree.allow_groups, &config.gid_map) {
                error!("Bad gid mapping (probably doesn't match allow_groups)");
                set_exit_status(1);
            }
        }
    }
}

fn check_binaries() {
    let dir = match self_exe_path() {
        Some(dir) => dir,
        None => {
            error!("Can't find out exe path");
            set_exit_status(1);
            return;
        }
    };
    if !dir.join("lithos_tree").exists() {
        error!("Can't find lithos_tree binary");
        set_exit_status(1);
    }
    if !dir.join("lithos_knot").exists() {
        error!("Can't find lithos_knot binary");
        set_exit_status(1);
    }
}

fn main() {

    signal::block_all();
    if getenv("RUST_LOG").is_none() {
        setenv("RUST_LOG", "warn");
    }

    let mut config_file = Path::new("/etc/lithos.yaml");
    let mut verbose = false;
    let mut config_dir = None;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Checks if lithos configuration is ok");
        ap.refer(&mut config_file)
          .add_option(["-C", "--config"], box Store::<Path>,
            "Name of the global configuration file (default /etc/lithos.yaml)")
          .metavar("FILE");
        ap.refer(&mut verbose)
          .add_option(["-v", "--verbose"], box StoreTrue,
            "Verbose configuration");
        ap.refer(&mut config_dir)
          .add_option(["-D", "--config-dir"], box StoreOption::<Path>,
            concat!("Name of the alterate directory with configs. ",
                    "Useful to test configuration directory before ",
                    "switching it to be primary one"))
          .metavar("DIR");
        match ap.parse_args() {
            Ok(()) => {}
            Err(x) => {
                set_exit_status(x);
                return;
            }
        }
    }
    check_binaries();
    check(config_file, verbose);
}
<commit_msg>Allow to check alternate dir in lithos_check (was broken with namespaces)<commit_after>#![feature(phase, macro_rules, if_let)]

extern crate serialize;
extern crate libc;
#[phase(plugin, link)] extern crate log;
extern crate regex;
#[phase(plugin)] extern crate regex_macros;
extern crate time;
extern crate debug;

extern crate argparse;
extern crate quire;
#[phase(plugin, link)] extern crate lithos;

use std::os::{getenv, setenv};
use std::os::args;
use std::io::fs::readdir;
use std::os::{set_exit_status, self_exe_path};
use std::io::fs::PathExtensions;
use std::default::Default;

use argparse::{ArgumentParser, Store, StoreOption, StoreTrue};
use quire::parse_config;

use lithos::signal;
use lithos::utils::{in_range, in_mapping, check_mapping};
use lithos::master_config::MasterConfig;
use lithos::tree_config::TreeConfig;
use lithos::container_config::ContainerConfig;
use lithos::child_config::ChildConfig;
use lithos::network::{get_host_name, get_host_ip};

fn check_master_config(master: &MasterConfig, verbose: bool) {
    // TODO(tailhook) maybe check host only if we need it for hosts file
    match get_host_name() {
        Ok(hostname) => {
            if verbose {
                println!("Hostname is {}", hostname);
            }
        }
        Err(e) => {
            warn!("Can't get hostname: {}", e);
            set_exit_status(1);
        }
    }
    match get_host_ip() {
        Ok(ipaddr) => {
            if verbose {
                println!("IPAddr is {}", ipaddr);
            }
        }
        Err(e) => {
            warn!("Can't get IPAddress: {}", e);
            set_exit_status(1);
        }
    }

    if !master.devfs_dir.exists() {
        error!("Devfs dir ({}) must exist and contain device nodes",
            master.devfs_dir.display());
        set_exit_status(1);
    }
}

fn check_tree_config(tree: &TreeConfig) {
    if tree.allow_users.len() == 0 {
        error!("No allowed users range. Please add `allow-users: [1-1000]`");
        set_exit_status(1);
    }
    if tree.allow_groups.len() == 0 {
        error!("No allowed groups range. Please add `allow-groups: [1-1000]`");
        set_exit_status(1);
    }
}

fn check(config_file: Path, verbose: bool,
    tree_name: Option<String>, replacement_dir: Option<Path>)
{
    let name_re = regex!(r"^(\w+)\.yaml$");
    let mut replacement_dir = replacement_dir;
    let master: MasterConfig = match parse_config(&config_file,
        &*MasterConfig::validator(), Default::default()) {
        Ok(cfg) => cfg,
        Err(e) => {
            error!("Can't parse config: {}", e);
            set_exit_status(1);
            return;
        }
    };

    check_master_config(&master, verbose);

    for tree_fn in readdir(&master.config_dir)
        .map_err(|e| {
            error!("Can't open config directory {}: {}",
                master.config_dir.display(), e);
            set_exit_status(1);
        })
        .unwrap_or(Vec::new())
        .into_iter()
        .filter(|f| f.filename_str()
                     .map(|s| name_re.is_match(s))
                     .unwrap_or(false))
    {
        let tree: TreeConfig = match parse_config(&tree_fn,
            &*TreeConfig::validator(), Default::default()) {
            Ok(cfg) => cfg,
            Err(e) => {
                error!("Can't parse config: {}", e);
                set_exit_status(1);
                continue;
            }
        };
        check_tree_config(&tree);

        let config_dir = match (tree_fn.filestem_str(), &tree_name) {
            (Some(ref f), &Some(ref t)) if *f == t.as_slice()
            => replacement_dir.take().unwrap_or(tree.config_dir),
            _ => tree.config_dir,
        };

        debug!("Checking child dir {}", config_dir.display());
        let dirlist = match readdir(&config_dir) {
            Ok(dirlist) => dirlist,
            Err(e) => {
                error!("Can't open config directory {}: {}",
                    config_dir.display(), e);
                set_exit_status(1);
                return;
            }
        };
        for child_fn in dirlist.into_iter() {
            match (child_fn.filestem_str(), child_fn.extension_str()) {
                (Some(""), _) => continue,  // Hidden files
                (_, Some("yaml")) => {}
                _ => continue,  // Non-yaml, old, whatever, files
            }
            debug!("Checking {}", child_fn.display());
            let child_cfg: ChildConfig = match parse_config(&child_fn,
                &*ChildConfig::validator(), Default::default()) {
                Ok(cfg) => cfg,
                Err(e) => {
                    error!("Can't read child config {}: {}",
                        child_fn.display(), e);
                    set_exit_status(1);
                    continue;
                }
            };
            let cfg_path = Path::new(child_cfg.config);
            if !cfg_path.is_absolute() {
                error!("Config path must be absolute");
                set_exit_status(1);
                continue;
            }
            debug!("Opening config {}", child_fn.display());
            let config: ContainerConfig = match parse_config(
                &tree.image_dir
                    .join(child_cfg.image)
                    .join(cfg_path.path_relative_from(
                        &Path::new("/")).unwrap()),
                &*ContainerConfig::validator(), Default::default()) {
                Ok(cfg) => cfg,
                Err(e) => {
                    error!(concat!("Can't read child config {}: {}.",
                        "Sometimes the reason is absolute symlinks for config, ",
                        "in that case it may work in real daemon, but better ",
                        "fix it."), child_fn.display(), e);
                    set_exit_status(1);
                    continue;
                }
            };
            if config.uid_map.len() > 0 {
                if !in_mapping(&config.uid_map, config.user_id) {
                    error!("User is not in mapped range (uid: {})",
                        config.user_id);
                    set_exit_status(1);
                }
            } else {
                if !in_range(&tree.allow_users, config.user_id) {
                    error!("User is not in allowed range (uid: {})",
                        config.user_id);
                    set_exit_status(1);
                }
            }
            if config.gid_map.len() > 0 {
                if !in_mapping(&config.gid_map, config.group_id) {
                    error!("Group is not in mapped range (gid: {})",
                        config.user_id);
                    set_exit_status(1);
                }
            } else {
                if !in_range(&tree.allow_groups, config.group_id) {
                    error!("Group is not in allowed range (gid: {})",
                        config.group_id);
                    set_exit_status(1);
                }
            }
            if !check_mapping(&tree.allow_users, &config.uid_map) {
                error!("Bad uid mapping (probably doesn't match allow_users)");
                set_exit_status(1);
            }
            if !check_mapping(&tree.allow_groups, &config.gid_map) {
                error!("Bad gid mapping (probably doesn't match allow_groups)");
                set_exit_status(1);
            }
        }
    }
    if replacement_dir.is_some() {
        error!("Tree {} is not used", tree_name);
        set_exit_status(1);
    }
}

fn check_binaries() {
    let dir = match self_exe_path() {
        Some(dir) => dir,
        None => {
            error!("Can't find out exe path");
            set_exit_status(1);
            return;
        }
    };
    if !dir.join("lithos_tree").exists() {
        error!("Can't find lithos_tree binary");
        set_exit_status(1);
    }
    if !dir.join("lithos_knot").exists() {
        error!("Can't find lithos_knot binary");
        set_exit_status(1);
    }
}

fn main() {

    signal::block_all();
    if getenv("RUST_LOG").is_none() {
        setenv("RUST_LOG", "warn");
    }

    let mut config_file = Path::new("/etc/lithos.yaml");
    let mut verbose = false;
    let mut config_dir = None;
    let mut tree_name = None;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Checks if lithos configuration is ok");
        ap.refer(&mut config_file)
          .add_option(["-C", "--config"], box Store::<Path>,
            "Name of the global configuration file (default /etc/lithos.yaml)")
          .metavar("FILE");
        ap.refer(&mut verbose)
          .add_option(["-v", "--verbose"], box StoreTrue,
            "Verbose configuration");
        ap.refer(&mut config_dir)
          .add_option(["-D", "--dir", "--config-dir"], box StoreOption::<Path>,
            concat!("Name of the alterate directory with configs. ",
                    "Useful to test configuration directory before ",
                    "switching it to be primary one. ",
                    "You must also specify --tree."))
          .metavar("DIR");
        ap.refer(&mut tree_name)
          .add_option(["-T", "--tree", "--subtree-name"],
            box StoreOption::<String>,
            concat!("Name of the tree for which --config-dir takes effect"))
          .metavar("NAME");
        match ap.parse_args() {
            Ok(()) => {}
            Err(x) => {
                set_exit_status(x);
                return;
            }
        }
    }
    if config_dir.is_some() && tree_name.is_none() {
        error!("Please specify --tree if you use --dir");
        set_exit_status(1);
        return;
    }
    check_binaries();
    check(config_file, verbose, tree_name, config_dir);
}
<|endoftext|>


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

//! Compilation of native dependencies like LLVM.
//!
//! Native projects like LLVM unfortunately aren't suited just yet for
//! compilation in build scripts that Cargo has. This is because thie
//! compilation takes a *very* long time but also because we don't want to
//! compile LLVM 3 times as part of a normal bootstrap (we want it cached).
//!
//! LLVM and compiler-rt are essentially just wired up to everything else to
//! ensure that they're always in place if needed.

use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::Path;
use std::process::Command;

use build_helper::output;
use cmake;
use gcc;

use Build;
use util::up_to_date;

/// Compile LLVM for `target`.
pub fn llvm(build: &Build, target: &str) {
    // If we're using a custom LLVM bail out here, but we can only use a
    // custom LLVM for the build triple.
    if let Some(config) = build.config.target_config.get(target) {
        if let Some(ref s) = config.llvm_config {
            return check_llvm_version(build, s);
        }
    }

    // If the cleaning trigger is newer than our built artifacts (or if the
    // artifacts are missing) then we keep going, otherwise we bail out.
    let dst = build.llvm_out(target);
    let stamp = build.src.join("src/rustllvm/llvm-auto-clean-trigger");
    let mut stamp_contents = String::new();
    t!(t!(File::open(&stamp)).read_to_string(&mut stamp_contents));
    let done_stamp = dst.join("llvm-finished-building");
    if done_stamp.exists() {
        let mut done_contents = String::new();
        t!(t!(File::open(&done_stamp)).read_to_string(&mut done_contents));
        if done_contents == stamp_contents {
            return
        }
    }
    drop(fs::remove_dir_all(&dst));

    println!("Building LLVM for {}", target);

    let _ = fs::remove_dir_all(&dst.join("build"));
    t!(fs::create_dir_all(&dst.join("build")));
    let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};

    // http://llvm.org/docs/CMake.html
    let mut cfg = cmake::Config::new(build.src.join("src/llvm"));
    if build.config.ninja {
        cfg.generator("Ninja");
    }

    let profile = match (build.config.llvm_optimize, build.config.llvm_release_debuginfo) {
        (false, _) => "Debug",
        (true, false) => "Release",
        (true, true) => "RelWithDebInfo",
    };

    cfg.target(target)
       .host(&build.config.build)
       .out_dir(&dst)
       .profile(profile)
       .define("LLVM_ENABLE_ASSERTIONS", assertions)
       .define("LLVM_TARGETS_TO_BUILD", "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend")
       .define("LLVM_INCLUDE_EXAMPLES", "OFF")
       .define("LLVM_INCLUDE_TESTS", "OFF")
       .define("LLVM_INCLUDE_DOCS", "OFF")
       .define("LLVM_ENABLE_ZLIB", "OFF")
       .define("WITH_POLLY", "OFF")
       .define("LLVM_ENABLE_TERMINFO", "OFF")
       .define("LLVM_ENABLE_LIBEDIT", "OFF")
       .define("LLVM_PARALLEL_COMPILE_JOBS", build.jobs().to_string())
       .define("LLVM_TARGET_ARCH", target.split('-').next().unwrap())
       .define("LLVM_DEFAULT_TARGET_TRIPLE", target);

    if target.starts_with("i686") {
        cfg.define("LLVM_BUILD_32_BITS", "ON");
    }

    // http://llvm.org/docs/HowToCrossCompileLLVM.html
    if target != build.config.build {
        // FIXME: if the llvm root for the build triple is overridden then we
        //        should use llvm-tblgen from there, also should verify that it
        //        actually exists most of the time in normal installs of LLVM.
        let host = build.llvm_out(&build.config.build).join("bin/llvm-tblgen");
        cfg.define("CMAKE_CROSSCOMPILING", "True")
           .define("LLVM_TABLEGEN", &host);
    }

    // MSVC handles compiler business itself
    if !target.contains("msvc") {
        if build.config.ccache {
           cfg.define("CMAKE_C_COMPILER", "ccache")
              .define("CMAKE_C_COMPILER_ARG1", build.cc(target))
              .define("CMAKE_CXX_COMPILER", "ccache")
              .define("CMAKE_CXX_COMPILER_ARG1", build.cxx(target));
        } else {
           cfg.define("CMAKE_C_COMPILER", build.cc(target))
              .define("CMAKE_CXX_COMPILER", build.cxx(target));
        }
        cfg.build_arg("-j").build_arg(build.jobs().to_string());

        cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" "));
        cfg.define("CMAKE_CXX_FLAGS", build.cflags(target).join(" "));
    }

    // FIXME: we don't actually need to build all LLVM tools and all LLVM
    //        libraries here, e.g. we just want a few components and a few
    //        tools. Figure out how to filter them down and only build the right
    //        tools and libs on all platforms.
    cfg.build();

    t!(t!(File::create(&done_stamp)).write_all(stamp_contents.as_bytes()));
}

fn check_llvm_version(build: &Build, llvm_config: &Path) {
    if !build.config.llvm_version_check {
        return
    }

    let mut cmd = Command::new(llvm_config);
    let version = output(cmd.arg("--version"));
    if version.starts_with("3.5") || version.starts_with("3.6") ||
       version.starts_with("3.7") {
        return
    }
    panic!("\n\nbad LLVM version: {}, need >=3.5\n\n", version)
}

/// Compiles the `rust_test_helpers.c` library which we used in various
/// `run-pass` test suites for ABI testing.
pub fn test_helpers(build: &Build, target: &str) {
    let dst = build.test_helpers_out(target);
    let src = build.src.join("src/rt/rust_test_helpers.c");
    if up_to_date(&src, &dst.join("librust_test_helpers.a")) {
        return
    }

    println!("Building test helpers");
    t!(fs::create_dir_all(&dst));
    let mut cfg = gcc::Config::new();
    cfg.cargo_metadata(false)
       .out_dir(&dst)
       .target(target)
       .host(&build.config.build)
       .opt_level(0)
       .debug(false)
       .file(build.src.join("src/rt/rust_test_helpers.c"))
       .compile("librust_test_helpers.a");
}
<commit_msg>oops, forgot to commit a rustbuild related file<commit_after>// Copyright 2015 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.

//! Compilation of native dependencies like LLVM.
//!
//! Native projects like LLVM unfortunately aren't suited just yet for
//! compilation in build scripts that Cargo has. This is because thie
//! compilation takes a *very* long time but also because we don't want to
//! compile LLVM 3 times as part of a normal bootstrap (we want it cached).
//!
//! LLVM and compiler-rt are essentially just wired up to everything else to
//! ensure that they're always in place if needed.

use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::Path;
use std::process::Command;

use build_helper::output;
use cmake;
use gcc;

use Build;
use util::up_to_date;

/// Compile LLVM for `target`.
pub fn llvm(build: &Build, target: &str) {
    // If we're using a custom LLVM bail out here, but we can only use a
    // custom LLVM for the build triple.
    if let Some(config) = build.config.target_config.get(target) {
        if let Some(ref s) = config.llvm_config {
            return check_llvm_version(build, s);
        }
    }

    // If the cleaning trigger is newer than our built artifacts (or if the
    // artifacts are missing) then we keep going, otherwise we bail out.
    let dst = build.llvm_out(target);
    let stamp = build.src.join("src/rustllvm/llvm-auto-clean-trigger");
    let mut stamp_contents = String::new();
    t!(t!(File::open(&stamp)).read_to_string(&mut stamp_contents));
    let done_stamp = dst.join("llvm-finished-building");
    if done_stamp.exists() {
        let mut done_contents = String::new();
        t!(t!(File::open(&done_stamp)).read_to_string(&mut done_contents));
        if done_contents == stamp_contents {
            return
        }
    }
    drop(fs::remove_dir_all(&dst));

    println!("Building LLVM for {}", target);

    let _ = fs::remove_dir_all(&dst.join("build"));
    t!(fs::create_dir_all(&dst.join("build")));
    let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};

    // http://llvm.org/docs/CMake.html
    let mut cfg = cmake::Config::new(build.src.join("src/llvm"));
    if build.config.ninja {
        cfg.generator("Ninja");
    }

    let profile = match (build.config.llvm_optimize, build.config.llvm_release_debuginfo) {
        (false, _) => "Debug",
        (true, false) => "Release",
        (true, true) => "RelWithDebInfo",
    };

    cfg.target(target)
       .host(&build.config.build)
       .out_dir(&dst)
       .profile(profile)
       .define("LLVM_ENABLE_ASSERTIONS", assertions)
       .define("LLVM_TARGETS_TO_BUILD",
               "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430")
       .define("LLVM_INCLUDE_EXAMPLES", "OFF")
       .define("LLVM_INCLUDE_TESTS", "OFF")
       .define("LLVM_INCLUDE_DOCS", "OFF")
       .define("LLVM_ENABLE_ZLIB", "OFF")
       .define("WITH_POLLY", "OFF")
       .define("LLVM_ENABLE_TERMINFO", "OFF")
       .define("LLVM_ENABLE_LIBEDIT", "OFF")
       .define("LLVM_PARALLEL_COMPILE_JOBS", build.jobs().to_string())
       .define("LLVM_TARGET_ARCH", target.split('-').next().unwrap())
       .define("LLVM_DEFAULT_TARGET_TRIPLE", target);

    if target.starts_with("i686") {
        cfg.define("LLVM_BUILD_32_BITS", "ON");
    }

    // http://llvm.org/docs/HowToCrossCompileLLVM.html
    if target != build.config.build {
        // FIXME: if the llvm root for the build triple is overridden then we
        //        should use llvm-tblgen from there, also should verify that it
        //        actually exists most of the time in normal installs of LLVM.
        let host = build.llvm_out(&build.config.build).join("bin/llvm-tblgen");
        cfg.define("CMAKE_CROSSCOMPILING", "True")
           .define("LLVM_TABLEGEN", &host);
    }

    // MSVC handles compiler business itself
    if !target.contains("msvc") {
        if build.config.ccache {
           cfg.define("CMAKE_C_COMPILER", "ccache")
              .define("CMAKE_C_COMPILER_ARG1", build.cc(target))
              .define("CMAKE_CXX_COMPILER", "ccache")
              .define("CMAKE_CXX_COMPILER_ARG1", build.cxx(target));
        } else {
           cfg.define("CMAKE_C_COMPILER", build.cc(target))
              .define("CMAKE_CXX_COMPILER", build.cxx(target));
        }
        cfg.build_arg("-j").build_arg(build.jobs().to_string());

        cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" "));
        cfg.define("CMAKE_CXX_FLAGS", build.cflags(target).join(" "));
    }

    // FIXME: we don't actually need to build all LLVM tools and all LLVM
    //        libraries here, e.g. we just want a few components and a few
    //        tools. Figure out how to filter them down and only build the right
    //        tools and libs on all platforms.
    cfg.build();

    t!(t!(File::create(&done_stamp)).write_all(stamp_contents.as_bytes()));
}

fn check_llvm_version(build: &Build, llvm_config: &Path) {
    if !build.config.llvm_version_check {
        return
    }

    let mut cmd = Command::new(llvm_config);
    let version = output(cmd.arg("--version"));
    if version.starts_with("3.5") || version.starts_with("3.6") ||
       version.starts_with("3.7") {
        return
    }
    panic!("\n\nbad LLVM version: {}, need >=3.5\n\n", version)
}

/// Compiles the `rust_test_helpers.c` library which we used in various
/// `run-pass` test suites for ABI testing.
pub fn test_helpers(build: &Build, target: &str) {
    let dst = build.test_helpers_out(target);
    let src = build.src.join("src/rt/rust_test_helpers.c");
    if up_to_date(&src, &dst.join("librust_test_helpers.a")) {
        return
    }

    println!("Building test helpers");
    t!(fs::create_dir_all(&dst));
    let mut cfg = gcc::Config::new();
    cfg.cargo_metadata(false)
       .out_dir(&dst)
       .target(target)
       .host(&build.config.build)
       .opt_level(0)
       .debug(false)
       .file(build.src.join("src/rt/rust_test_helpers.c"))
       .compile("librust_test_helpers.a");
}
<|endoftext|>