<commit_before>// Copyright 2018, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use futures_core::future::Future;
use futures_core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError};
use futures_util::future::FutureExt;
use get_thread_id;
use glib_sys;
use std::mem;
use std::ptr;
use std::sync::atomic::{AtomicUsize, Ordering};
use translate::{from_glib_full, from_glib_none, mut_override, ToGlib};

use MainContext;
use MainLoop;
use Priority;
use Source;

// We can't use an enum here because we want to store this in an atomic variable
const INIT: usize = 0;
const NOT_READY: usize = 1;
const READY: usize = 2;
const DONE: usize = 3;

#[allow(clippy::type_complexity)]
#[repr(C)]
struct TaskSource {
    source: glib_sys::GSource,
    future: Option<FutureObj<'static, ()>>,
    thread: Option<usize>,
    state: AtomicUsize,
}

static TASK_SOURCE_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
    TaskSource::clone_raw,
    TaskSource::wake_raw,
    TaskSource::wake_by_ref_raw,
    TaskSource::drop_raw,
);

impl TaskSource {
    unsafe fn clone_raw(waker: *const ()) -> RawWaker {
        let waker = &*(waker as *const TaskSource);
        glib_sys::g_source_ref(mut_override(&waker.source));
        RawWaker::new(waker as *const Self as *const (), &TASK_SOURCE_WAKER_VTABLE)
    }
    unsafe fn wake_raw(waker: *const ()) {
        Self::wake_by_ref_raw(waker);
        Self::drop_raw(waker);
    }

    unsafe fn wake_by_ref_raw(waker: *const ()) {
        let waker = &*(waker as *const TaskSource);
        if waker
            .state
            .compare_and_swap(NOT_READY, READY, Ordering::SeqCst)
            == NOT_READY
        {
            glib_sys::g_source_set_ready_time(mut_override(&waker.source), 0);
        }
    }

    unsafe fn drop_raw(waker: *const ()) {
        let waker = &*(waker as *const TaskSource);
        glib_sys::g_source_unref(mut_override(&waker.source));
    }

    fn as_waker(&self) -> Waker {
        unsafe { Waker::from_raw(Self::clone_raw(self as *const Self as *const ())) }
    }
}

unsafe extern "C" fn prepare(
    source: *mut glib_sys::GSource,
    timeout: *mut i32,
) -> glib_sys::gboolean {
    let source = &mut *(source as *mut TaskSource);

    *timeout = -1;

    let mut cur = source
        .state
        .compare_and_swap(INIT, NOT_READY, Ordering::SeqCst);
    if cur == INIT {
        // XXX: This is not actually correct, we should not dispatch the
        // GSource here already but we need to know its current status so
        // that if it is not ready yet something can register to the waker
        if let Poll::Ready(()) = source.poll() {
            source.state.store(DONE, Ordering::SeqCst);
            cur = DONE;
        } else {
            cur = NOT_READY;
        }
    }

    if cur == READY || cur == DONE {
        glib_sys::GTRUE
    } else {
        glib_sys::GFALSE
    }
}

unsafe extern "C" fn check(source: *mut glib_sys::GSource) -> glib_sys::gboolean {
    let source = &mut *(source as *mut TaskSource);

    let cur = source.state.load(Ordering::SeqCst);
    if cur == READY || cur == DONE {
        glib_sys::GTRUE
    } else {
        glib_sys::GFALSE
    }
}

unsafe extern "C" fn dispatch(
    source: *mut glib_sys::GSource,
    callback: glib_sys::GSourceFunc,
    _user_data: glib_sys::gpointer,
) -> glib_sys::gboolean {
    let source = &mut *(source as *mut TaskSource);
    assert!(callback.is_none());

    glib_sys::g_source_set_ready_time(mut_override(&source.source), -1);
    let mut cur = source
        .state
        .compare_and_swap(READY, NOT_READY, Ordering::SeqCst);
    if cur == READY {
        if let Poll::Ready(()) = source.poll() {
            source.state.store(DONE, Ordering::SeqCst);
            cur = DONE;
        } else {
            cur = NOT_READY;
        }
    }

    if cur == DONE {
        glib_sys::G_SOURCE_REMOVE
    } else {
        glib_sys::G_SOURCE_CONTINUE
    }
}

unsafe extern "C" fn finalize(source: *mut glib_sys::GSource) {
    let source = source as *mut TaskSource;
    let _ = (*source).future.take();
}

static SOURCE_FUNCS: glib_sys::GSourceFuncs = glib_sys::GSourceFuncs {
    check: Some(check),
    prepare: Some(prepare),
    dispatch: Some(dispatch),
    finalize: Some(finalize),
    closure_callback: None,
    closure_marshal: None,
};

unsafe impl Send for TaskSource {}
unsafe impl Sync for TaskSource {}

impl TaskSource {
    #[allow(clippy::new_ret_no_self)]
    fn new(priority: Priority, thread: Option<usize>, future: FutureObj<'static, ()>) -> Source {
        unsafe {
            let source = glib_sys::g_source_new(
                mut_override(&SOURCE_FUNCS),
                mem::size_of::<TaskSource>() as u32,
            );
            {
                let source = &mut *(source as *mut TaskSource);
                ptr::write(&mut source.future, Some(future));
                source.thread = thread;
                source.state = AtomicUsize::new(INIT);
            }

            glib_sys::g_source_set_priority(source, priority.to_glib());

            from_glib_full(source)
        }
    }

    fn poll(&mut self) -> Poll<()> {
        // Make sure that the first time we're polled that the current thread is remembered
        // and from there one we ensure that we're always polled from exactly the same thread.
        //
        // In theory a GMainContext can be first run from one thread and later from another
        // thread, but we allow spawning non-Send futures and must not ever use them from
        // any other thread.
        match &mut self.thread {
            thread @ &mut None => {
                *thread = Some(get_thread_id());
            }
            &mut Some(thread_id) => {
                assert_eq!(
                    get_thread_id(),
                    thread_id,
                    "Task polled on a different thread than before"
                );
            }
        }

        let waker = self.as_waker();
        let source = &self.source as *const _;
        if let Some(ref mut future) = self.future {
            let executor: MainContext =
                unsafe { from_glib_none(glib_sys::g_source_get_context(mut_override(source))) };

            assert!(
                executor.is_owner(),
                "Polling futures only allowed if the thread is owning the MainContext"
            );

            // Clone that we store in the task local data so that
            // it can be retrieved as needed
            executor.with_thread_default(|| {
                let enter = futures_executor::enter().unwrap();
                let mut context = Context::from_waker(&waker);

                let res = future.poll_unpin(&mut context);

                drop(enter);

                res
            })
        } else {
            Poll::Ready(())
        }
    }
}

impl MainContext {
    /// Spawn a new infallible `Future` on the main context.
    ///
    /// This can be called from any thread and will execute the future from the thread
    /// where main context is running, e.g. via a `MainLoop`.
    pub fn spawn<F: Future<Output = ()> + Send + 'static>(&self, f: F) {
        self.spawn_with_priority(::PRIORITY_DEFAULT, f);
    }

    /// Spawn a new infallible `Future` on the main context.
    ///
    /// The given `Future` does not have to be `Send`.
    ///
    /// This can be called only from the thread where the main context is running, e.g.
    /// from any other `Future` that is executed on this main context, or after calling
    /// `push_thread_default` or `acquire` on the main context.
    pub fn spawn_local<F: Future<Output = ()> + 'static>(&self, f: F) {
        self.spawn_local_with_priority(::PRIORITY_DEFAULT, f);
    }

    /// Spawn a new infallible `Future` on the main context, with a non-default priority.
    ///
    /// This can be called from any thread and will execute the future from the thread
    /// where main context is running, e.g. via a `MainLoop`.
    pub fn spawn_with_priority<F: Future<Output = ()> + Send + 'static>(
        &self,
        priority: Priority,
        f: F,
    ) {
        let f = FutureObj::new(Box::new(f));
        let source = TaskSource::new(priority, None, f);
        source.attach(Some(&*self));
    }

    /// Spawn a new infallible `Future` on the main context, with a non-default priority.
    ///
    /// The given `Future` does not have to be `Send`.
    ///
    /// This can be called only from the thread where the main context is running, e.g.
    /// from any other `Future` that is executed on this main context, or after calling
    /// `push_thread_default` or `acquire` on the main context.
    pub fn spawn_local_with_priority<F: Future<Output = ()> + 'static>(
        &self,
        priority: Priority,
        f: F,
    ) {
        assert!(
            self.is_owner(),
            "Spawning local futures only allowed on the thread owning the MainContext"
        );
        unsafe {
            let f = LocalFutureObj::new(Box::new(f));
            // We ensure here that we only ever run the future on this very task
            // and that the futures executor is running on this task. Otherwise
            // we will panic later.
            // As such we can add the Send impl here safely
            let f = f.into_future_obj();
            let source = TaskSource::new(priority, Some(get_thread_id()), f);
            source.attach(Some(&*self));
        }
    }

    /// Runs a new, infallible `Future` on the main context and block until it finished, returning
    /// the result of the `Future`.
    ///
    /// The given `Future` does not have to be `Send` or `'static`.
    ///
    /// This must only be called if no `MainLoop` or anything else is running on this specific main
    /// context.
    #[allow(clippy::transmute_ptr_to_ptr)]
    pub fn block_on<F: Future>(&self, f: F) -> F::Output {
        let mut res = None;
        let l = MainLoop::new(Some(&*self), false);
        let l_clone = l.clone();

        unsafe {
            let f = f.then(|r| {
                res = Some(r);
                l_clone.quit();
                futures_util::future::ready(())
            });

            // Super-unsafe: We transmute here to get rid of the 'static lifetime
            let f = LocalFutureObj::new(Box::new(f));
            let f: LocalFutureObj<'static, ()> = mem::transmute(f);

            // And ensure that we are only ever running on this very thread.
            let f = f.into_future_obj();

            let source = TaskSource::new(::PRIORITY_DEFAULT, Some(get_thread_id()), f);
            source.attach(Some(&*self));
        }

        l.run();

        res.unwrap()
    }
}

impl Spawn for MainContext {
    fn spawn_obj(&self, f: FutureObj<'static, ()>) -> Result<(), SpawnError> {
        let source = TaskSource::new(::PRIORITY_DEFAULT, None, f);
        source.attach(Some(&*self));
        Ok(())
    }
}

impl LocalSpawn for MainContext {
    fn spawn_local_obj(&self, f: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> {
        let source = TaskSource::new(::PRIORITY_DEFAULT, Some(get_thread_id()), unsafe {
            f.into_future_obj()
        });
        source.attach(Some(&*self));
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use futures_channel::oneshot;
    use futures_util::future::TryFutureExt;
    use std::sync::mpsc;
    use std::thread;

    #[test]
    fn test_spawn() {
        let c = MainContext::new();
        let l = ::MainLoop::new(Some(&c), false);

        let (sender, receiver) = mpsc::channel();
        let (o_sender, o_receiver) = oneshot::channel();

        let l_clone = l.clone();
        c.spawn(
            o_receiver
                .and_then(move |()| {
                    sender.send(()).unwrap();
                    l_clone.quit();

                    futures_util::future::ok(())
                })
                .then(|res| futures_util::future::ready(res.unwrap())),
        );

        thread::spawn(move || {
            l.run();
        });

        o_sender.send(()).unwrap();

        let _ = receiver.recv().unwrap();
    }

    #[test]
    fn test_spawn_local() {
        let c = MainContext::new();
        let l = ::MainLoop::new(Some(&c), false);

        c.push_thread_default();
        let l_clone = l.clone();
        c.spawn_local(futures_util::future::lazy(move |_ctx| {
            l_clone.quit();
        }));

        l.run();

        c.pop_thread_default();
    }

    #[test]
    fn test_block_on() {
        let c = MainContext::new();

        let mut v = None;
        {
            let v = &mut v;

            let future = futures_util::future::lazy(|_ctx| {
                *v = Some(123);
                Ok::<i32, ()>(123)
            });

            let res = c.block_on(future);
            assert_eq!(res, Ok(123));
        }

        assert_eq!(v, Some(123));
    }
}
<commit_msg>Clean up futures executor and checking that the future is always used from the correct thread<commit_after>// Copyright 2018, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use futures_core::future::Future;
use futures_core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError};
use futures_util::future::FutureExt;
use glib_sys;
use std::mem;
use std::pin;
use std::ptr;
use std::sync::atomic::{AtomicUsize, Ordering};
use translate::{from_glib_full, from_glib_none, mut_override, ToGlib};
use ThreadGuard;

use MainContext;
use MainLoop;
use Priority;
use Source;

// We can't use an enum here because we want to store this in an atomic variable
const INIT: usize = 0;
const NOT_READY: usize = 1;
const READY: usize = 2;
const DONE: usize = 3;

// Wrapper around Send Futures and non-Send Futures that will panic
// if the non-Send Future is polled/dropped from a different thread
// than where this was created.
enum FutureWrapper {
    Send(FutureObj<'static, ()>),
    NonSend(ThreadGuard<LocalFutureObj<'static, ()>>),
}

impl Future for FutureWrapper {
    type Output = ();

    fn poll(self: pin::Pin<&mut Self>, ctx: &mut Context) -> Poll<()> {
        match self.get_mut() {
            FutureWrapper::Send(fut) => fut.poll_unpin(ctx),
            FutureWrapper::NonSend(fut) => fut.get_mut().poll_unpin(ctx),
        }
    }
}

#[allow(clippy::type_complexity)]
#[repr(C)]
struct TaskSource {
    source: glib_sys::GSource,
    future: Option<FutureWrapper>,
    state: AtomicUsize,
}

static TASK_SOURCE_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
    TaskSource::clone_raw,
    TaskSource::wake_raw,
    TaskSource::wake_by_ref_raw,
    TaskSource::drop_raw,
);

impl TaskSource {
    unsafe fn clone_raw(waker: *const ()) -> RawWaker {
        let waker = &*(waker as *const TaskSource);
        glib_sys::g_source_ref(mut_override(&waker.source));
        RawWaker::new(waker as *const Self as *const (), &TASK_SOURCE_WAKER_VTABLE)
    }
    unsafe fn wake_raw(waker: *const ()) {
        Self::wake_by_ref_raw(waker);
        Self::drop_raw(waker);
    }

    unsafe fn wake_by_ref_raw(waker: *const ()) {
        let waker = &*(waker as *const TaskSource);
        if waker
            .state
            .compare_and_swap(NOT_READY, READY, Ordering::SeqCst)
            == NOT_READY
        {
            glib_sys::g_source_set_ready_time(mut_override(&waker.source), 0);
        }
    }

    unsafe fn drop_raw(waker: *const ()) {
        let waker = &*(waker as *const TaskSource);
        glib_sys::g_source_unref(mut_override(&waker.source));
    }

    fn as_waker(&self) -> Waker {
        unsafe { Waker::from_raw(Self::clone_raw(self as *const Self as *const ())) }
    }
}

unsafe extern "C" fn prepare(
    source: *mut glib_sys::GSource,
    timeout: *mut i32,
) -> glib_sys::gboolean {
    let source = &mut *(source as *mut TaskSource);

    *timeout = -1;

    let mut cur = source
        .state
        .compare_and_swap(INIT, NOT_READY, Ordering::SeqCst);
    if cur == INIT {
        // XXX: This is not actually correct, we should not dispatch the
        // GSource here already but we need to know its current status so
        // that if it is not ready yet something can register to the waker
        if let Poll::Ready(()) = source.poll() {
            source.state.store(DONE, Ordering::SeqCst);
            cur = DONE;
        } else {
            cur = NOT_READY;
        }
    }

    if cur == READY || cur == DONE {
        glib_sys::GTRUE
    } else {
        glib_sys::GFALSE
    }
}

unsafe extern "C" fn check(source: *mut glib_sys::GSource) -> glib_sys::gboolean {
    let source = &mut *(source as *mut TaskSource);

    let cur = source.state.load(Ordering::SeqCst);
    if cur == READY || cur == DONE {
        glib_sys::GTRUE
    } else {
        glib_sys::GFALSE
    }
}

unsafe extern "C" fn dispatch(
    source: *mut glib_sys::GSource,
    callback: glib_sys::GSourceFunc,
    _user_data: glib_sys::gpointer,
) -> glib_sys::gboolean {
    let source = &mut *(source as *mut TaskSource);
    assert!(callback.is_none());

    glib_sys::g_source_set_ready_time(mut_override(&source.source), -1);
    let mut cur = source
        .state
        .compare_and_swap(READY, NOT_READY, Ordering::SeqCst);
    if cur == READY {
        if let Poll::Ready(()) = source.poll() {
            source.state.store(DONE, Ordering::SeqCst);
            cur = DONE;
        } else {
            cur = NOT_READY;
        }
    }

    if cur == DONE {
        glib_sys::G_SOURCE_REMOVE
    } else {
        glib_sys::G_SOURCE_CONTINUE
    }
}

unsafe extern "C" fn finalize(source: *mut glib_sys::GSource) {
    let source = source as *mut TaskSource;

    // This will panic if the future was a local future and is dropped from
    // a different thread than where it was created
    let _ = (*source).future.take();
}

static SOURCE_FUNCS: glib_sys::GSourceFuncs = glib_sys::GSourceFuncs {
    check: Some(check),
    prepare: Some(prepare),
    dispatch: Some(dispatch),
    finalize: Some(finalize),
    closure_callback: None,
    closure_marshal: None,
};

unsafe impl Send for TaskSource {}
unsafe impl Sync for TaskSource {}

impl TaskSource {
    #[allow(clippy::new_ret_no_self)]
    fn new(priority: Priority, future: FutureWrapper) -> Source {
        unsafe {
            let source = glib_sys::g_source_new(
                mut_override(&SOURCE_FUNCS),
                mem::size_of::<TaskSource>() as u32,
            );
            {
                let source = &mut *(source as *mut TaskSource);
                ptr::write(&mut source.future, Some(future));
                source.state = AtomicUsize::new(INIT);
            }

            glib_sys::g_source_set_priority(source, priority.to_glib());

            from_glib_full(source)
        }
    }

    fn poll(&mut self) -> Poll<()> {
        let waker = self.as_waker();
        let source = &self.source as *const _;
        if let Some(ref mut future) = self.future {
            let executor: MainContext =
                unsafe { from_glib_none(glib_sys::g_source_get_context(mut_override(source))) };

            assert!(
                executor.is_owner(),
                "Polling futures only allowed if the thread is owning the MainContext"
            );

            // Clone that we store in the task local data so that
            // it can be retrieved as needed
            executor.with_thread_default(|| {
                let enter = futures_executor::enter().unwrap();
                let mut context = Context::from_waker(&waker);

                // This will panic if the future was a local future and is called from
                // a different thread than where it was created
                let res = future.poll_unpin(&mut context);

                drop(enter);

                res
            })
        } else {
            Poll::Ready(())
        }
    }
}

impl MainContext {
    /// Spawn a new infallible `Future` on the main context.
    ///
    /// This can be called from any thread and will execute the future from the thread
    /// where main context is running, e.g. via a `MainLoop`.
    pub fn spawn<F: Future<Output = ()> + Send + 'static>(&self, f: F) {
        self.spawn_with_priority(::PRIORITY_DEFAULT, f);
    }

    /// Spawn a new infallible `Future` on the main context.
    ///
    /// The given `Future` does not have to be `Send`.
    ///
    /// This can be called only from the thread where the main context is running, e.g.
    /// from any other `Future` that is executed on this main context, or after calling
    /// `push_thread_default` or `acquire` on the main context.
    pub fn spawn_local<F: Future<Output = ()> + 'static>(&self, f: F) {
        self.spawn_local_with_priority(::PRIORITY_DEFAULT, f);
    }

    /// Spawn a new infallible `Future` on the main context, with a non-default priority.
    ///
    /// This can be called from any thread and will execute the future from the thread
    /// where main context is running, e.g. via a `MainLoop`.
    pub fn spawn_with_priority<F: Future<Output = ()> + Send + 'static>(
        &self,
        priority: Priority,
        f: F,
    ) {
        let f = FutureObj::new(Box::new(f));
        let source = TaskSource::new(priority, FutureWrapper::Send(f));
        source.attach(Some(&*self));
    }

    /// Spawn a new infallible `Future` on the main context, with a non-default priority.
    ///
    /// The given `Future` does not have to be `Send`.
    ///
    /// This can be called only from the thread where the main context is running, e.g.
    /// from any other `Future` that is executed on this main context, or after calling
    /// `push_thread_default` or `acquire` on the main context.
    pub fn spawn_local_with_priority<F: Future<Output = ()> + 'static>(
        &self,
        priority: Priority,
        f: F,
    ) {
        assert!(
            self.is_owner(),
            "Spawning local futures only allowed on the thread owning the MainContext"
        );
        let f = LocalFutureObj::new(Box::new(f));
        let source = TaskSource::new(priority, FutureWrapper::NonSend(ThreadGuard::new(f)));
        source.attach(Some(&*self));
    }

    /// Runs a new, infallible `Future` on the main context and block until it finished, returning
    /// the result of the `Future`.
    ///
    /// The given `Future` does not have to be `Send` or `'static`.
    ///
    /// This must only be called if no `MainLoop` or anything else is running on this specific main
    /// context.
    #[allow(clippy::transmute_ptr_to_ptr)]
    pub fn block_on<F: Future>(&self, f: F) -> F::Output {
        let mut res = None;
        let l = MainLoop::new(Some(&*self), false);
        let l_clone = l.clone();

        unsafe {
            let f = f.then(|r| {
                res = Some(r);
                l_clone.quit();
                futures_util::future::ready(())
            });

            // Super-unsafe: We transmute here to get rid of the 'static lifetime
            let f = LocalFutureObj::new(Box::new(f));
            let f: LocalFutureObj<'static, ()> = mem::transmute(f);

            let source = TaskSource::new(
                ::PRIORITY_DEFAULT,
                FutureWrapper::NonSend(ThreadGuard::new(f)),
            );
            source.attach(Some(&*self));
        }

        l.run();

        res.unwrap()
    }
}

impl Spawn for MainContext {
    fn spawn_obj(&self, f: FutureObj<'static, ()>) -> Result<(), SpawnError> {
        let source = TaskSource::new(::PRIORITY_DEFAULT, FutureWrapper::Send(f));
        source.attach(Some(&*self));
        Ok(())
    }
}

impl LocalSpawn for MainContext {
    fn spawn_local_obj(&self, f: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> {
        let source = TaskSource::new(
            ::PRIORITY_DEFAULT,
            FutureWrapper::NonSend(ThreadGuard::new(f)),
        );
        source.attach(Some(&*self));
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use futures_channel::oneshot;
    use futures_util::future::TryFutureExt;
    use std::sync::mpsc;
    use std::thread;

    #[test]
    fn test_spawn() {
        let c = MainContext::new();
        let l = ::MainLoop::new(Some(&c), false);

        let (sender, receiver) = mpsc::channel();
        let (o_sender, o_receiver) = oneshot::channel();

        let l_clone = l.clone();
        c.spawn(
            o_receiver
                .and_then(move |()| {
                    sender.send(()).unwrap();
                    l_clone.quit();

                    futures_util::future::ok(())
                })
                .then(|res| futures_util::future::ready(res.unwrap())),
        );

        thread::spawn(move || {
            l.run();
        });

        o_sender.send(()).unwrap();

        let _ = receiver.recv().unwrap();
    }

    #[test]
    fn test_spawn_local() {
        let c = MainContext::new();
        let l = ::MainLoop::new(Some(&c), false);

        c.push_thread_default();
        let l_clone = l.clone();
        c.spawn_local(futures_util::future::lazy(move |_ctx| {
            l_clone.quit();
        }));

        l.run();

        c.pop_thread_default();
    }

    #[test]
    fn test_block_on() {
        let c = MainContext::new();

        let mut v = None;
        {
            let v = &mut v;

            let future = futures_util::future::lazy(|_ctx| {
                *v = Some(123);
                Ok::<i32, ()>(123)
            });

            let res = c.block_on(future);
            assert_eq!(res, Ok(123));
        }

        assert_eq!(v, Some(123));
    }
}
<|endoftext|>


<commit_before>#![unstable(issue = "0", feature = "windows_stdio")]

use io::prelude::*;

use cmp;
use io::{self, Cursor};
use ptr;
use str;
use sync::Mutex;
use sys::c;
use sys::cvt;
use sys::handle::Handle;

pub enum Output {
    Console(c::HANDLE),
    Pipe(c::HANDLE),
}

pub struct Stdin {
    utf8: Mutex<io::Cursor<Vec<u8>>>,
}
pub struct Stdout;
pub struct Stderr;

pub fn get(handle: c::DWORD) -> io::Result<Output> {
    let handle = unsafe { c::GetStdHandle(handle) };
    if handle == c::INVALID_HANDLE_VALUE {
        Err(io::Error::last_os_error())
    } else if handle.is_null() {
        Err(io::Error::from_raw_os_error(c::ERROR_INVALID_HANDLE as i32))
    } else {
        let mut out = 0;
        match unsafe { c::GetConsoleMode(handle, &mut out) } {
            0 => Ok(Output::Pipe(handle)),
            _ => Ok(Output::Console(handle)),
        }
    }
}

fn write(handle: c::DWORD, data: &[u8]) -> io::Result<usize> {
    let handle = match get(handle)? {
        Output::Console(c) => c,
        Output::Pipe(p) => {
            let handle = Handle::new(p);
            let ret = handle.write(data);
            handle.into_raw();
            return ret
        }
    };

    // As with stdin on windows, stdout often can't handle writes of large
    // sizes. For an example, see #14940. For this reason, don't try to
    // write the entire output buffer on windows.
    //
    // For some other references, it appears that this problem has been
    // encountered by others [1] [2]. We choose the number 8K just because
    // libuv does the same.
    //
    // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232
    // [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html
    const OUT_MAX: usize = 8192;
    let len = cmp::min(data.len(), OUT_MAX);
    let utf8 = match str::from_utf8(&data[..len]) {
        Ok(s) => s,
        Err(ref e) if e.valid_up_to() == 0 => return Err(invalid_encoding()),
        Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(),
    };
    let utf16 = utf8.encode_utf16().collect::<Vec<u16>>();
    let mut written = 0;
    cvt(unsafe {
        c::WriteConsoleW(handle,
                         utf16.as_ptr() as c::LPCVOID,
                         utf16.len() as u32,
                         &mut written,
                         ptr::null_mut())
    })?;

    // FIXME if this only partially writes the utf16 buffer then we need to
    //       figure out how many bytes of `data` were actually written
    assert_eq!(written as usize, utf16.len());
    Ok(utf8.len())
}

impl Stdin {
    pub fn new() -> io::Result<Stdin> {
        Ok(Stdin {
            utf8: Mutex::new(Cursor::new(Vec::new())),
        })
    }

    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
        let handle = match get(c::STD_INPUT_HANDLE)? {
            Output::Console(c) => c,
            Output::Pipe(p) => {
                let handle = Handle::new(p);
                let ret = handle.read(buf);
                handle.into_raw();
                return ret
            }
        };
        let mut utf8 = self.utf8.lock().unwrap();
        // Read more if the buffer is empty
        if utf8.position() as usize == utf8.get_ref().len() {
            let mut utf16 = vec![0u16; 0x1000];
            let mut num = 0;
            let mut input_control = readconsole_input_control(CTRL_Z_MASK);
            cvt(unsafe {
                c::ReadConsoleW(handle,
                                utf16.as_mut_ptr() as c::LPVOID,
                                utf16.len() as u32,
                                &mut num,
                                &mut input_control as c::PCONSOLE_READCONSOLE_CONTROL)
            })?;
            utf16.truncate(num as usize);
            // FIXME: what to do about this data that has already been read?
            let mut data = match String::from_utf16(&utf16) {
                Ok(utf8) => utf8.into_bytes(),
                Err(..) => return Err(invalid_encoding()),
            };
            if let Some(&last_byte) = data.last() {
                if last_byte == CTRL_Z {
                    data.pop();
                }
            }
            *utf8 = Cursor::new(data);
        }

        // MemReader shouldn't error here since we just filled it
        utf8.read(buf)
    }
}

#[unstable(reason = "not public", issue = "0", feature = "fd_read")]
impl<'a> Read for &'a Stdin {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (**self).read(buf)
    }
}

impl Stdout {
    pub fn new() -> io::Result<Stdout> {
        Ok(Stdout)
    }

    pub fn write(&self, data: &[u8]) -> io::Result<usize> {
        write(c::STD_OUTPUT_HANDLE, data)
    }

    pub fn flush(&self) -> io::Result<()> {
        Ok(())
    }
}

impl Stderr {
    pub fn new() -> io::Result<Stderr> {
        Ok(Stderr)
    }

    pub fn write(&self, data: &[u8]) -> io::Result<usize> {
        write(c::STD_ERROR_HANDLE, data)
    }

    pub fn flush(&self) -> io::Result<()> {
        Ok(())
    }
}

impl Output {
    pub fn handle(&self) -> c::HANDLE {
        match *self {
            Output::Console(c) => c,
            Output::Pipe(c) => c,
        }
    }
}

fn invalid_encoding() -> io::Error {
    io::Error::new(io::ErrorKind::InvalidData,
                   "Windows stdio in console mode does not support non-UTF-8 byte sequences; \
                    see https://github.com/rust-lang/rust/issues/23344")
}

fn readconsole_input_control(wakeup_mask: c::ULONG) -> c::CONSOLE_READCONSOLE_CONTROL {
    c::CONSOLE_READCONSOLE_CONTROL {
        nLength: ::mem::size_of::<c::CONSOLE_READCONSOLE_CONTROL>() as c::ULONG,
        nInitialChars: 0,
        dwCtrlWakeupMask: wakeup_mask,
        dwControlKeyState: 0,
    }
}

const CTRL_Z: u8 = 0x1A;
const CTRL_Z_MASK: c::ULONG = 0x4000000; //1 << 0x1A

pub fn is_ebadf(err: &io::Error) -> bool {
    err.raw_os_error() == Some(c::ERROR_INVALID_HANDLE as i32)
}

// The default buffer capacity is 64k, but apparently windows
// doesn't like 64k reads on stdin. See #13304 for details, but the
// idea is that on windows we use a slightly smaller buffer that's
// been seen to be acceptable.
pub const STDIN_BUF_SIZE: usize = 8 * 1024;

pub fn panic_output() -> Option<impl io::Write> {
    io::stderr_raw().ok()
}
<commit_msg>Remove unused Read implementation on sys::Windows::Stdin<commit_after>#![unstable(issue = "0", feature = "windows_stdio")]

use cmp;
use io::{self, Cursor};
use ptr;
use str;
use sync::Mutex;
use sys::c;
use sys::cvt;
use sys::handle::Handle;

pub enum Output {
    Console(c::HANDLE),
    Pipe(c::HANDLE),
}

pub struct Stdin {
    utf8: Mutex<io::Cursor<Vec<u8>>>,
}
pub struct Stdout;
pub struct Stderr;

pub fn get(handle: c::DWORD) -> io::Result<Output> {
    let handle = unsafe { c::GetStdHandle(handle) };
    if handle == c::INVALID_HANDLE_VALUE {
        Err(io::Error::last_os_error())
    } else if handle.is_null() {
        Err(io::Error::from_raw_os_error(c::ERROR_INVALID_HANDLE as i32))
    } else {
        let mut out = 0;
        match unsafe { c::GetConsoleMode(handle, &mut out) } {
            0 => Ok(Output::Pipe(handle)),
            _ => Ok(Output::Console(handle)),
        }
    }
}

fn write(handle: c::DWORD, data: &[u8]) -> io::Result<usize> {
    let handle = match get(handle)? {
        Output::Console(c) => c,
        Output::Pipe(p) => {
            let handle = Handle::new(p);
            let ret = handle.write(data);
            handle.into_raw();
            return ret
        }
    };

    // As with stdin on windows, stdout often can't handle writes of large
    // sizes. For an example, see #14940. For this reason, don't try to
    // write the entire output buffer on windows.
    //
    // For some other references, it appears that this problem has been
    // encountered by others [1] [2]. We choose the number 8K just because
    // libuv does the same.
    //
    // [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232
    // [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html
    const OUT_MAX: usize = 8192;
    let len = cmp::min(data.len(), OUT_MAX);
    let utf8 = match str::from_utf8(&data[..len]) {
        Ok(s) => s,
        Err(ref e) if e.valid_up_to() == 0 => return Err(invalid_encoding()),
        Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(),
    };
    let utf16 = utf8.encode_utf16().collect::<Vec<u16>>();
    let mut written = 0;
    cvt(unsafe {
        c::WriteConsoleW(handle,
                         utf16.as_ptr() as c::LPCVOID,
                         utf16.len() as u32,
                         &mut written,
                         ptr::null_mut())
    })?;

    // FIXME if this only partially writes the utf16 buffer then we need to
    //       figure out how many bytes of `data` were actually written
    assert_eq!(written as usize, utf16.len());
    Ok(utf8.len())
}

impl Stdin {
    pub fn new() -> io::Result<Stdin> {
        Ok(Stdin {
            utf8: Mutex::new(Cursor::new(Vec::new())),
        })
    }

    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
        let handle = match get(c::STD_INPUT_HANDLE)? {
            Output::Console(c) => c,
            Output::Pipe(p) => {
                let handle = Handle::new(p);
                let ret = handle.read(buf);
                handle.into_raw();
                return ret
            }
        };
        let mut utf8 = self.utf8.lock().unwrap();
        // Read more if the buffer is empty
        if utf8.position() as usize == utf8.get_ref().len() {
            let mut utf16 = vec![0u16; 0x1000];
            let mut num = 0;
            let mut input_control = readconsole_input_control(CTRL_Z_MASK);
            cvt(unsafe {
                c::ReadConsoleW(handle,
                                utf16.as_mut_ptr() as c::LPVOID,
                                utf16.len() as u32,
                                &mut num,
                                &mut input_control as c::PCONSOLE_READCONSOLE_CONTROL)
            })?;
            utf16.truncate(num as usize);
            // FIXME: what to do about this data that has already been read?
            let mut data = match String::from_utf16(&utf16) {
                Ok(utf8) => utf8.into_bytes(),
                Err(..) => return Err(invalid_encoding()),
            };
            if let Some(&last_byte) = data.last() {
                if last_byte == CTRL_Z {
                    data.pop();
                }
            }
            *utf8 = Cursor::new(data);
        }

        // MemReader shouldn't error here since we just filled it
        utf8.read(buf)
    }
}

impl Stdout {
    pub fn new() -> io::Result<Stdout> {
        Ok(Stdout)
    }

    pub fn write(&self, data: &[u8]) -> io::Result<usize> {
        write(c::STD_OUTPUT_HANDLE, data)
    }

    pub fn flush(&self) -> io::Result<()> {
        Ok(())
    }
}

impl Stderr {
    pub fn new() -> io::Result<Stderr> {
        Ok(Stderr)
    }

    pub fn write(&self, data: &[u8]) -> io::Result<usize> {
        write(c::STD_ERROR_HANDLE, data)
    }

    pub fn flush(&self) -> io::Result<()> {
        Ok(())
    }
}

impl Output {
    pub fn handle(&self) -> c::HANDLE {
        match *self {
            Output::Console(c) => c,
            Output::Pipe(c) => c,
        }
    }
}

fn invalid_encoding() -> io::Error {
    io::Error::new(io::ErrorKind::InvalidData,
                   "Windows stdio in console mode does not support non-UTF-8 byte sequences; \
                    see https://github.com/rust-lang/rust/issues/23344")
}

fn readconsole_input_control(wakeup_mask: c::ULONG) -> c::CONSOLE_READCONSOLE_CONTROL {
    c::CONSOLE_READCONSOLE_CONTROL {
        nLength: ::mem::size_of::<c::CONSOLE_READCONSOLE_CONTROL>() as c::ULONG,
        nInitialChars: 0,
        dwCtrlWakeupMask: wakeup_mask,
        dwControlKeyState: 0,
    }
}

const CTRL_Z: u8 = 0x1A;
const CTRL_Z_MASK: c::ULONG = 0x4000000; //1 << 0x1A

pub fn is_ebadf(err: &io::Error) -> bool {
    err.raw_os_error() == Some(c::ERROR_INVALID_HANDLE as i32)
}

// The default buffer capacity is 64k, but apparently windows
// doesn't like 64k reads on stdin. See #13304 for details, but the
// idea is that on windows we use a slightly smaller buffer that's
// been seen to be acceptable.
pub const STDIN_BUF_SIZE: usize = 8 * 1024;

pub fn panic_output() -> Option<impl io::Write> {
    io::stderr_raw().ok()
}
<|endoftext|>


<commit_before>/*

  This struct essentially encapsulates the "instance variables"

*/
pub struct SimpleHdrHistogram {
    pub leading_zeros_count_base: i32,
    pub sub_bucket_mask: i64,
    pub unit_magnitude: i32,
    pub sub_bucket_count: i32,
    pub sub_bucket_half_count: i32,
    pub sub_bucket_half_count_magnitude: i32,
    pub counts: Vec<u64>,
}

/*

  Implementing this trait (Default) for our struct gives us a nice way to
  initialize an instance using default args instead of having to provide all of them

 */
impl Default for SimpleHdrHistogram {
    fn default () -> SimpleHdrHistogram {
        SimpleHdrHistogram {
            leading_zeros_count_base: 0,
            sub_bucket_mask: 0,
            unit_magnitude: 0,
            sub_bucket_count: 0,
            sub_bucket_half_count: 0,
            sub_bucket_half_count_magnitude: 0,
            counts: Vec::new(),
        }
    }
}

pub trait HistogramBase {

    //FIXME this stuff could be mostly unsigned

    //TODO should be default impl of this trait
    fn record_single_value(&self, value: i64) -> Result<(), String>;

    //TODO should be default impl of this trait
    fn counts_array_index(&self, value: i64) -> Result<i32, String>;

    //TODO should be default impl of this trait
    fn counts_array_index_sub(&self, bucket_index: i32, sub_bucket_index: i32) -> i32;

    //TODO should be default impl of this trait
    fn get_bucket_index(&self, value: i64) -> i32;

    //TODO should be default impl of this trait
    fn get_sub_bucket_index(&self, value: i64, bucket_index: i32) -> i32;

    fn increment_count_at_index(&self, index: i32) -> Result<(), String>;
    fn normalize_index(&self, index: i32, normalizing_index_offset: i32, array_length: i32) ->
        Result<i32, String>;

}

impl HistogramBase for SimpleHdrHistogram {

    fn normalize_index(&self, index: i32, normalizing_index_offset: i32, array_length: i32) ->
Result<i32, String> {
        Ok(0)
//        match normalizing_index_offset {
//            0 => index,
//            _ =>
//                match {
//
//                }
//        }

    }

    fn increment_count_at_index(&self, index: i32) -> Result<(), String> {
        Ok(())
    }

    fn get_sub_bucket_index(&self, value: i64, bucket_index: i32) -> i32 {
        let sum = bucket_index + self.unit_magnitude;
        value.rotate_right(sum as u32) as i32
    }

    fn get_bucket_index(&self, value: i64) -> i32 {
        let valueOrred = value | self.sub_bucket_mask;
        self.leading_zeros_count_base - valueOrred.leading_zeros() as i32
    }

    fn record_single_value(&self, value: i64) -> Result<(), String> {
        let counts_index = self.counts_array_index(value);

//        let something_which_might_error_out = self.increment_count_at_index(counts_index);

        if true {
            Ok(())
        } else {
            Err(String::from("Could not record single value"))
        }
    }

    fn counts_array_index(&self, value: i64) -> Result<i32, String> {
        if value < 0 {
            Err(String::from("Histogram recorded values cannot be negative."))
        } else {
            let bucket_index = self.get_bucket_index(value);
            let sub_bucket_index = self.get_sub_bucket_index(value, bucket_index);
            let result = self.counts_array_index_sub(bucket_index, sub_bucket_index);
            Ok(result)
        }
    }

    fn counts_array_index_sub(&self, bucket_index: i32, sub_bucket_index: i32) -> i32 {
        assert!(sub_bucket_index < self.sub_bucket_count);
        assert!(bucket_index == 0 || (sub_bucket_index >= self.sub_bucket_half_count));

        let bucket_base_index = (bucket_index + 1) << self.sub_bucket_half_count_magnitude;
        let offset_in_bucket = sub_bucket_index - self.sub_bucket_half_count;

        bucket_base_index + offset_in_bucket
    }
}

#[test]
fn can_record_single_value() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.record_single_value(99);

    match result {
        Ok(_) => (),
        Err(err) => panic!(format!("could not add single record to histogram because error: {}", err))
    }
}

#[test]
fn can_compute_counts_array_index() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.counts_array_index(99);

    match result {
        Ok(_) => (),
        Err(err) => panic!(format!("could not compute counts array index because error: {}", err))
    }
}

#[test]
fn can_get_bucket_index() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.get_bucket_index(99);
    assert_eq!(result, 0)
}

#[test]
fn can_get_sub_bucket_index() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.get_sub_bucket_index(99, 1);
    assert_eq!(result, 0)
}<commit_msg>big nasty nested pattern matchers<commit_after>/*

  This struct essentially encapsulates the "instance variables"

*/
pub struct SimpleHdrHistogram {
    pub leading_zeros_count_base: i32,
    pub sub_bucket_mask: i64,
    pub unit_magnitude: i32,
    pub sub_bucket_count: i32,
    pub sub_bucket_half_count: i32,
    pub sub_bucket_half_count_magnitude: i32,
    pub counts: Vec<u64>,
}

/*

  Implementing this trait (Default) for our struct gives us a nice way to
  initialize an instance using default args instead of having to provide all of them

 */
impl Default for SimpleHdrHistogram {
    fn default () -> SimpleHdrHistogram {
        SimpleHdrHistogram {
            leading_zeros_count_base: 0,
            sub_bucket_mask: 0,
            unit_magnitude: 0,
            sub_bucket_count: 0,
            sub_bucket_half_count: 0,
            sub_bucket_half_count_magnitude: 0,
            counts: Vec::new(),
        }
    }
}

pub trait HistogramBase {

    //FIXME this stuff could be mostly unsigned

    //TODO should be default impl of this trait
    fn record_single_value(&self, value: i64) -> Result<(), String>;

    //TODO should be default impl of this trait
    fn counts_array_index(&self, value: i64) -> Result<i32, String>;

    //TODO should be default impl of this trait
    fn counts_array_index_sub(&self, bucket_index: i32, sub_bucket_index: i32) -> i32;

    //TODO should be default impl of this trait
    fn get_bucket_index(&self, value: i64) -> i32;

    //TODO should be default impl of this trait
    fn get_sub_bucket_index(&self, value: i64, bucket_index: i32) -> i32;

    fn increment_count_at_index(&self, index: i32) -> Result<(), String>;
    fn normalize_index(&self, index: i32, normalizing_index_offset: i32, array_length: i32) ->
        Result<i32, String>;

}

impl HistogramBase for SimpleHdrHistogram {

    fn normalize_index(&self, index: i32, normalizing_index_offset: i32, array_length: i32) ->
Result<i32, String> {
        Ok(0)
//        match normalizing_index_offset {
//            0 => index,
//            _ =>
//                match {
//
//                }
//        }

    }

    fn increment_count_at_index(&self, index: i32) -> Result<(), String> {
        Ok(())
    }

    fn get_sub_bucket_index(&self, value: i64, bucket_index: i32) -> i32 {
        let sum = bucket_index + self.unit_magnitude;
        value.rotate_right(sum as u32) as i32
    }

    fn get_bucket_index(&self, value: i64) -> i32 {
        let valueOrred = value | self.sub_bucket_mask;
        self.leading_zeros_count_base - valueOrred.leading_zeros() as i32
    }

    fn record_single_value(&self, value: i64) -> Result<(), String> {

        match self.counts_array_index(value) {
            Ok(counts_index) =>
            match self.increment_count_at_index(counts_index) {
                Ok(_) =>
                if true {
                    Ok(())
                } else {
                    Err(String::from("Could not record single value"))
                },
                Err(err) => Err(String::from("Could not increment stuff"))
            },
            Err(err) => Err(String::from("Could not get index"))
        }

    }

    fn counts_array_index(&self, value: i64) -> Result<i32, String> {
        if value < 0 {
            Err(String::from("Histogram recorded values cannot be negative."))
        } else {
            let bucket_index = self.get_bucket_index(value);
            let sub_bucket_index = self.get_sub_bucket_index(value, bucket_index);
            let result = self.counts_array_index_sub(bucket_index, sub_bucket_index);
            Ok(result)
        }
    }

    fn counts_array_index_sub(&self, bucket_index: i32, sub_bucket_index: i32) -> i32 {
        assert!(sub_bucket_index < self.sub_bucket_count);
        assert!(bucket_index == 0 || (sub_bucket_index >= self.sub_bucket_half_count));

        let bucket_base_index = (bucket_index + 1) << self.sub_bucket_half_count_magnitude;
        let offset_in_bucket = sub_bucket_index - self.sub_bucket_half_count;

        bucket_base_index + offset_in_bucket
    }
}

#[test]
fn can_record_single_value() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.record_single_value(99);

    match result {
        Ok(_) => (),
        Err(err) => panic!(format!("could not add single record to histogram because error: {}", err))
    }
}

#[test]
fn can_compute_counts_array_index() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.counts_array_index(99);

    match result {
        Ok(_) => (),
        Err(err) => panic!(format!("could not compute counts array index because error: {}", err))
    }
}

#[test]
fn can_get_bucket_index() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.get_bucket_index(99);
    assert_eq!(result, 0)
}

#[test]
fn can_get_sub_bucket_index() {
    let the_hist = SimpleHdrHistogram { ..Default::default() };
    let result = the_hist.get_sub_bucket_index(99, 1);
    assert_eq!(result, 0)
}<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate libc;

use std::collections::HashMap;
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::{Path, PathBuf};

use devicemapper::{DM, Device, DmNameBuf};

use super::super::engine::{Engine, Eventable, Pool};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::structures::Table;
use super::super::types::{DevUuid, Name, PoolUuid, Redundancy, RenameAction};

use super::backstore::{find_all, is_stratis_device};
use super::cleanup::teardown_pools;
use super::devlinks;
use super::pool::StratPool;

pub const DEV_PATH: &str = "/dev/stratis";
const REQUIRED_DM_MINOR_VERSION: u32 = 37;

#[derive(Debug, PartialEq, Eq)]
pub enum DevOwnership {
    Ours(PoolUuid, DevUuid),
    Unowned,
    Theirs,
}

#[derive(Debug)]
pub struct StratEngine {
    pools: Table<StratPool>,

    // Map of stratis devices that have been found but one or more stratis block devices are missing
    // which prevents the associated pools from being setup.
    incomplete_pools: HashMap<PoolUuid, HashMap<Device, PathBuf>>,

    // Maps name of DM devices we are watching to the most recent event number
    // we've handled for each
    watched_dev_last_event_nrs: HashMap<DmNameBuf, u32>,
}

impl Eventable for DM {
    /// Get file we'd like to have monitored for activity
    fn get_pollable_fd(&mut self) -> RawFd {
        self.file().as_raw_fd()
    }

    fn clear_event(&mut self) -> EngineResult<()> {
        self.arm_poll()?;

        Ok(())
    }
}

impl StratEngine {
    /// Setup a StratEngine.
    /// 1. Verify the existence of Stratis /dev directory.
    /// 2. Setup all the pools belonging to the engine.
    ///    a. Places any devices which belong to a pool, but are not complete
    ///       in the incomplete pools data structure.
    ///
    /// Returns an error if the kernel doesn't support required DM features.
    /// Returns an error if there was an error reading device nodes.
    /// Returns an error and tears down all the pools if we find a pool with a
    /// duplicate name found.
    pub fn initialize() -> EngineResult<StratEngine> {
        let dm = DM::new()?;
        let minor_dm_version = dm.version()?.1;
        if minor_dm_version < REQUIRED_DM_MINOR_VERSION {
            let err_msg = format!("Requires DM minor version {} but kernel only supports {}",
                                  REQUIRED_DM_MINOR_VERSION,
                                  minor_dm_version);
            return Err(EngineError::Engine(ErrorEnum::Error, err_msg));
        }

        devlinks::setup_dev_path()?;

        let pools = find_all()?;

        let mut table = Table::default();
        let mut incomplete_pools = HashMap::new();

        for (pool_uuid, devices) in &pools {
            match StratPool::setup(&dm, *pool_uuid, devices) {
                Ok((pool_name, pool)) => {
                    let evicted = table.insert(pool_name, *pool_uuid, pool);
                    if evicted.is_some() {
                        // TODO: update state machine on failure.
                        let _ = teardown_pools(&dm, table);
                        let err_msg = "found two pools with the same name";
                        return Err(EngineError::Engine(ErrorEnum::Invalid, err_msg.into()));
                    }
                }
                Err(e) => {
                    // TODO: Add ability to distinguish expected vs. unexpected errors,
                    // expected error would be caused by missing block device(s).
                    warn!("Pool {} failed to setup, reason {:?}.", pool_uuid, e);
                    // Unable to setup pool, it might become complete later.
                    incomplete_pools.insert(*pool_uuid, devices.clone());
                }
            }
        }

        let engine = StratEngine {
            pools: table,
            incomplete_pools,
            watched_dev_last_event_nrs: HashMap::new(),
        };

        devlinks::setup_devlinks(engine.pools().iter())?;

        Ok(engine)
    }

    /// Teardown Stratis, preparatory to a shutdown.
    #[cfg(test)]
    pub fn teardown(self) -> EngineResult<()> {
        teardown_pools(&DM::new()?, self.pools)
    }
}

impl Engine for StratEngine {
    fn create_pool(&mut self,
                   name: &str,
                   blockdev_paths: &[&Path],
                   redundancy: Option<u16>,
                   force: bool)
                   -> EngineResult<PoolUuid> {

        let redundancy = calculate_redundancy!(redundancy);

        if self.pools.contains_name(name) {
            return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.into()));
        }

        let dm = DM::new()?;
        let (uuid, pool) = StratPool::initialize(&dm, name, blockdev_paths, redundancy, force)?;

        let name = Name::new(name.to_owned());
        devlinks::pool_added(&name)?;
        self.pools.insert(name, uuid, pool);
        Ok(uuid)
    }

    /// Evaluate a device node & devicemapper::Device to see if it's a valid
    /// stratis device.  If all the devices are present in the pool and the pool isn't already
    /// up and running, it will get setup and the pool uuid will be returned.
    ///
    /// Returns an error if the status of the block device can not be evaluated.
    fn block_evaluate(&mut self,
                      dev_node: PathBuf,
                      device: Device)
                      -> EngineResult<Option<PoolUuid>> {
        let dm = DM::new()?;
        if let Some(pool_uuid) = is_stratis_device(&dev_node)? {
            if self.pools.contains_uuid(pool_uuid) {
                // TODO: Handle the case where we have found a device for an already active pool
                // ref. https://github.com/stratis-storage/stratisd/issues/748
                warn!("udev add: pool {} is already known, ignoring device {:?}!",
                      pool_uuid,
                      dev_node);
                Ok(None)
            } else {

                let mut devices = self.incomplete_pools
                    .remove(&pool_uuid)
                    .or_else(|| Some(HashMap::new()))
                    .expect("We just retrieved or created a HashMap");
                devices.insert(device, dev_node);

                let rc = match StratPool::setup(&dm, pool_uuid, &devices) {
                    Ok((pool_name, pool)) => {
                        // We know we have a unique uuid, but the pools table requires a unique
                        // name too so we will ensure unique before we insert as we don't want to
                        // change the existing state if we have a conflict.
                        //
                        // We will also _not_ exit the daemon as we do in initialize as we were
                        // previously up and running for some duration of time.
                        if !self.pools.contains_name(&pool_name) {
                            self.pools.insert(pool_name, pool_uuid, pool);
                            Some(pool_uuid)
                        } else {
                            let dev_paths = devices
                                .values()
                                .map(|p| p.to_str().expect("Expecting valid device path!"))
                                .collect::<Vec<&str>>()
                                .join(", ");

                            self.incomplete_pools.insert(pool_uuid, devices);

                            error!("udev add: duplicate pool name {:?} for uuid {:?}, \
                                devices[{}], failing to setup complete pool.",
                                   pool_name,
                                   pool_uuid,
                                   dev_paths);
                            if let Err(e) = pool.teardown(&dm) {
                                error!("Error while tearing down pool with duplicate name {:?}.",
                                       e);
                            }
                            None
                        }
                    }
                    Err(e) => {
                        warn!("udev add: pool {} failed to setup, reason {:?}.",
                              pool_uuid,
                              e);
                        self.incomplete_pools.insert(pool_uuid, devices);
                        None
                    }
                };
                Ok(rc)
            }
        } else {
            Ok(None)
        }
    }

    fn destroy_pool(&mut self, uuid: PoolUuid) -> EngineResult<bool> {
        if let Some((_, pool)) = self.pools.get_by_uuid(uuid) {
            if pool.has_filesystems() {
                return Err(EngineError::Engine(ErrorEnum::Busy,
                                               "filesystems remaining on pool".into()));
            };
        } else {
            return Ok(false);
        }

        let (pool_name, pool) =
            self.pools
                .remove_by_uuid(uuid)
                .expect("Must succeed since self.pools.get_by_uuid() returned a value");

        pool.destroy()?;
        devlinks::pool_removed(&pool_name)?;
        Ok(true)
    }

    fn rename_pool(&mut self, uuid: PoolUuid, new_name: &str) -> EngineResult<RenameAction> {
        let old_name = rename_pool_pre!(self; uuid; new_name);

        let (_, mut pool) =
            self.pools
                .remove_by_uuid(uuid)
                .expect("Must succeed since self.pools.get_by_uuid() returned a value");

        let new_name = Name::new(new_name.to_owned());
        if let Err(err) = pool.write_metadata(&new_name) {
            self.pools.insert(old_name, uuid, pool);
            Err(err)
        } else {
            self.pools.insert(new_name.clone(), uuid, pool);
            devlinks::pool_renamed(&old_name, &new_name)?;
            Ok(RenameAction::Renamed)
        }
    }

    fn get_pool(&self, uuid: PoolUuid) -> Option<(Name, &Pool)> {
        get_pool!(self; uuid)
    }

    fn get_mut_pool(&mut self, uuid: PoolUuid) -> Option<(Name, &mut Pool)> {
        get_mut_pool!(self; uuid)
    }

    fn configure_simulator(&mut self, _denominator: u32) -> EngineResult<()> {
        Ok(()) // we're not the simulator and not configurable, so just say ok
    }

    fn check(&mut self) -> () {
        check_engine!(self);
    }

    fn pools(&self) -> Vec<(Name, PoolUuid, &Pool)> {
        self.pools
            .iter()
            .map(|(name, uuid, pool)| (name.clone(), *uuid, pool as &Pool))
            .collect()
    }

    fn get_eventable(&mut self) -> EngineResult<Option<Box<Eventable>>> {
        Ok(Some(Box::new(DM::new()?)))
    }

    fn evented(&mut self) -> EngineResult<()> {
        let device_list: HashMap<_, _> = DM::new()?
            .list_devices()?
            .into_iter()
            .map(|(dm_name, _, event_nr)| {
                     (dm_name, event_nr.expect("Supported DM versions always provide a value"))
                 })
            .collect();

        for (pool_name, _, pool) in &mut self.pools {
            for dm_name in pool.get_eventing_dev_names() {
                if device_list.get(&dm_name) > self.watched_dev_last_event_nrs.get(&dm_name) {
                    pool.event_on(pool_name, &dm_name)?;
                }
            }
        }

        self.watched_dev_last_event_nrs = device_list;

        Ok(())
    }
}

#[cfg(test)]
mod test {
    use std::fs::remove_dir_all;

    use super::super::tests::{loopbacked, real};

    use super::*;

    /// Verify that a pool rename causes the pool metadata to get the new name.
    fn test_pool_rename(paths: &[&Path]) {
        let mut engine = StratEngine::initialize().unwrap();

        let name1 = "name1";
        let uuid1 = engine.create_pool(&name1, paths, None, false).unwrap();

        let name2 = "name2";
        let action = engine.rename_pool(uuid1, name2).unwrap();

        assert_eq!(action, RenameAction::Renamed);
        engine.teardown().unwrap();

        let engine = StratEngine::initialize().unwrap();
        let pool_name: String = engine.get_pool(uuid1).unwrap().0.to_owned();
        assert_eq!(pool_name, name2);
    }

    #[test]
    pub fn loop_test_pool_rename() {
        loopbacked::test_with_spec(loopbacked::DeviceLimits::Range(1, 3), test_pool_rename);
    }

    #[test]
    pub fn real_test_pool_rename() {
        real::test_with_spec(real::DeviceLimits::AtLeast(1), test_pool_rename);
    }

    /// Test engine setup.
    /// 1. Create two pools.
    /// 2. Verify that both exist.
    /// 3. Teardown the engine.
    /// 4. Initialize the engine.
    /// 5. Verify that pools can be found again.
    /// 6. Teardown the engine and remove "/dev/stratis".
    /// 7. Initialize the engine one more time.
    /// 8. Verify that both pools are found and that there are no incomplete pools.
    fn test_setup(paths: &[&Path]) {
        assert!(paths.len() > 1);

        let (paths1, paths2) = paths.split_at(paths.len() / 2);

        let mut engine = StratEngine::initialize().unwrap();

        let name1 = "name1";
        let uuid1 = engine.create_pool(&name1, paths1, None, false).unwrap();

        let name2 = "name2";
        let uuid2 = engine.create_pool(&name2, paths2, None, false).unwrap();

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();

        let engine = StratEngine::initialize().unwrap();

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();
        remove_dir_all(DEV_PATH).unwrap();

        let engine = StratEngine::initialize().unwrap();
        assert_eq!(engine.incomplete_pools, HashMap::new());

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();
    }

    #[test]
    pub fn loop_test_setup() {
        loopbacked::test_with_spec(loopbacked::DeviceLimits::Range(2, 3), test_setup);
    }

    #[test]
    pub fn real_test_setup() {
        real::test_with_spec(real::DeviceLimits::AtLeast(2), test_setup);
    }
}
<commit_msg>strat_engine/engine.rs: Rework expect<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

extern crate libc;

use std::collections::HashMap;
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::{Path, PathBuf};

use devicemapper::{DM, Device, DmNameBuf};

use super::super::engine::{Engine, Eventable, Pool};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::structures::Table;
use super::super::types::{DevUuid, Name, PoolUuid, Redundancy, RenameAction};

use super::backstore::{find_all, is_stratis_device};
use super::cleanup::teardown_pools;
use super::devlinks;
use super::pool::StratPool;

pub const DEV_PATH: &str = "/dev/stratis";
const REQUIRED_DM_MINOR_VERSION: u32 = 37;

#[derive(Debug, PartialEq, Eq)]
pub enum DevOwnership {
    Ours(PoolUuid, DevUuid),
    Unowned,
    Theirs,
}

#[derive(Debug)]
pub struct StratEngine {
    pools: Table<StratPool>,

    // Map of stratis devices that have been found but one or more stratis block devices are missing
    // which prevents the associated pools from being setup.
    incomplete_pools: HashMap<PoolUuid, HashMap<Device, PathBuf>>,

    // Maps name of DM devices we are watching to the most recent event number
    // we've handled for each
    watched_dev_last_event_nrs: HashMap<DmNameBuf, u32>,
}

impl Eventable for DM {
    /// Get file we'd like to have monitored for activity
    fn get_pollable_fd(&mut self) -> RawFd {
        self.file().as_raw_fd()
    }

    fn clear_event(&mut self) -> EngineResult<()> {
        self.arm_poll()?;

        Ok(())
    }
}

impl StratEngine {
    /// Setup a StratEngine.
    /// 1. Verify the existence of Stratis /dev directory.
    /// 2. Setup all the pools belonging to the engine.
    ///    a. Places any devices which belong to a pool, but are not complete
    ///       in the incomplete pools data structure.
    ///
    /// Returns an error if the kernel doesn't support required DM features.
    /// Returns an error if there was an error reading device nodes.
    /// Returns an error and tears down all the pools if we find a pool with a
    /// duplicate name found.
    pub fn initialize() -> EngineResult<StratEngine> {
        let dm = DM::new()?;
        let minor_dm_version = dm.version()?.1;
        if minor_dm_version < REQUIRED_DM_MINOR_VERSION {
            let err_msg = format!("Requires DM minor version {} but kernel only supports {}",
                                  REQUIRED_DM_MINOR_VERSION,
                                  minor_dm_version);
            return Err(EngineError::Engine(ErrorEnum::Error, err_msg));
        }

        devlinks::setup_dev_path()?;

        let pools = find_all()?;

        let mut table = Table::default();
        let mut incomplete_pools = HashMap::new();

        for (pool_uuid, devices) in &pools {
            match StratPool::setup(&dm, *pool_uuid, devices) {
                Ok((pool_name, pool)) => {
                    let evicted = table.insert(pool_name, *pool_uuid, pool);
                    if evicted.is_some() {
                        // TODO: update state machine on failure.
                        let _ = teardown_pools(&dm, table);
                        let err_msg = "found two pools with the same name";
                        return Err(EngineError::Engine(ErrorEnum::Invalid, err_msg.into()));
                    }
                }
                Err(e) => {
                    // TODO: Add ability to distinguish expected vs. unexpected errors,
                    // expected error would be caused by missing block device(s).
                    warn!("Pool {} failed to setup, reason {:?}.", pool_uuid, e);
                    // Unable to setup pool, it might become complete later.
                    incomplete_pools.insert(*pool_uuid, devices.clone());
                }
            }
        }

        let engine = StratEngine {
            pools: table,
            incomplete_pools,
            watched_dev_last_event_nrs: HashMap::new(),
        };

        devlinks::setup_devlinks(engine.pools().iter())?;

        Ok(engine)
    }

    /// Teardown Stratis, preparatory to a shutdown.
    #[cfg(test)]
    pub fn teardown(self) -> EngineResult<()> {
        teardown_pools(&DM::new()?, self.pools)
    }
}

impl Engine for StratEngine {
    fn create_pool(&mut self,
                   name: &str,
                   blockdev_paths: &[&Path],
                   redundancy: Option<u16>,
                   force: bool)
                   -> EngineResult<PoolUuid> {

        let redundancy = calculate_redundancy!(redundancy);

        if self.pools.contains_name(name) {
            return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.into()));
        }

        let dm = DM::new()?;
        let (uuid, pool) = StratPool::initialize(&dm, name, blockdev_paths, redundancy, force)?;

        let name = Name::new(name.to_owned());
        devlinks::pool_added(&name)?;
        self.pools.insert(name, uuid, pool);
        Ok(uuid)
    }

    /// Evaluate a device node & devicemapper::Device to see if it's a valid
    /// stratis device.  If all the devices are present in the pool and the pool isn't already
    /// up and running, it will get setup and the pool uuid will be returned.
    ///
    /// Returns an error if the status of the block device can not be evaluated.
    fn block_evaluate(&mut self,
                      dev_node: PathBuf,
                      device: Device)
                      -> EngineResult<Option<PoolUuid>> {
        let dm = DM::new()?;
        if let Some(pool_uuid) = is_stratis_device(&dev_node)? {
            if self.pools.contains_uuid(pool_uuid) {
                // TODO: Handle the case where we have found a device for an already active pool
                // ref. https://github.com/stratis-storage/stratisd/issues/748
                warn!("udev add: pool {} is already known, ignoring device {:?}!",
                      pool_uuid,
                      dev_node);
                Ok(None)
            } else {

                let mut devices = self.incomplete_pools
                    .remove(&pool_uuid)
                    .or_else(|| Some(HashMap::new()))
                    .expect("We just retrieved or created a HashMap");
                devices.insert(device, dev_node);

                let rc = match StratPool::setup(&dm, pool_uuid, &devices) {
                    Ok((pool_name, pool)) => {
                        // We know we have a unique uuid, but the pools table requires a unique
                        // name too so we will ensure unique before we insert as we don't want to
                        // change the existing state if we have a conflict.
                        //
                        // We will also _not_ exit the daemon as we do in initialize as we were
                        // previously up and running for some duration of time.
                        if !self.pools.contains_name(&pool_name) {
                            self.pools.insert(pool_name, pool_uuid, pool);
                            Some(pool_uuid)
                        } else {
                            let dev_paths = devices
                                .values()
                                .map(|p| p.to_str().expect("Unix is utf-8"))
                                .collect::<Vec<&str>>()
                                .join(", ");

                            self.incomplete_pools.insert(pool_uuid, devices);

                            error!("udev add: duplicate pool name {:?} for uuid {:?}, \
                                devices[{}], failing to setup complete pool.",
                                   pool_name,
                                   pool_uuid,
                                   dev_paths);
                            if let Err(e) = pool.teardown(&dm) {
                                error!("Error while tearing down pool with duplicate name {:?}.",
                                       e);
                            }
                            None
                        }
                    }
                    Err(e) => {
                        warn!("udev add: pool {} failed to setup, reason {:?}.",
                              pool_uuid,
                              e);
                        self.incomplete_pools.insert(pool_uuid, devices);
                        None
                    }
                };
                Ok(rc)
            }
        } else {
            Ok(None)
        }
    }

    fn destroy_pool(&mut self, uuid: PoolUuid) -> EngineResult<bool> {
        if let Some((_, pool)) = self.pools.get_by_uuid(uuid) {
            if pool.has_filesystems() {
                return Err(EngineError::Engine(ErrorEnum::Busy,
                                               "filesystems remaining on pool".into()));
            };
        } else {
            return Ok(false);
        }

        let (pool_name, pool) =
            self.pools
                .remove_by_uuid(uuid)
                .expect("Must succeed since self.pools.get_by_uuid() returned a value");

        pool.destroy()?;
        devlinks::pool_removed(&pool_name)?;
        Ok(true)
    }

    fn rename_pool(&mut self, uuid: PoolUuid, new_name: &str) -> EngineResult<RenameAction> {
        let old_name = rename_pool_pre!(self; uuid; new_name);

        let (_, mut pool) =
            self.pools
                .remove_by_uuid(uuid)
                .expect("Must succeed since self.pools.get_by_uuid() returned a value");

        let new_name = Name::new(new_name.to_owned());
        if let Err(err) = pool.write_metadata(&new_name) {
            self.pools.insert(old_name, uuid, pool);
            Err(err)
        } else {
            self.pools.insert(new_name.clone(), uuid, pool);
            devlinks::pool_renamed(&old_name, &new_name)?;
            Ok(RenameAction::Renamed)
        }
    }

    fn get_pool(&self, uuid: PoolUuid) -> Option<(Name, &Pool)> {
        get_pool!(self; uuid)
    }

    fn get_mut_pool(&mut self, uuid: PoolUuid) -> Option<(Name, &mut Pool)> {
        get_mut_pool!(self; uuid)
    }

    fn configure_simulator(&mut self, _denominator: u32) -> EngineResult<()> {
        Ok(()) // we're not the simulator and not configurable, so just say ok
    }

    fn check(&mut self) -> () {
        check_engine!(self);
    }

    fn pools(&self) -> Vec<(Name, PoolUuid, &Pool)> {
        self.pools
            .iter()
            .map(|(name, uuid, pool)| (name.clone(), *uuid, pool as &Pool))
            .collect()
    }

    fn get_eventable(&mut self) -> EngineResult<Option<Box<Eventable>>> {
        Ok(Some(Box::new(DM::new()?)))
    }

    fn evented(&mut self) -> EngineResult<()> {
        let device_list: HashMap<_, _> = DM::new()?
            .list_devices()?
            .into_iter()
            .map(|(dm_name, _, event_nr)| {
                     (dm_name, event_nr.expect("Supported DM versions always provide a value"))
                 })
            .collect();

        for (pool_name, _, pool) in &mut self.pools {
            for dm_name in pool.get_eventing_dev_names() {
                if device_list.get(&dm_name) > self.watched_dev_last_event_nrs.get(&dm_name) {
                    pool.event_on(pool_name, &dm_name)?;
                }
            }
        }

        self.watched_dev_last_event_nrs = device_list;

        Ok(())
    }
}

#[cfg(test)]
mod test {
    use std::fs::remove_dir_all;

    use super::super::tests::{loopbacked, real};

    use super::*;

    /// Verify that a pool rename causes the pool metadata to get the new name.
    fn test_pool_rename(paths: &[&Path]) {
        let mut engine = StratEngine::initialize().unwrap();

        let name1 = "name1";
        let uuid1 = engine.create_pool(&name1, paths, None, false).unwrap();

        let name2 = "name2";
        let action = engine.rename_pool(uuid1, name2).unwrap();

        assert_eq!(action, RenameAction::Renamed);
        engine.teardown().unwrap();

        let engine = StratEngine::initialize().unwrap();
        let pool_name: String = engine.get_pool(uuid1).unwrap().0.to_owned();
        assert_eq!(pool_name, name2);
    }

    #[test]
    pub fn loop_test_pool_rename() {
        loopbacked::test_with_spec(loopbacked::DeviceLimits::Range(1, 3), test_pool_rename);
    }

    #[test]
    pub fn real_test_pool_rename() {
        real::test_with_spec(real::DeviceLimits::AtLeast(1), test_pool_rename);
    }

    /// Test engine setup.
    /// 1. Create two pools.
    /// 2. Verify that both exist.
    /// 3. Teardown the engine.
    /// 4. Initialize the engine.
    /// 5. Verify that pools can be found again.
    /// 6. Teardown the engine and remove "/dev/stratis".
    /// 7. Initialize the engine one more time.
    /// 8. Verify that both pools are found and that there are no incomplete pools.
    fn test_setup(paths: &[&Path]) {
        assert!(paths.len() > 1);

        let (paths1, paths2) = paths.split_at(paths.len() / 2);

        let mut engine = StratEngine::initialize().unwrap();

        let name1 = "name1";
        let uuid1 = engine.create_pool(&name1, paths1, None, false).unwrap();

        let name2 = "name2";
        let uuid2 = engine.create_pool(&name2, paths2, None, false).unwrap();

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();

        let engine = StratEngine::initialize().unwrap();

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();
        remove_dir_all(DEV_PATH).unwrap();

        let engine = StratEngine::initialize().unwrap();
        assert_eq!(engine.incomplete_pools, HashMap::new());

        assert!(engine.get_pool(uuid1).is_some());
        assert!(engine.get_pool(uuid2).is_some());

        engine.teardown().unwrap();
    }

    #[test]
    pub fn loop_test_setup() {
        loopbacked::test_with_spec(loopbacked::DeviceLimits::Range(2, 3), test_setup);
    }

    #[test]
    pub fn real_test_setup() {
        real::test_with_spec(real::DeviceLimits::AtLeast(2), test_setup);
    }
}
<|endoftext|>


<commit_before>use std::slice::raw::buf_as_slice;
use std::str::raw::c_str_to_static_slice;
use libc::{c_char, c_uint, c_void, ptrdiff_t, size_t};
use libc;

use {encode, Encode};

pub enum Object { }

pub struct Sel {
	ptr: *const c_void,
}

pub enum Ivar { }

pub enum Method { }

pub enum Class { }

pub type Imp = extern fn(*mut Object, Sel, ...) -> *mut Object;

#[link(name = "Foundation", kind = "framework")]
extern {
	pub fn sel_registerName(name: *const c_char) -> Sel;
	pub fn sel_getName(sel: Sel) -> *const c_char;

	pub fn objc_getClass(name: *const c_char) -> *const Class;
	pub fn class_getName(cls: *const Class) -> *const c_char;
	pub fn class_getInstanceSize(cls: *const Class) -> size_t;
	pub fn class_getInstanceMethod(cls: *const Class, sel: Sel) -> *const Method;
	pub fn class_getInstanceVariable(cls: *const Class, name: *const c_char) -> *const Ivar;
	pub fn class_copyIvarList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Ivar;
	pub fn class_addMethod(cls: *mut Class, name: Sel, imp: Imp, types: *const c_char) -> bool;
	pub fn class_addIvar(cls: *mut Class, name: *const c_char, size: size_t, alignment: u8, types: *const c_char) -> bool;

	pub fn objc_allocateClassPair(superclass: *const Class, name: *const c_char, extraBytes: size_t) -> *mut Class;
	pub fn objc_disposeClassPair(cls: *mut Class);
	pub fn objc_registerClassPair(cls: *mut Class);

	pub fn object_getClass(obj: *const Object) -> *const Class;

	pub fn ivar_getName(ivar: *const Ivar) -> *const c_char;
	pub fn ivar_getOffset(ivar: *const Ivar) -> ptrdiff_t;
	pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char;

	pub fn objc_msgSend(obj: *mut Object, op: Sel, ...) -> *mut Object;

	pub fn method_getName(method: *const Method) -> Sel;
	pub fn method_getImplementation(method: *const Method) -> Imp;
	pub fn method_getTypeEncoding(method: *const Method) -> *const c_char;
	pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
	pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp;
}

impl Sel {
	pub fn register(name: &str) -> Sel {
		name.with_c_str(|name| unsafe {
			sel_registerName(name)
		})
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = sel_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl PartialEq for Sel {
	fn eq(&self, other: &Sel) -> bool {
		self.ptr == other.ptr
	}
}

impl Eq for Sel { }

impl Clone for Sel {
	fn clone(&self) -> Sel { *self }
}

impl Ivar {
	pub fn name(&self) -> &str {
		unsafe {
			let name = ivar_getName(self);
			c_str_to_static_slice(name)
		}
	}

	pub fn offset(&self) -> int {
		let offset = unsafe {
			ivar_getOffset(self)
		};
		offset as int
	}

	pub fn type_encoding(&self) -> &str {
		unsafe {
			let encoding = ivar_getTypeEncoding(self);
			c_str_to_static_slice(encoding)
		}
	}
}

impl Method {
	pub fn name(&self) -> Sel {
		unsafe {
			method_getName(self)
		}
	}

	pub fn type_encoding(&self) -> &str {
		unsafe {
			let encoding = method_getTypeEncoding(self);
			c_str_to_static_slice(encoding)
		}
	}

	pub fn arguments(&self) -> uint {
		unsafe {
			method_getNumberOfArguments(self) as uint
		}
	}

	pub fn implementation(&self) -> Imp {
		unsafe {
			method_getImplementation(self)
		}
	}

	pub unsafe fn set_implementation(&mut self, imp: Imp) -> Imp {
		method_setImplementation(self, imp)
	}
}

impl Object {
	pub fn class(&self) -> &'static Class {
		unsafe {
			&*object_getClass(self)
		}
	}

	pub unsafe fn get_ivar<T: Encode>(&self, name: &str) -> &T {
		let cls = self.class();
		let ptr = match cls.instance_variable(name) {
			Some(ivar) => {
				assert!(ivar.type_encoding() == encode::<T>());
				let offset = ivar.offset();
				let self_ptr = self as *const Object;
				(self_ptr as *const u8).offset(offset) as *const T
			}
			None => fail!("Ivar {} not found on class {}", name, cls.name()),
		};
		&*ptr
	}

	pub unsafe fn get_mut_ivar<T: Encode>(&mut self, name: &str) -> &mut T {
		let cls = self.class();
		let ptr = match cls.instance_variable(name) {
			Some(ivar) => {
				assert!(ivar.type_encoding() == encode::<T>());
				let offset = ivar.offset();
				let self_ptr = self as *mut Object;
				(self_ptr as *mut u8).offset(offset) as *mut T
			}
			None => fail!("Ivar {} not found on class {}", name, cls.name()),
		};
		&mut *ptr
	}

	pub unsafe fn set_ivar<T: Encode>(&mut self, name: &str, value: T) {
		*self.get_mut_ivar::<T>(name) = value;
	}
}

impl Class {
	pub fn get(name: &str) -> Option<&'static Class> {
		let cls = name.with_c_str(|name| unsafe {
			objc_getClass(name)
		});
		if cls.is_null() {
			None
		} else {
			Some(unsafe { &*cls })
		}
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = class_getName(self);
			c_str_to_static_slice(name)
		}
	}

	pub fn instance_size(&self) -> uint {
		unsafe {
			class_getInstanceSize(self) as uint
		}
	}

	pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
		let method = unsafe {
			class_getInstanceMethod(self, sel)
		};
		if method.is_null() {
			None
		} else {
			Some(unsafe { &*method })
		}
	}

	pub fn instance_variable(&self, name: &str) -> Option<&Ivar> {
		let ivar = name.with_c_str(|name| unsafe {
			class_getInstanceVariable(self, name)
		});
		if ivar.is_null() {
			None
		} else {
			Some(unsafe { &*ivar })
		}
	}

	pub fn instance_variables(&self) -> Vec<&Ivar> {
		unsafe {
			let mut count: c_uint = 0;
			let ivars = class_copyIvarList(self, &mut count);
			let vec = buf_as_slice(ivars as *const _, count as uint, |ivars| {
				ivars.to_vec()
			});
			libc::free(ivars as *mut c_void);
			vec
		}
	}
}

pub trait Message { }

impl Message for Object { }

impl Message for Class { }

pub trait ToMessage {
	fn as_ptr(&self) -> *mut Object;

	fn is_nil(&self) -> bool {
		self.as_ptr().is_null()
	}
}

impl<T: Message> ToMessage for *mut T {
	fn as_ptr(&self) -> *mut Object {
		*self as *mut Object
	}
}

impl<'a, T: Message> ToMessage for &'a T {
	fn as_ptr(&self) -> *mut Object {
		(*self as *const T as *mut T).as_ptr()
	}
}
<commit_msg>Added exchange_implementation to Method.<commit_after>use std::slice::raw::buf_as_slice;
use std::str::raw::c_str_to_static_slice;
use libc::{c_char, c_uint, c_void, ptrdiff_t, size_t};
use libc;

use {encode, Encode};

pub enum Object { }

pub struct Sel {
	ptr: *const c_void,
}

pub enum Ivar { }

pub enum Method { }

pub enum Class { }

pub type Imp = extern fn(*mut Object, Sel, ...) -> *mut Object;

#[link(name = "Foundation", kind = "framework")]
extern {
	pub fn sel_registerName(name: *const c_char) -> Sel;
	pub fn sel_getName(sel: Sel) -> *const c_char;

	pub fn objc_getClass(name: *const c_char) -> *const Class;
	pub fn class_getName(cls: *const Class) -> *const c_char;
	pub fn class_getInstanceSize(cls: *const Class) -> size_t;
	pub fn class_getInstanceMethod(cls: *const Class, sel: Sel) -> *const Method;
	pub fn class_getInstanceVariable(cls: *const Class, name: *const c_char) -> *const Ivar;
	pub fn class_copyIvarList(cls: *const Class, outCount: *mut c_uint) -> *mut *const Ivar;
	pub fn class_addMethod(cls: *mut Class, name: Sel, imp: Imp, types: *const c_char) -> bool;
	pub fn class_addIvar(cls: *mut Class, name: *const c_char, size: size_t, alignment: u8, types: *const c_char) -> bool;

	pub fn objc_allocateClassPair(superclass: *const Class, name: *const c_char, extraBytes: size_t) -> *mut Class;
	pub fn objc_disposeClassPair(cls: *mut Class);
	pub fn objc_registerClassPair(cls: *mut Class);

	pub fn object_getClass(obj: *const Object) -> *const Class;

	pub fn ivar_getName(ivar: *const Ivar) -> *const c_char;
	pub fn ivar_getOffset(ivar: *const Ivar) -> ptrdiff_t;
	pub fn ivar_getTypeEncoding(ivar: *const Ivar) -> *const c_char;

	pub fn objc_msgSend(obj: *mut Object, op: Sel, ...) -> *mut Object;

	pub fn method_getName(method: *const Method) -> Sel;
	pub fn method_getImplementation(method: *const Method) -> Imp;
	pub fn method_getTypeEncoding(method: *const Method) -> *const c_char;
	pub fn method_getNumberOfArguments(method: *const Method) -> c_uint;
	pub fn method_setImplementation(method: *mut Method, imp: Imp) -> Imp;
	pub fn method_exchangeImplementations(m1: *mut Method, m2: *mut Method);
}

impl Sel {
	pub fn register(name: &str) -> Sel {
		name.with_c_str(|name| unsafe {
			sel_registerName(name)
		})
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = sel_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl PartialEq for Sel {
	fn eq(&self, other: &Sel) -> bool {
		self.ptr == other.ptr
	}
}

impl Eq for Sel { }

impl Clone for Sel {
	fn clone(&self) -> Sel { *self }
}

impl Ivar {
	pub fn name(&self) -> &str {
		unsafe {
			let name = ivar_getName(self);
			c_str_to_static_slice(name)
		}
	}

	pub fn offset(&self) -> int {
		let offset = unsafe {
			ivar_getOffset(self)
		};
		offset as int
	}

	pub fn type_encoding(&self) -> &str {
		unsafe {
			let encoding = ivar_getTypeEncoding(self);
			c_str_to_static_slice(encoding)
		}
	}
}

impl Method {
	pub fn name(&self) -> Sel {
		unsafe {
			method_getName(self)
		}
	}

	pub fn type_encoding(&self) -> &str {
		unsafe {
			let encoding = method_getTypeEncoding(self);
			c_str_to_static_slice(encoding)
		}
	}

	pub fn arguments(&self) -> uint {
		unsafe {
			method_getNumberOfArguments(self) as uint
		}
	}

	pub fn implementation(&self) -> Imp {
		unsafe {
			method_getImplementation(self)
		}
	}

	pub unsafe fn set_implementation(&mut self, imp: Imp) -> Imp {
		method_setImplementation(self, imp)
	}

	pub unsafe fn exchange_implementation(&mut self, other: &mut Method) {
		method_exchangeImplementations(self, other);
	}
}

impl Object {
	pub fn class(&self) -> &'static Class {
		unsafe {
			&*object_getClass(self)
		}
	}

	pub unsafe fn get_ivar<T: Encode>(&self, name: &str) -> &T {
		let cls = self.class();
		let ptr = match cls.instance_variable(name) {
			Some(ivar) => {
				assert!(ivar.type_encoding() == encode::<T>());
				let offset = ivar.offset();
				let self_ptr = self as *const Object;
				(self_ptr as *const u8).offset(offset) as *const T
			}
			None => fail!("Ivar {} not found on class {}", name, cls.name()),
		};
		&*ptr
	}

	pub unsafe fn get_mut_ivar<T: Encode>(&mut self, name: &str) -> &mut T {
		let cls = self.class();
		let ptr = match cls.instance_variable(name) {
			Some(ivar) => {
				assert!(ivar.type_encoding() == encode::<T>());
				let offset = ivar.offset();
				let self_ptr = self as *mut Object;
				(self_ptr as *mut u8).offset(offset) as *mut T
			}
			None => fail!("Ivar {} not found on class {}", name, cls.name()),
		};
		&mut *ptr
	}

	pub unsafe fn set_ivar<T: Encode>(&mut self, name: &str, value: T) {
		*self.get_mut_ivar::<T>(name) = value;
	}
}

impl Class {
	pub fn get(name: &str) -> Option<&'static Class> {
		let cls = name.with_c_str(|name| unsafe {
			objc_getClass(name)
		});
		if cls.is_null() {
			None
		} else {
			Some(unsafe { &*cls })
		}
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = class_getName(self);
			c_str_to_static_slice(name)
		}
	}

	pub fn instance_size(&self) -> uint {
		unsafe {
			class_getInstanceSize(self) as uint
		}
	}

	pub fn instance_method(&self, sel: Sel) -> Option<&Method> {
		let method = unsafe {
			class_getInstanceMethod(self, sel)
		};
		if method.is_null() {
			None
		} else {
			Some(unsafe { &*method })
		}
	}

	pub fn instance_variable(&self, name: &str) -> Option<&Ivar> {
		let ivar = name.with_c_str(|name| unsafe {
			class_getInstanceVariable(self, name)
		});
		if ivar.is_null() {
			None
		} else {
			Some(unsafe { &*ivar })
		}
	}

	pub fn instance_variables(&self) -> Vec<&Ivar> {
		unsafe {
			let mut count: c_uint = 0;
			let ivars = class_copyIvarList(self, &mut count);
			let vec = buf_as_slice(ivars as *const _, count as uint, |ivars| {
				ivars.to_vec()
			});
			libc::free(ivars as *mut c_void);
			vec
		}
	}
}

pub trait Message { }

impl Message for Object { }

impl Message for Class { }

pub trait ToMessage {
	fn as_ptr(&self) -> *mut Object;

	fn is_nil(&self) -> bool {
		self.as_ptr().is_null()
	}
}

impl<T: Message> ToMessage for *mut T {
	fn as_ptr(&self) -> *mut Object {
		*self as *mut Object
	}
}

impl<'a, T: Message> ToMessage for &'a T {
	fn as_ptr(&self) -> *mut Object {
		(*self as *const T as *mut T).as_ptr()
	}
}
<|endoftext|>


<commit_before>use std::slice::raw::buf_as_slice;
use std::str::raw::c_str_to_static_slice;
use libc::{c_char, c_uint, c_void, ptrdiff_t, size_t};
use libc;

pub enum Object { }

pub struct Sel {
	ptr: *const c_void,
}

pub struct Ivar {
	ptr: *const c_void,
}

pub struct Class {
	ptr: *mut Object,
}

pub type Imp = extern fn(*mut Object, Sel, ...) -> *mut Object;

#[link(name = "Foundation", kind = "framework")]
extern {
	pub fn sel_registerName(name: *const c_char) -> Sel;
	pub fn sel_getName(sel: Sel) -> *const c_char;

	pub fn objc_getClass(name: *const c_char) -> Class;
	pub fn class_getName(cls: Class) -> *const c_char;
	pub fn class_getInstanceSize(cls: Class) -> size_t;
	pub fn class_getInstanceVariable(cls: Class, name: *const c_char) -> Ivar;
	pub fn class_copyIvarList(cls: Class, outCount: *mut c_uint) -> *mut Ivar;
	pub fn class_addMethod(cls: Class, name: Sel, imp: Imp, types: *const c_char) -> bool;
	pub fn class_addIvar(cls: Class, name: *const c_char, size: size_t, alignment: u8, types: *const c_char) -> bool;

	pub fn objc_allocateClassPair(superclass: Class, name: *const c_char, extraBytes: size_t) -> Class;
	pub fn objc_disposeClassPair(cls: Class);
	pub fn objc_registerClassPair(cls: Class);

	pub fn object_setInstanceVariable(obj: *mut Object, name: *const c_char, value: *mut c_void) -> Ivar;
	pub fn object_getInstanceVariable(obj: *mut Object, name: *const c_char, outValue: *mut *mut c_void) -> Ivar;
	pub fn object_setIvar(obj: *mut Object, ivar: Ivar, value: *mut Object);
	pub fn object_getIvar(obj: *mut Object, ivar: Ivar) -> *mut Object;
	pub fn object_getClass(obj: *mut Object) -> Class;

	pub fn ivar_getName(ivar: Ivar) -> *const c_char;
	pub fn ivar_getOffset(ivar: Ivar) -> ptrdiff_t;
	pub fn ivar_getTypeEncoding(ivar: Ivar) -> *const c_char;

	pub fn objc_msgSend(obj: *mut Object, op: Sel, ...) -> *mut Object;
}

impl Sel {
	pub fn register(name: &str) -> Sel {
		name.with_c_str(|name| unsafe {
			sel_registerName(name)
		})
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = sel_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl PartialEq for Sel {
	fn eq(&self, other: &Sel) -> bool {
		self.ptr == other.ptr
	}
}

impl Eq for Sel { }

impl Clone for Sel {
	fn clone(&self) -> Sel { *self }
}

impl Ivar {
	pub fn name(&self) -> &str {
		unsafe {
			let name = ivar_getName(*self);
			c_str_to_static_slice(name)
		}
	}

	pub fn offset(&self) -> int {
		let offset = unsafe {
			ivar_getOffset(*self)
		};
		offset as int
	}

	pub fn type_encoding(&self) -> &str {
		unsafe {
			let encoding = ivar_getName(*self);
			c_str_to_static_slice(encoding)
		}
	}
}

impl Clone for Ivar {
	fn clone(&self) -> Ivar { *self }
}

impl Object {
	unsafe fn get_ivar_ptr<T>(obj: *mut Object, name: &str) -> *mut T {
		if obj.is_null() {
			RawPtr::null()
		} else {
			let ivar = name.with_c_str(|name| {
				object_getInstanceVariable(obj, name, RawPtr::null())
			});
			let bytes = obj as *mut u8;
			let offset = ivar.offset();
			bytes.offset(offset) as *mut T
		}
	}

	pub unsafe fn get_ivar<T>(&self, name: &str) -> &T {
		let obj = self as *const Object as *mut Object;
		&*Object::get_ivar_ptr(obj, name)
	}

	pub unsafe fn get_mut_ivar<T>(&mut self, name: &str) -> &mut T {
		&mut *Object::get_ivar_ptr(self, name)
	}

	pub unsafe fn set_ivar<T>(&mut self, name: &str, value: T) {
		*self.get_mut_ivar::<T>(name) = value;
	}
}

impl Class {
	pub fn get(name: &str) -> Option<Class> {
		let cls = name.with_c_str(|name| unsafe {
			objc_getClass(name)
		});
		if cls.ptr.is_null() {
			None
		} else {
			Some(cls)
		}
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = class_getName(*self);
			c_str_to_static_slice(name)
		}
	}

	pub fn instance_size(&self) -> uint {
		unsafe {
			class_getInstanceSize(*self) as uint
		}
	}

	pub fn instance_variable(&self, name: &str) -> Option<Ivar> {
		let ivar = name.with_c_str(|name| unsafe {
			class_getInstanceVariable(*self, name)
		});
		if ivar.ptr.is_null() {
			None
		} else {
			Some(ivar)
		}
	}

	pub fn instance_variables(&self) -> Vec<Ivar> {
		unsafe {
			let mut count: c_uint = 0;
			let ivars = class_copyIvarList(*self, &mut count) as *const Ivar;
			let vec = buf_as_slice(ivars, count as uint, |ivars| {
				ivars.to_vec()
			});
			libc::free(ivars as *mut c_void);
			vec
		}
	}
}

impl Clone for Class {
	fn clone(&self) -> Class { *self }
}

pub trait Message { }

impl Message for Object { }

pub trait ToMessage {
	fn as_ptr(&self) -> *mut Object;

	fn is_nil(&self) -> bool {
		self.as_ptr().is_null()
	}
}

impl<T: Message> ToMessage for *mut T {
	fn as_ptr(&self) -> *mut Object {
		*self as *mut Object
	}
}

impl<'a, T: Message> ToMessage for &'a T {
	fn as_ptr(&self) -> *mut Object {
		(*self as *const T as *mut T).as_ptr()
	}
}

impl ToMessage for Class {
	fn as_ptr(&self) -> *mut Object {
		self.ptr
	}
}
<commit_msg>Added class method to Object.<commit_after>use std::slice::raw::buf_as_slice;
use std::str::raw::c_str_to_static_slice;
use libc::{c_char, c_uint, c_void, ptrdiff_t, size_t};
use libc;

pub enum Object { }

pub struct Sel {
	ptr: *const c_void,
}

pub struct Ivar {
	ptr: *const c_void,
}

pub struct Class {
	ptr: *mut Object,
}

pub type Imp = extern fn(*mut Object, Sel, ...) -> *mut Object;

#[link(name = "Foundation", kind = "framework")]
extern {
	pub fn sel_registerName(name: *const c_char) -> Sel;
	pub fn sel_getName(sel: Sel) -> *const c_char;

	pub fn objc_getClass(name: *const c_char) -> Class;
	pub fn class_getName(cls: Class) -> *const c_char;
	pub fn class_getInstanceSize(cls: Class) -> size_t;
	pub fn class_getInstanceVariable(cls: Class, name: *const c_char) -> Ivar;
	pub fn class_copyIvarList(cls: Class, outCount: *mut c_uint) -> *mut Ivar;
	pub fn class_addMethod(cls: Class, name: Sel, imp: Imp, types: *const c_char) -> bool;
	pub fn class_addIvar(cls: Class, name: *const c_char, size: size_t, alignment: u8, types: *const c_char) -> bool;

	pub fn objc_allocateClassPair(superclass: Class, name: *const c_char, extraBytes: size_t) -> Class;
	pub fn objc_disposeClassPair(cls: Class);
	pub fn objc_registerClassPair(cls: Class);

	pub fn object_setInstanceVariable(obj: *mut Object, name: *const c_char, value: *mut c_void) -> Ivar;
	pub fn object_getInstanceVariable(obj: *mut Object, name: *const c_char, outValue: *mut *mut c_void) -> Ivar;
	pub fn object_setIvar(obj: *mut Object, ivar: Ivar, value: *mut Object);
	pub fn object_getIvar(obj: *mut Object, ivar: Ivar) -> *mut Object;
	pub fn object_getClass(obj: *mut Object) -> Class;

	pub fn ivar_getName(ivar: Ivar) -> *const c_char;
	pub fn ivar_getOffset(ivar: Ivar) -> ptrdiff_t;
	pub fn ivar_getTypeEncoding(ivar: Ivar) -> *const c_char;

	pub fn objc_msgSend(obj: *mut Object, op: Sel, ...) -> *mut Object;
}

impl Sel {
	pub fn register(name: &str) -> Sel {
		name.with_c_str(|name| unsafe {
			sel_registerName(name)
		})
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = sel_getName(*self);
			c_str_to_static_slice(name)
		}
	}
}

impl PartialEq for Sel {
	fn eq(&self, other: &Sel) -> bool {
		self.ptr == other.ptr
	}
}

impl Eq for Sel { }

impl Clone for Sel {
	fn clone(&self) -> Sel { *self }
}

impl Ivar {
	pub fn name(&self) -> &str {
		unsafe {
			let name = ivar_getName(*self);
			c_str_to_static_slice(name)
		}
	}

	pub fn offset(&self) -> int {
		let offset = unsafe {
			ivar_getOffset(*self)
		};
		offset as int
	}

	pub fn type_encoding(&self) -> &str {
		unsafe {
			let encoding = ivar_getName(*self);
			c_str_to_static_slice(encoding)
		}
	}
}

impl Clone for Ivar {
	fn clone(&self) -> Ivar { *self }
}

impl Object {
	pub fn class(&self) -> Class {
		unsafe {
			object_getClass(self as *const Object as *mut Object)
		}
	}

	pub unsafe fn get_ivar<T>(&self, name: &str) -> &T {
		let cls = self.class();
		let ivar = cls.instance_variable(name).unwrap();
		let ptr = {
			let offset = ivar.offset();
			let self_ptr = self as *const Object;
			(self_ptr as *const u8).offset(offset) as *const T
		};
		&*ptr
	}

	pub unsafe fn get_mut_ivar<T>(&mut self, name: &str) -> &mut T {
		let cls = self.class();
		let ivar = cls.instance_variable(name).unwrap();
		let ptr = {
			let offset = ivar.offset();
			let self_ptr = self as *mut Object;
			(self_ptr as *mut u8).offset(offset) as *mut T
		};
		&mut *ptr
	}

	pub unsafe fn set_ivar<T>(&mut self, name: &str, value: T) {
		*self.get_mut_ivar::<T>(name) = value;
	}
}

impl Class {
	pub fn get(name: &str) -> Option<Class> {
		let cls = name.with_c_str(|name| unsafe {
			objc_getClass(name)
		});
		if cls.ptr.is_null() {
			None
		} else {
			Some(cls)
		}
	}

	pub fn name(&self) -> &str {
		unsafe {
			let name = class_getName(*self);
			c_str_to_static_slice(name)
		}
	}

	pub fn instance_size(&self) -> uint {
		unsafe {
			class_getInstanceSize(*self) as uint
		}
	}

	pub fn instance_variable(&self, name: &str) -> Option<Ivar> {
		let ivar = name.with_c_str(|name| unsafe {
			class_getInstanceVariable(*self, name)
		});
		if ivar.ptr.is_null() {
			None
		} else {
			Some(ivar)
		}
	}

	pub fn instance_variables(&self) -> Vec<Ivar> {
		unsafe {
			let mut count: c_uint = 0;
			let ivars = class_copyIvarList(*self, &mut count) as *const Ivar;
			let vec = buf_as_slice(ivars, count as uint, |ivars| {
				ivars.to_vec()
			});
			libc::free(ivars as *mut c_void);
			vec
		}
	}
}

impl Clone for Class {
	fn clone(&self) -> Class { *self }
}

pub trait Message { }

impl Message for Object { }

pub trait ToMessage {
	fn as_ptr(&self) -> *mut Object;

	fn is_nil(&self) -> bool {
		self.as_ptr().is_null()
	}
}

impl<T: Message> ToMessage for *mut T {
	fn as_ptr(&self) -> *mut Object {
		*self as *mut Object
	}
}

impl<'a, T: Message> ToMessage for &'a T {
	fn as_ptr(&self) -> *mut Object {
		(*self as *const T as *mut T).as_ptr()
	}
}

impl ToMessage for Class {
	fn as_ptr(&self) -> *mut Object {
		self.ptr
	}
}
<|endoftext|>