<commit_before>//! Interfaces for adding custom transports to libgit2

use std::ffi::{CStr, CString};
use std::io::prelude::*;
use std::io;
use std::mem;
use std::slice;
use std::str;
use std::sync::{StaticMutex, MUTEX_INIT};
use libc::{c_int, c_void, c_uint, c_char, size_t};

use {raw, panic, Error, Remote};
use util::Binding;

/// A transport is a structure which knows how to transfer data to and from a
/// remote.
///
/// This transport is a representation of the raw transport underneath it, which
/// is similar to a trait object in Rust.
#[allow(missing_copy_implementations)]
pub struct Transport {
    raw: *mut raw::git_transport,
    owned: bool,
}

/// Interfaced used by smart transports.
///
/// The full-fledged definiton of transports has to deal with lots of
/// nitty-gritty details of the git protocol, but "smart transports" largely
/// only need to deal with read() and write() of data over a channel.
///
/// A smart subtransport is contained within an instance of a smart transport
/// and is delegated to in order to actually conduct network activity to push or
/// pull data from a remote.
pub trait SmartSubtransport: Send + 'static {
    /// Indicates that this subtransport will be performing the specified action
    /// on the specified URL.
    ///
    /// This function is responsible for making any network connections and
    /// returns a stream which can be read and written from in order to
    /// negotiate the git protocol.
    fn action(&self, url: &str, action: Service)
              -> Result<Box<SmartSubtransportStream>, Error>;

    /// Terminates a connection with the remote.
    ///
    /// Each subtransport is guaranteed a call to close() between calls to
    /// action(), except for the following tow natural progressions of actions
    /// against a constant URL.
    ///
    /// 1. UploadPackLs -> UploadPack
    /// 2. ReceivePackLs -> ReceivePack
    fn close(&self) -> Result<(), Error>;
}

/// Actions that a smart transport can ask a subtransport to perform
#[derive(Copy)]
#[allow(missing_docs)]
pub enum Service {
    UploadPackLs,
    UploadPack,
    ReceivePackLs,
    ReceivePack,
}

/// An instance of a stream over which a smart transport will communicate with a
/// remote.
///
/// Currently this only requires the standard `Read` and `Write` traits. This
/// trait also does not need to be implemented manually as long as the `Read`
/// and `Write` traits are implemented.
pub trait SmartSubtransportStream: Read + Write + Send + 'static {}

impl<T: Read + Write + Send + 'static> SmartSubtransportStream for T {}

type TransportFactory = Fn(&Remote) -> Result<Transport, Error> + Send + Sync +
                                        'static;

/// Boxed data payload used for registering new transports.
///
/// Currently only contains a field which knows how to create transports.
struct TransportData {
    factory: Box<TransportFactory>,
}

/// Instance of a `git_smart_subtransport`, must use `#[repr(C)]` to ensure that
/// the C fields come first.
#[repr(C)]
struct RawSmartSubtransport {
    raw: raw::git_smart_subtransport,
    obj: Box<SmartSubtransport>,
}

/// Instance of a `git_smart_subtransport_stream`, must use `#[repr(C)]` to
/// ensure that the C fields come first.
#[repr(C)]
struct RawSmartSubtransportStream {
    raw: raw::git_smart_subtransport_stream,
    obj: Box<SmartSubtransportStream>,
}

/// Add a custom transport definition, to be used in addition to the built-in
/// set of transports that come with libgit2.
///
/// This function is unsafe as it needs to be externally synchronized with calls
/// to creation of other transports.
pub unsafe fn register<F>(prefix: &str, factory: F) -> Result<(), Error>
    where F: Fn(&Remote) -> Result<Transport, Error> + Send + Sync + 'static
{
    let mut data = Box::new(TransportData {
        factory: Box::new(factory),
    });
    let prefix = try!(CString::new(prefix));
    let datap = (&mut *data) as *mut TransportData as *mut c_void;
    try_call!(raw::git_transport_register(prefix,
                                          transport_factory,
                                          datap));
    mem::forget(data);
    Ok(())
}

impl Transport {
    /// Creates a new transport which will use the "smart" transport protocol
    /// for transferring data.
    ///
    /// A smart transport requires a *subtransport* over which data is actually
    /// communicated, but this subtransport largely just needs to be able to
    /// read() and write(). The subtransport provided will be used to make
    /// connections which can then be read/written from.
    ///
    /// The `rpc` argument is `true` if the protocol is stateless, false
    /// otherwise. For example `http://` is stateless but `git://` is not.
    pub fn smart<S>(remote: &Remote,
                    rpc: bool,
                    subtransport: S) -> Result<Transport, Error>
        where S: SmartSubtransport
    {
        static LOCK: StaticMutex = MUTEX_INIT;
        static mut PTR: usize = 0;

        let mut defn = raw::git_smart_subtransport_definition {
            callback: smart_factory,
            rpc: rpc as c_uint,
        };
        let mut ret = 0 as *mut _;

        let raw = Box::new(RawSmartSubtransport {
            raw: raw::git_smart_subtransport {
                action: subtransport_action,
                close: subtransport_close,
                free: subtransport_free,
            },
            obj: Box::new(subtransport),
        });

        // Currently there's no way to pass a paload via the
        // git_smart_subtransport_definition structure, but it's only used as a
        // configuration for the initial creation of the smart transport (verified
        // by reading the current code, hopefully it doesn't change!).
        //
        // We, however, need some state (gotta pass in our
        // `RawSmartSubtransport`). This also means that this block must be
        // entirely synchronized with a lock (boo!)
        unsafe {
            let _g = LOCK.lock();
            assert!(PTR == 0);
            PTR = &*raw as *const RawSmartSubtransport as usize;
            try_call!(raw::git_transport_smart(&mut ret, remote.raw(),
                                               &mut defn as *mut _ as *mut _));
            mem::forget(raw); // ownership transport to `ret`
            PTR = 0;
        }
        return Ok(Transport { raw: ret, owned: true });

        extern fn smart_factory(out: *mut *mut raw::git_smart_subtransport,
                                _owner: *mut raw::git_transport) -> c_int {
            unsafe {
                assert!(PTR as usize != 0);
                *out = PTR as *mut raw::git_smart_subtransport;
                0
            }
        }
    }
}

impl Drop for Transport {
    fn drop(&mut self) {
        if self.owned {
            unsafe {
                ((*self.raw).free)(self.raw)
            }
        }
    }
}

// callback used by register() to create new transports
extern fn transport_factory(out: *mut *mut raw::git_transport,
                            owner: *mut raw::git_remote,
                            param: *mut c_void) -> c_int {
    unsafe {
        let remote = Binding::from_raw(owner);
        let data = &mut *(param as *mut TransportData);
        let res = panic::wrap(|| (data.factory)(&remote));
        mem::forget(remote); // TODO: maybe a method instead?
        let mut transport = match res {
            Some(Ok(transport)) => transport,
            Some(Err(e)) => return e.raw_code() as c_int,
            None => return -1,
        };
        *out = transport.raw;
        transport.owned = false;
        0
    }
}

// callback used by smart transports to delegate an action to a
// `SmartSubtransport` trait object.
extern fn subtransport_action(stream: *mut *mut raw::git_smart_subtransport_stream,
                              raw_transport: *mut raw::git_smart_subtransport,
                              url: *const c_char,
                              action: raw::git_smart_service_t) -> c_int {
    unsafe {
        let url = CStr::from_ptr(url).to_bytes();
        let url = match str::from_utf8(url).ok() {
            Some(s) => s,
            None => return -1,
        };
        let action = match action {
            raw::GIT_SERVICE_UPLOADPACK_LS => Service::UploadPackLs,
            raw::GIT_SERVICE_UPLOADPACK => Service::UploadPack,
            raw::GIT_SERVICE_RECEIVEPACK_LS => Service::ReceivePackLs,
            raw::GIT_SERVICE_RECEIVEPACK => Service::ReceivePack,
        };
        let transport = &mut *(raw_transport as *mut RawSmartSubtransport);
        let obj = match panic::wrap(|| transport.obj.action(url, action)) {
            Some(Ok(s)) => s,
            Some(Err(e)) => return e.raw_code() as c_int,
            None => return -1,
        };
        *stream = mem::transmute(Box::new(RawSmartSubtransportStream {
            raw: raw::git_smart_subtransport_stream {
                subtransport: raw_transport,
                read: stream_read,
                write: stream_write,
                free: stream_free,
            },
            obj: obj,
        }));
        0
    }
}

// callback used by smart transports to close a `SmartSubtransport` trait
// object.
extern fn subtransport_close(transport: *mut raw::git_smart_subtransport)
                             -> c_int {
    unsafe {
        let transport = &mut *(transport as *mut RawSmartSubtransport);
        match panic::wrap(|| transport.obj.close()) {
            Some(Ok(())) => 0,
            Some(Err(e)) => e.raw_code() as c_int,
            None => -1,
        }
    }
}

// callback used by smart transports to free a `SmartSubtransport` trait
// object.
extern fn subtransport_free(transport: *mut raw::git_smart_subtransport) {
    unsafe {
        mem::transmute::<_, Box<RawSmartSubtransport>>(transport);
    }
}

// callback used by smart transports to read from a `SmartSubtransportStream`
// object.
extern fn stream_read(stream: *mut raw::git_smart_subtransport_stream,
                      buffer: *mut c_char,
                      buf_size: size_t,
                      bytes_read: *mut size_t) -> c_int {
    unsafe {
        let transport = &mut *(stream as *mut RawSmartSubtransportStream);
        let buf = slice::from_raw_parts_mut(buffer as *mut u8, buf_size as usize);
        match panic::wrap(|| transport.obj.read(buf)) {
            Some(Ok(n)) => { *bytes_read = n as size_t; 0 }
            Some(Err(e)) => { set_err(e); -2 }
            None => -1,
        }
    }
}

// callback used by smart transports to write to a `SmartSubtransportStream`
// object.
extern fn stream_write(stream: *mut raw::git_smart_subtransport_stream,
                       buffer: *const c_char,
                       len: size_t) -> c_int {
    unsafe {
        let transport = &mut *(stream as *mut RawSmartSubtransportStream);
        let buf = slice::from_raw_parts(buffer as *const u8, len as usize);
        match panic::wrap(|| transport.obj.write_all(buf)) {
            Some(Ok(())) => 0,
            Some(Err(e)) => { set_err(e); -2 }
            None => -1,
        }
    }
}

unsafe fn set_err(e: io::Error) {
    let s = CString::new(e.to_string()).unwrap();
    raw::giterr_set_str(raw::GITERR_NET as c_int, s.as_ptr())
}

// callback used by smart transports to free a `SmartSubtransportStream`
// object.
extern fn stream_free(stream: *mut raw::git_smart_subtransport_stream) {
    unsafe {
        mem::transmute::<_, Box<RawSmartSubtransportStream>>(stream);
    }
}
<commit_msg>Remove usgae of StaticMutex<commit_after>//! Interfaces for adding custom transports to libgit2

use std::ffi::{CStr, CString};
use std::io::prelude::*;
use std::io;
use std::mem;
use std::slice;
use std::str;
use std::sync::{Once, ONCE_INIT, Mutex};
use libc::{c_int, c_void, c_uint, c_char, size_t};

use {raw, panic, Error, Remote};
use util::Binding;

/// A transport is a structure which knows how to transfer data to and from a
/// remote.
///
/// This transport is a representation of the raw transport underneath it, which
/// is similar to a trait object in Rust.
#[allow(missing_copy_implementations)]
pub struct Transport {
    raw: *mut raw::git_transport,
    owned: bool,
}

/// Interfaced used by smart transports.
///
/// The full-fledged definiton of transports has to deal with lots of
/// nitty-gritty details of the git protocol, but "smart transports" largely
/// only need to deal with read() and write() of data over a channel.
///
/// A smart subtransport is contained within an instance of a smart transport
/// and is delegated to in order to actually conduct network activity to push or
/// pull data from a remote.
pub trait SmartSubtransport: Send + 'static {
    /// Indicates that this subtransport will be performing the specified action
    /// on the specified URL.
    ///
    /// This function is responsible for making any network connections and
    /// returns a stream which can be read and written from in order to
    /// negotiate the git protocol.
    fn action(&self, url: &str, action: Service)
              -> Result<Box<SmartSubtransportStream>, Error>;

    /// Terminates a connection with the remote.
    ///
    /// Each subtransport is guaranteed a call to close() between calls to
    /// action(), except for the following tow natural progressions of actions
    /// against a constant URL.
    ///
    /// 1. UploadPackLs -> UploadPack
    /// 2. ReceivePackLs -> ReceivePack
    fn close(&self) -> Result<(), Error>;
}

/// Actions that a smart transport can ask a subtransport to perform
#[derive(Copy)]
#[allow(missing_docs)]
pub enum Service {
    UploadPackLs,
    UploadPack,
    ReceivePackLs,
    ReceivePack,
}

/// An instance of a stream over which a smart transport will communicate with a
/// remote.
///
/// Currently this only requires the standard `Read` and `Write` traits. This
/// trait also does not need to be implemented manually as long as the `Read`
/// and `Write` traits are implemented.
pub trait SmartSubtransportStream: Read + Write + Send + 'static {}

impl<T: Read + Write + Send + 'static> SmartSubtransportStream for T {}

type TransportFactory = Fn(&Remote) -> Result<Transport, Error> + Send + Sync +
                                        'static;

/// Boxed data payload used for registering new transports.
///
/// Currently only contains a field which knows how to create transports.
struct TransportData {
    factory: Box<TransportFactory>,
}

/// Instance of a `git_smart_subtransport`, must use `#[repr(C)]` to ensure that
/// the C fields come first.
#[repr(C)]
struct RawSmartSubtransport {
    raw: raw::git_smart_subtransport,
    obj: Box<SmartSubtransport>,
}

/// Instance of a `git_smart_subtransport_stream`, must use `#[repr(C)]` to
/// ensure that the C fields come first.
#[repr(C)]
struct RawSmartSubtransportStream {
    raw: raw::git_smart_subtransport_stream,
    obj: Box<SmartSubtransportStream>,
}

/// Add a custom transport definition, to be used in addition to the built-in
/// set of transports that come with libgit2.
///
/// This function is unsafe as it needs to be externally synchronized with calls
/// to creation of other transports.
pub unsafe fn register<F>(prefix: &str, factory: F) -> Result<(), Error>
    where F: Fn(&Remote) -> Result<Transport, Error> + Send + Sync + 'static
{
    let mut data = Box::new(TransportData {
        factory: Box::new(factory),
    });
    let prefix = try!(CString::new(prefix));
    let datap = (&mut *data) as *mut TransportData as *mut c_void;
    try_call!(raw::git_transport_register(prefix,
                                          transport_factory,
                                          datap));
    mem::forget(data);
    Ok(())
}

impl Transport {
    /// Creates a new transport which will use the "smart" transport protocol
    /// for transferring data.
    ///
    /// A smart transport requires a *subtransport* over which data is actually
    /// communicated, but this subtransport largely just needs to be able to
    /// read() and write(). The subtransport provided will be used to make
    /// connections which can then be read/written from.
    ///
    /// The `rpc` argument is `true` if the protocol is stateless, false
    /// otherwise. For example `http://` is stateless but `git://` is not.
    pub fn smart<S>(remote: &Remote,
                    rpc: bool,
                    subtransport: S) -> Result<Transport, Error>
        where S: SmartSubtransport
    {
        static INIT: Once = ONCE_INIT;
        static mut LOCK: *mut Mutex<()> = 0 as *mut _;
        static mut PTR: usize = 0;

        let mut defn = raw::git_smart_subtransport_definition {
            callback: smart_factory,
            rpc: rpc as c_uint,
        };
        let mut ret = 0 as *mut _;

        let raw = Box::new(RawSmartSubtransport {
            raw: raw::git_smart_subtransport {
                action: subtransport_action,
                close: subtransport_close,
                free: subtransport_free,
            },
            obj: Box::new(subtransport),
        });

        // Currently there's no way to pass a paload via the
        // git_smart_subtransport_definition structure, but it's only used as a
        // configuration for the initial creation of the smart transport (verified
        // by reading the current code, hopefully it doesn't change!).
        //
        // We, however, need some state (gotta pass in our
        // `RawSmartSubtransport`). This also means that this block must be
        // entirely synchronized with a lock (boo!)
        INIT.call_once(init_lock);
        unsafe {
            let _g = (*LOCK).lock();
            assert!(PTR == 0);
            PTR = &*raw as *const RawSmartSubtransport as usize;
            try_call!(raw::git_transport_smart(&mut ret, remote.raw(),
                                               &mut defn as *mut _ as *mut _));
            mem::forget(raw); // ownership transport to `ret`
            PTR = 0;
        }
        return Ok(Transport { raw: ret, owned: true });

        extern fn smart_factory(out: *mut *mut raw::git_smart_subtransport,
                                _owner: *mut raw::git_transport) -> c_int {
            unsafe {
                assert!(PTR as usize != 0);
                *out = PTR as *mut raw::git_smart_subtransport;
                0
            }
        }

        fn init_lock() {
            unsafe {
                assert!(LOCK.is_null());
                LOCK = mem::transmute(Box::new(Mutex::new(())));
            }
        }
    }
}

impl Drop for Transport {
    fn drop(&mut self) {
        if self.owned {
            unsafe {
                ((*self.raw).free)(self.raw)
            }
        }
    }
}

// callback used by register() to create new transports
extern fn transport_factory(out: *mut *mut raw::git_transport,
                            owner: *mut raw::git_remote,
                            param: *mut c_void) -> c_int {
    unsafe {
        let remote = Binding::from_raw(owner);
        let data = &mut *(param as *mut TransportData);
        let res = panic::wrap(|| (data.factory)(&remote));
        mem::forget(remote); // TODO: maybe a method instead?
        let mut transport = match res {
            Some(Ok(transport)) => transport,
            Some(Err(e)) => return e.raw_code() as c_int,
            None => return -1,
        };
        *out = transport.raw;
        transport.owned = false;
        0
    }
}

// callback used by smart transports to delegate an action to a
// `SmartSubtransport` trait object.
extern fn subtransport_action(stream: *mut *mut raw::git_smart_subtransport_stream,
                              raw_transport: *mut raw::git_smart_subtransport,
                              url: *const c_char,
                              action: raw::git_smart_service_t) -> c_int {
    unsafe {
        let url = CStr::from_ptr(url).to_bytes();
        let url = match str::from_utf8(url).ok() {
            Some(s) => s,
            None => return -1,
        };
        let action = match action {
            raw::GIT_SERVICE_UPLOADPACK_LS => Service::UploadPackLs,
            raw::GIT_SERVICE_UPLOADPACK => Service::UploadPack,
            raw::GIT_SERVICE_RECEIVEPACK_LS => Service::ReceivePackLs,
            raw::GIT_SERVICE_RECEIVEPACK => Service::ReceivePack,
        };
        let transport = &mut *(raw_transport as *mut RawSmartSubtransport);
        let obj = match panic::wrap(|| transport.obj.action(url, action)) {
            Some(Ok(s)) => s,
            Some(Err(e)) => return e.raw_code() as c_int,
            None => return -1,
        };
        *stream = mem::transmute(Box::new(RawSmartSubtransportStream {
            raw: raw::git_smart_subtransport_stream {
                subtransport: raw_transport,
                read: stream_read,
                write: stream_write,
                free: stream_free,
            },
            obj: obj,
        }));
        0
    }
}

// callback used by smart transports to close a `SmartSubtransport` trait
// object.
extern fn subtransport_close(transport: *mut raw::git_smart_subtransport)
                             -> c_int {
    unsafe {
        let transport = &mut *(transport as *mut RawSmartSubtransport);
        match panic::wrap(|| transport.obj.close()) {
            Some(Ok(())) => 0,
            Some(Err(e)) => e.raw_code() as c_int,
            None => -1,
        }
    }
}

// callback used by smart transports to free a `SmartSubtransport` trait
// object.
extern fn subtransport_free(transport: *mut raw::git_smart_subtransport) {
    unsafe {
        mem::transmute::<_, Box<RawSmartSubtransport>>(transport);
    }
}

// callback used by smart transports to read from a `SmartSubtransportStream`
// object.
extern fn stream_read(stream: *mut raw::git_smart_subtransport_stream,
                      buffer: *mut c_char,
                      buf_size: size_t,
                      bytes_read: *mut size_t) -> c_int {
    unsafe {
        let transport = &mut *(stream as *mut RawSmartSubtransportStream);
        let buf = slice::from_raw_parts_mut(buffer as *mut u8, buf_size as usize);
        match panic::wrap(|| transport.obj.read(buf)) {
            Some(Ok(n)) => { *bytes_read = n as size_t; 0 }
            Some(Err(e)) => { set_err(e); -2 }
            None => -1,
        }
    }
}

// callback used by smart transports to write to a `SmartSubtransportStream`
// object.
extern fn stream_write(stream: *mut raw::git_smart_subtransport_stream,
                       buffer: *const c_char,
                       len: size_t) -> c_int {
    unsafe {
        let transport = &mut *(stream as *mut RawSmartSubtransportStream);
        let buf = slice::from_raw_parts(buffer as *const u8, len as usize);
        match panic::wrap(|| transport.obj.write_all(buf)) {
            Some(Ok(())) => 0,
            Some(Err(e)) => { set_err(e); -2 }
            None => -1,
        }
    }
}

unsafe fn set_err(e: io::Error) {
    let s = CString::new(e.to_string()).unwrap();
    raw::giterr_set_str(raw::GITERR_NET as c_int, s.as_ptr())
}

// callback used by smart transports to free a `SmartSubtransportStream`
// object.
extern fn stream_free(stream: *mut raw::git_smart_subtransport_stream) {
    unsafe {
        mem::transmute::<_, Box<RawSmartSubtransportStream>>(stream);
    }
}
<|endoftext|>


<commit_before>
use vertex::{Vertex, VertexProxy};
use edge::{Edge, EdgeProxy};
// define a traversal as a series of stages

struct VertexVisit {
    vertex: VertexProxy
}

struct EdgeVisit {
    edge: EdgeProxy
}

enum TraversalStage {

}

pub enum GraphIterable {
    Vertex(VertexProxy),
    Edge(EdgeProxy)
}

// used to track a single graph traversal
// will be used in conjunction with a GraphQuery

struct Path {
    path: Vec<GraphIterable>
}

impl Path {
    fn new(vertex: VertexProxy) -> Path {
		Path{path:vec![GraphIterable::Vertex(vertex)]}
    }
}

pub struct GraphQuery {
	// many paths
    paths: Vec<Path>
}

impl GraphQuery {
    pub fn new(vertices: Vec<VertexProxy>) -> GraphQuery {
        // create a new path for each proxy, add the proxy
        let mut paths = Vec::new();
        for p in vertices {
			let mut path = Path::new(p);
            paths.push(path)
        }
        GraphQuery{paths:paths}
    }

    pub fn empty() -> GraphQuery {
        // return an empty graph query
        GraphQuery{paths:Vec::new()}
    }

    fn map(closure: &Fn(Path) -> Vec<Path>) -> GraphQuery  {
        GraphQuery{paths:Vec::new()}
    }

    pub fn outV(&self) -> GraphQuery {
        let g = GraphQuery::empty();

		let f = |path: Path| {

		};
//		let g = self.map()
        g
    }
}

<commit_msg>closure returning vec of path<commit_after>
use vertex::{Vertex, VertexProxy};
use edge::{Edge, EdgeProxy};
// define a traversal as a series of stages

struct VertexVisit {
    vertex: VertexProxy
}

struct EdgeVisit {
    edge: EdgeProxy
}

enum TraversalStage {

}

pub enum GraphIterable {
    Vertex(VertexProxy),
    Edge(EdgeProxy)
}

// used to track a single graph traversal
// will be used in conjunction with a GraphQuery

struct Path {
    path: Vec<GraphIterable>
}

impl Path {
    fn new(vertex: VertexProxy) -> Path {
		Path{path:vec![GraphIterable::Vertex(vertex)]}
    }
}

pub struct GraphQuery {
	// many paths
    paths: Vec<Path>
}

impl GraphQuery {
    pub fn new(vertices: Vec<VertexProxy>) -> GraphQuery {
        // create a new path for each proxy, add the proxy
        let mut paths = Vec::new();
        for p in vertices {
			let mut path = Path::new(p);
            paths.push(path)
        }
        GraphQuery{paths:paths}
    }

    pub fn empty() -> GraphQuery {
        // return an empty graph query
        GraphQuery{paths:Vec::new()}
    }

    fn map(closure: &Fn(Path) -> Vec<Path>) -> GraphQuery  {
        GraphQuery{paths:Vec::new()}
    }

    pub fn outV(&self) -> GraphQuery {
        let g = GraphQuery::empty();

		let f = |path: Path| {
			let mut result : Vec<Path>  = Vec::new();
			result
		};
//		let g = self.map()
        g
    }

	pub fn inv(&self) -> GraphQuery {
		let g = GraphQuery::empty();
		g
	}
}

<|endoftext|>


<commit_before>use std::io::stdio::StdinReader;
use std::sync::mpsc::SyncSender;

use super::types::Cmd;
use super::parse::parse;

pub fn parse_input(mut input: StdinReader, tx: SyncSender<Cmd>) {
    let mut inbuf = input.lock();
    for x in inbuf.lines() {
        let s = x.unwrap();
        if let Some(cmd) = parse(&*s) {
            let send_res = tx.send(cmd);
            if send_res.is_err() {
                return;
            }
        }
    }
}
<commit_msg>Panic if parse_input() cannot send commands<commit_after>use std::io::stdio::StdinReader;
use std::sync::mpsc::SyncSender;

use super::types::Cmd;
use super::parse::parse;

pub fn parse_input(mut input: StdinReader, tx: SyncSender<Cmd>) {
    let mut inbuf = input.lock();
    for x in inbuf.lines() {
        let s = x.unwrap();
        if let Some(cmd) = parse(&*s) {
            let send_res = tx.send(cmd).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use std::env::current_dir;
use std::collections::BTreeMap;
use std::collections::HashSet;
use std::ffi::{CString};
use std::fmt::{Debug, Formatter};
use std::fmt::Error as FormatError;
use std::io::Error as IoError;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::ptr::null;

use super::pipe::CPipe;
use super::uidmap::{Uidmap, get_max_uidmap, apply_uidmap};

use libc::{c_int, c_char, pid_t};
use self::Namespace::*;
use super::super::path_util::ToCString;

#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum Namespace {
    NewMount,
    NewUts,
    NewIpc,
    NewUser,
    NewPid,
    NewNet,
}

pub struct Command {
    pub name: String,
    chroot: CString,
    executable: CString,
    arguments: Vec<CString>,
    environment: BTreeMap<String, String>,
    namespaces: HashSet<Namespace>,
    restore_sigmask: bool,
    user_id: i32,
    workdir: CString,
    uidmap: Option<Uidmap>,
    stdin: i32,
    stdout: i32,
    stderr: i32,
}

impl Debug for Command {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FormatError> {
        write!(fmt, "{:?} {:?}", self.executable, self.arguments)
    }
}

impl Command {
    pub fn new(name: String, cmd: &Path) -> Command {
        let slc = cmd.as_os_str().as_bytes();
        return Command {
            name: name,
            chroot: "/".to_cstring(),
            workdir: current_dir().unwrap().to_cstring(),
            executable: slc.to_cstring(),
            arguments: vec!(slc.to_cstring()),
            namespaces: HashSet::new(),
            environment: BTreeMap::new(),
            restore_sigmask: true,
            user_id: 0,
            uidmap: None,
            stdin: 0,
            stdout: 1,
            stderr: 2,
        };
    }
    pub fn set_user_id(&mut self, uid: i32) {
        self.user_id = uid;
    }
    pub fn set_stdout_fd(&mut self, fd: i32) {
        self.stdout = fd;
    }
    pub fn set_stderr_fd(&mut self, fd: i32) {
        self.stderr = fd;
    }
    pub fn set_stdin_fd(&mut self, fd: i32) {
        self.stdin = fd;
    }
    pub fn chroot(&mut self, dir: &Path) {
        self.chroot = dir.to_cstring();
    }
    pub fn set_workdir(&mut self, dir: &Path) {
        self.workdir = dir.to_cstring();
    }
    pub fn keep_sigmask(&mut self) {
        self.restore_sigmask = false;
    }
    pub fn arg<T:AsRef<[u8]>>(&mut self, arg: T) {
        self.arguments.push(arg.to_cstring());
    }
    pub fn args<T:AsRef<[u8]>>(&mut self, arg: &[T]) {
        self.arguments.extend(arg.iter()
            .map(|v| v.to_cstring()));
    }
    pub fn set_env(&mut self, key: String, value: String)
    {
        self.environment.insert(key, value);
    }

    pub fn update_env<'x, I: Iterator<Item=(String, String)>>(&mut self,
        mut env: I)
    {
        for (k, v) in env {
            self.environment.insert(k, v);
        }
    }
    pub fn set_max_uidmap(&mut self) {
        self.namespaces.insert(NewUser);
        self.uidmap = Some(get_max_uidmap().unwrap());
    }
    pub fn set_uidmap(&mut self, uidmap: Uidmap) {
        self.namespaces.insert(NewUser);
        self.uidmap = Some(uidmap);
    }
    pub fn network_ns(&mut self) {
        self.namespaces.insert(NewNet);
        self.namespaces.insert(NewUts);
    }
    pub fn container(&mut self) {
        // Network and user namespaces are set separately
        self.namespaces.insert(NewMount);
        self.namespaces.insert(NewIpc);
        self.namespaces.insert(NewPid);
    }
    pub fn spawn(&self) -> Result<pid_t, String> {
        let mut exec_args: Vec<*const u8> = self.arguments.iter()
            .map(|a| a.as_bytes().as_ptr()).collect();
        exec_args.push(null());
        let environ_cstr: Vec<CString> = self.environment.iter()
            .map(|(k, v)| (k.clone() + "=" + &v).to_cstring())
            .collect();
        let mut exec_environ: Vec<*const u8> = environ_cstr.iter()
            .map(|p| p.as_bytes().as_ptr()).collect();
        exec_environ.push(null());

        let logprefix = format!(
            // Only errors are logged from C code
            "ERROR:vagga::container.c: [{}]", self.name
            ).to_cstring();

        let pipe = try!(CPipe::new()
                        .map_err(|e| format!("Error creating pipe: {}", e)));
        let pid = unsafe { execute_command(&CCommand {
            pipe_reader: pipe.reader,
            logprefix: logprefix.as_bytes().as_ptr(),
            fs_root: self.chroot.as_bytes().as_ptr(),
            exec_path: self.executable.as_bytes().as_ptr(),
            exec_args: exec_args[..].as_ptr(),
            exec_environ: exec_environ[..].as_ptr(),
            namespaces: convert_namespaces(&self.namespaces),
            user_id: self.user_id,
            restore_sigmask: if self.restore_sigmask { 1 } else { 0 },
            workdir: self.workdir.as_ptr(),
            stdin: self.stdin,
            stdout: self.stdout,
            stderr: self.stderr,
        }) };
        if pid < 0 {
            return Err(format!("Error executing: {}", IoError::last_os_error()));
        }
        if let Some(uidmap) = self.uidmap.as_ref() {
            try!(apply_uidmap(pid, uidmap)
                .map_err(|e| format!("Error writing uid_map: {}", e)));
        }
        try!(pipe.wakeup()
            .map_err(|e| format!("Error waking up process: {}. \
                Probably child already dead", e)));
        return Ok(pid)
    }
}

pub fn convert_namespace(value: Namespace) -> c_int {
    match value {
        NewMount => CLONE_NEWNS,
        NewUts => CLONE_NEWUTS,
        NewIpc => CLONE_NEWIPC,
        NewUser => CLONE_NEWUSER,
        NewPid => CLONE_NEWPID,
        NewNet => CLONE_NEWNET,
    }
}

fn convert_namespaces(set: &HashSet<Namespace>) -> c_int {
    let mut ns = 0;
    for &i in set.iter() {
        ns |= convert_namespace(i);
    }
    return ns;
}

static CLONE_NEWNS: c_int = 0x00020000;   /* Set to create new namespace.  */
static CLONE_NEWUTS: c_int = 0x04000000;  /* New utsname group.  */
static CLONE_NEWIPC: c_int = 0x08000000;  /* New ipcs.  */
static CLONE_NEWUSER: c_int = 0x10000000; /* New user namespace.  */
static CLONE_NEWPID: c_int = 0x20000000;  /* New pid namespace.  */
static CLONE_NEWNET: c_int = 0x40000000;  /* New network namespace.  */

#[repr(C)]
pub struct CCommand {
    namespaces: c_int,
    pipe_reader: c_int,
    user_id: c_int,
    restore_sigmask: c_int,
    stdin: c_int,
    stdout: c_int,
    stderr: c_int,
    logprefix: *const u8,
    fs_root: *const u8,
    exec_path: *const u8,
    exec_args: *const*const u8,
    exec_environ: *const*const u8,
    workdir: *const c_char,
}

#[link(name="container", kind="static")]
extern {
    fn execute_command(cmd: *const CCommand) -> pid_t;
}
<commit_msg>Remove unnecessary "#[link..]" attribute<commit_after>#![allow(dead_code)]

use std::env::current_dir;
use std::collections::BTreeMap;
use std::collections::HashSet;
use std::ffi::{CString};
use std::fmt::{Debug, Formatter};
use std::fmt::Error as FormatError;
use std::io::Error as IoError;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::ptr::null;

use super::pipe::CPipe;
use super::uidmap::{Uidmap, get_max_uidmap, apply_uidmap};

use libc::{c_int, c_char, pid_t};
use self::Namespace::*;
use super::super::path_util::ToCString;

#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub enum Namespace {
    NewMount,
    NewUts,
    NewIpc,
    NewUser,
    NewPid,
    NewNet,
}

pub struct Command {
    pub name: String,
    chroot: CString,
    executable: CString,
    arguments: Vec<CString>,
    environment: BTreeMap<String, String>,
    namespaces: HashSet<Namespace>,
    restore_sigmask: bool,
    user_id: i32,
    workdir: CString,
    uidmap: Option<Uidmap>,
    stdin: i32,
    stdout: i32,
    stderr: i32,
}

impl Debug for Command {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FormatError> {
        write!(fmt, "{:?} {:?}", self.executable, self.arguments)
    }
}

impl Command {
    pub fn new(name: String, cmd: &Path) -> Command {
        let slc = cmd.as_os_str().as_bytes();
        return Command {
            name: name,
            chroot: "/".to_cstring(),
            workdir: current_dir().unwrap().to_cstring(),
            executable: slc.to_cstring(),
            arguments: vec!(slc.to_cstring()),
            namespaces: HashSet::new(),
            environment: BTreeMap::new(),
            restore_sigmask: true,
            user_id: 0,
            uidmap: None,
            stdin: 0,
            stdout: 1,
            stderr: 2,
        };
    }
    pub fn set_user_id(&mut self, uid: i32) {
        self.user_id = uid;
    }
    pub fn set_stdout_fd(&mut self, fd: i32) {
        self.stdout = fd;
    }
    pub fn set_stderr_fd(&mut self, fd: i32) {
        self.stderr = fd;
    }
    pub fn set_stdin_fd(&mut self, fd: i32) {
        self.stdin = fd;
    }
    pub fn chroot(&mut self, dir: &Path) {
        self.chroot = dir.to_cstring();
    }
    pub fn set_workdir(&mut self, dir: &Path) {
        self.workdir = dir.to_cstring();
    }
    pub fn keep_sigmask(&mut self) {
        self.restore_sigmask = false;
    }
    pub fn arg<T:AsRef<[u8]>>(&mut self, arg: T) {
        self.arguments.push(arg.to_cstring());
    }
    pub fn args<T:AsRef<[u8]>>(&mut self, arg: &[T]) {
        self.arguments.extend(arg.iter()
            .map(|v| v.to_cstring()));
    }
    pub fn set_env(&mut self, key: String, value: String)
    {
        self.environment.insert(key, value);
    }

    pub fn update_env<'x, I: Iterator<Item=(String, String)>>(&mut self,
        mut env: I)
    {
        for (k, v) in env {
            self.environment.insert(k, v);
        }
    }
    pub fn set_max_uidmap(&mut self) {
        self.namespaces.insert(NewUser);
        self.uidmap = Some(get_max_uidmap().unwrap());
    }
    pub fn set_uidmap(&mut self, uidmap: Uidmap) {
        self.namespaces.insert(NewUser);
        self.uidmap = Some(uidmap);
    }
    pub fn network_ns(&mut self) {
        self.namespaces.insert(NewNet);
        self.namespaces.insert(NewUts);
    }
    pub fn container(&mut self) {
        // Network and user namespaces are set separately
        self.namespaces.insert(NewMount);
        self.namespaces.insert(NewIpc);
        self.namespaces.insert(NewPid);
    }
    pub fn spawn(&self) -> Result<pid_t, String> {
        let mut exec_args: Vec<*const u8> = self.arguments.iter()
            .map(|a| a.as_bytes().as_ptr()).collect();
        exec_args.push(null());
        let environ_cstr: Vec<CString> = self.environment.iter()
            .map(|(k, v)| (k.clone() + "=" + &v).to_cstring())
            .collect();
        let mut exec_environ: Vec<*const u8> = environ_cstr.iter()
            .map(|p| p.as_bytes().as_ptr()).collect();
        exec_environ.push(null());

        let logprefix = format!(
            // Only errors are logged from C code
            "ERROR:vagga::container.c: [{}]", self.name
            ).to_cstring();

        let pipe = try!(CPipe::new()
                        .map_err(|e| format!("Error creating pipe: {}", e)));
        let pid = unsafe { execute_command(&CCommand {
            pipe_reader: pipe.reader,
            logprefix: logprefix.as_bytes().as_ptr(),
            fs_root: self.chroot.as_bytes().as_ptr(),
            exec_path: self.executable.as_bytes().as_ptr(),
            exec_args: exec_args[..].as_ptr(),
            exec_environ: exec_environ[..].as_ptr(),
            namespaces: convert_namespaces(&self.namespaces),
            user_id: self.user_id,
            restore_sigmask: if self.restore_sigmask { 1 } else { 0 },
            workdir: self.workdir.as_ptr(),
            stdin: self.stdin,
            stdout: self.stdout,
            stderr: self.stderr,
        }) };
        if pid < 0 {
            return Err(format!("Error executing: {}", IoError::last_os_error()));
        }
        if let Some(uidmap) = self.uidmap.as_ref() {
            try!(apply_uidmap(pid, uidmap)
                .map_err(|e| format!("Error writing uid_map: {}", e)));
        }
        try!(pipe.wakeup()
            .map_err(|e| format!("Error waking up process: {}. \
                Probably child already dead", e)));
        return Ok(pid)
    }
}

pub fn convert_namespace(value: Namespace) -> c_int {
    match value {
        NewMount => CLONE_NEWNS,
        NewUts => CLONE_NEWUTS,
        NewIpc => CLONE_NEWIPC,
        NewUser => CLONE_NEWUSER,
        NewPid => CLONE_NEWPID,
        NewNet => CLONE_NEWNET,
    }
}

fn convert_namespaces(set: &HashSet<Namespace>) -> c_int {
    let mut ns = 0;
    for &i in set.iter() {
        ns |= convert_namespace(i);
    }
    return ns;
}

static CLONE_NEWNS: c_int = 0x00020000;   /* Set to create new namespace.  */
static CLONE_NEWUTS: c_int = 0x04000000;  /* New utsname group.  */
static CLONE_NEWIPC: c_int = 0x08000000;  /* New ipcs.  */
static CLONE_NEWUSER: c_int = 0x10000000; /* New user namespace.  */
static CLONE_NEWPID: c_int = 0x20000000;  /* New pid namespace.  */
static CLONE_NEWNET: c_int = 0x40000000;  /* New network namespace.  */

#[repr(C)]
pub struct CCommand {
    namespaces: c_int,
    pipe_reader: c_int,
    user_id: c_int,
    restore_sigmask: c_int,
    stdin: c_int,
    stdout: c_int,
    stderr: c_int,
    logprefix: *const u8,
    fs_root: *const u8,
    exec_path: *const u8,
    exec_args: *const*const u8,
    exec_environ: *const*const u8,
    workdir: *const c_char,
}

extern {
    fn execute_command(cmd: *const CCommand) -> pid_t;
}
<|endoftext|>


<commit_before>use core::mem;
use core::sync::atomic::AtomicBool;

use alloc::sync::Arc;

use crate::paging::{RmmA, RmmArch, TableKind};
use crate::syscall::FloatRegisters;

use memoffset::offset_of;
use spin::Once;

/// This must be used by the kernel to ensure that context switches are done atomically
/// Compare and exchange this to true when beginning a context switch on any CPU
/// The `Context::switch_to` function will set it back to false, allowing other CPU's to switch
/// This must be done, as no locks can be held on the stack during switch
pub static CONTEXT_SWITCH_LOCK: AtomicBool = AtomicBool::new(false);

const ST_RESERVED: u128 = 0xFFFF_FFFF_FFFF_0000_0000_0000_0000_0000;

pub const KFX_SIZE: usize = 512;
pub const KFX_ALIGN: usize = 16;

#[derive(Clone, Debug)]
#[repr(C)]
pub struct Context {
    /// RFLAGS register
    rflags: usize,
    /// RBX register
    rbx: usize,
    /// R12 register
    r12: usize,
    /// R13 register
    r13: usize,
    /// R14 register
    r14: usize,
    /// R15 register
    r15: usize,
    /// Base pointer
    rbp: usize,
    /// Stack pointer
    pub(crate) rsp: usize,
    /// FSBASE.
    ///
    /// NOTE: Same fsgsbase behavior as with gsbase.
    pub(crate) fsbase: usize,
    /// GSBASE.
    ///
    /// NOTE: Without fsgsbase, this register will strictly be equal to the register value when
    /// running. With fsgsbase, this is neither saved nor restored upon every syscall (there is no
    /// need to!), and thus it must be re-read from the register before copying this struct.
    pub(crate) gsbase: usize,
}

impl Context {
    pub fn new() -> Context {
        Context {
            rflags: 0,
            rbx: 0,
            r12: 0,
            r13: 0,
            r14: 0,
            r15: 0,
            rbp: 0,
            rsp: 0,
            fsbase: 0,
            gsbase: 0,
        }
    }

    pub fn set_stack(&mut self, address: usize) {
        self.rsp = address;
    }

    pub unsafe fn signal_stack(&mut self, handler: extern fn(usize), sig: u8) {
        self.push_stack(sig as usize);
        self.push_stack(handler as usize);
        self.push_stack(signal_handler_wrapper as usize);
    }

    pub unsafe fn push_stack(&mut self, value: usize) {
        self.rsp -= mem::size_of::<usize>();
        *(self.rsp as *mut usize) = value;
    }

    pub unsafe fn pop_stack(&mut self) -> usize {
        let value = *(self.rsp as *const usize);
        self.rsp += mem::size_of::<usize>();
        value
    }
}
impl super::Context {
    pub fn get_fx_regs(&self) -> FloatRegisters {
        let mut regs = unsafe { self.kfx.as_ptr().cast::<FloatRegisters>().read() };
        regs._reserved = 0;
        let mut new_st = regs.st_space;
        for st in &mut new_st {
            // Only allow access to the 80 lowest bits
            *st &= !ST_RESERVED;
        }
        regs.st_space = new_st;
        regs
    }

    pub fn set_fx_regs(&mut self, mut new: FloatRegisters) {
        {
            let old = unsafe { &*(self.kfx.as_ptr().cast::<FloatRegisters>()) };
            new._reserved = old._reserved;
            let old_st = new.st_space;
            let mut new_st = new.st_space;
            for (new_st, old_st) in new_st.iter_mut().zip(&old_st) {
                *new_st &= !ST_RESERVED;
                *new_st |= old_st & ST_RESERVED;
            }
            new.st_space = new_st;

            // Make sure we don't use `old` from now on
        }

        unsafe {
            self.kfx.as_mut_ptr().cast::<FloatRegisters>().write(new);
        }
    }
}

pub static EMPTY_CR3: Once<rmm::PhysicalAddress> = Once::new();

// SAFETY: EMPTY_CR3 must be initialized.
pub unsafe fn empty_cr3() -> rmm::PhysicalAddress {
    debug_assert!(EMPTY_CR3.poll().is_some());
    *EMPTY_CR3.get_unchecked()
}

/// Switch to the next context by restoring its stack and registers
pub unsafe fn switch_to(prev: &mut super::Context, next: &mut super::Context) {
    core::arch::asm!("
        fxsave64 [{prev_fx}]
        fxrstor64 [{next_fx}]
        ", prev_fx = in(reg) prev.kfx.as_mut_ptr(),
        next_fx = in(reg) next.kfx.as_ptr(),
    );

    {
        use x86::{bits64::segmentation::*, msr};

        // This is so much shorter in Rust!

        if cfg!(feature = "x86_fsgsbase") {
            prev.arch.fsbase = rdfsbase() as usize;
            wrfsbase(next.arch.fsbase as u64);
            swapgs();
            prev.arch.gsbase = rdgsbase() as usize;
            wrgsbase(next.arch.gsbase as u64);
            swapgs();
        } else {
            prev.arch.fsbase = msr::rdmsr(msr::IA32_FS_BASE) as usize;
            msr::wrmsr(msr::IA32_FS_BASE, next.arch.fsbase as u64);
            prev.arch.gsbase = msr::rdmsr(msr::IA32_KERNEL_GSBASE) as usize;
            msr::wrmsr(msr::IA32_KERNEL_GSBASE, next.arch.gsbase as u64);
        }
    }

    match next.addr_space {
        // Since Arc is essentially just wraps a pointer, in this case a regular pointer (as
        // opposed to dyn or slice fat pointers), and NonNull optimization exists, map_or will
        // hopefully be optimized down to checking prev and next pointers, as next cannot be null.
        Some(ref next_space) => if prev.addr_space.as_ref().map_or(true, |prev_space| !Arc::ptr_eq(&prev_space, &next_space)) {
            // Suppose we have two sibling threads A and B. A runs on CPU 0 and B on CPU 1. A
            // recently called yield and is now here about to switch back. Meanwhile, B is
            // currently creating a new mapping in their shared address space, for example a
            // message on a channel.
            //
            // Unless we acquire this lock, it may be possible that the TLB will not contain new
            // entries. While this can be caught and corrected in a page fault handler, this is not
            // true when entries are removed from a page table!
            next_space.read().table.utable.make_current();
        }
        None => {
            RmmA::set_table(TableKind::User, empty_cr3());
        }
    }
    switch_to_inner(&mut prev.arch, &mut next.arch)
}

// Check disassembly!
#[naked]
unsafe extern "sysv64" fn switch_to_inner(_prev: &mut Context, _next: &mut Context) {
    use Context as Cx;

    core::arch::asm!(
        // As a quick reminder for those who are unfamiliar with the System V ABI (extern "C"):
        //
        // - the current parameters are passed in the registers `rdi`, `rsi`,
        // - we can modify scratch registers, e.g. rax
        // - we cannot change callee-preserved registers arbitrarily, e.g. rbx, which is why we
        //   store them here in the first place.
        concat!("
        // Save old registers, and load new ones
        mov [rdi + {off_rbx}], rbx
        mov rbx, [rsi + {off_rbx}]

        mov [rdi + {off_r12}], r12
        mov r12, [rsi + {off_r12}]

        mov [rdi + {off_r13}], r13
        mov r13, [rsi + {off_r13}]

        mov [rdi + {off_r14}], r14
        mov r14, [rsi + {off_r14}]

        mov [rdi + {off_r15}], r15
        mov r15, [rsi + {off_r15}]

        mov [rdi + {off_rbp}], rbp
        mov rbp, [rsi + {off_rbp}]

        mov [rdi + {off_rsp}], rsp
        mov rsp, [rsi + {off_rsp}]

        // push RFLAGS (can only be modified via stack)
        pushfq
        // pop RFLAGS into `self.rflags`
        pop QWORD PTR [rdi + {off_rflags}]

        // push `next.rflags`
        push QWORD PTR [rsi + {off_rflags}]
        // pop into RFLAGS
        popfq

        // When we return, we cannot even guarantee that the return address on the stack, points to
        // the calling function, `context::switch`. Thus, we have to execute this Rust hook by
        // ourselves, which will unlock the contexts before the later switch.

        // Note that switch_finish_hook will be responsible for executing `ret`.
        jmp {switch_hook}

        "),

        off_rflags = const(offset_of!(Cx, rflags)),

        off_rbx = const(offset_of!(Cx, rbx)),
        off_r12 = const(offset_of!(Cx, r12)),
        off_r13 = const(offset_of!(Cx, r13)),
        off_r14 = const(offset_of!(Cx, r14)),
        off_r15 = const(offset_of!(Cx, r15)),
        off_rbp = const(offset_of!(Cx, rbp)),
        off_rsp = const(offset_of!(Cx, rsp)),

        switch_hook = sym crate::context::switch_finish_hook,
        options(noreturn),
    );
}
#[allow(dead_code)]
#[repr(packed)]
pub struct SignalHandlerStack {
    r11: usize,
    r10: usize,
    r9: usize,
    r8: usize,
    rsi: usize,
    rdi: usize,
    rdx: usize,
    rcx: usize,
    rax: usize,
    handler: extern fn(usize),
    sig: usize,
    rip: usize,
}

#[naked]
unsafe extern fn signal_handler_wrapper() {
    #[inline(never)]
    unsafe extern "C" fn inner(stack: &SignalHandlerStack) {
        (stack.handler)(stack.sig);
    }

    // Push scratch registers
    core::arch::asm!(
        "
            push rax
            push rcx
            push rdx
            push rdi
            push rsi
            push r8
            push r9
            push r10
            push r11

            mov rdi, rsp
            call {inner}

            pop r11
            pop r10
            pop r9
            pop r8
            pop rsi
            pop rdi
            pop rdx
            pop rcx
            pop rax
            add rsp, 16
            ret
        ",

        inner = sym inner,
        options(noreturn),
    );
}
<commit_msg>Use push/pup scratch for x86_64 signal handler<commit_after>use core::mem;
use core::sync::atomic::AtomicBool;

use alloc::sync::Arc;

use crate::{push_scratch, pop_scratch};
use crate::interrupt::handler::ScratchRegisters;
use crate::paging::{RmmA, RmmArch, TableKind};
use crate::syscall::FloatRegisters;

use memoffset::offset_of;
use spin::Once;

/// This must be used by the kernel to ensure that context switches are done atomically
/// Compare and exchange this to true when beginning a context switch on any CPU
/// The `Context::switch_to` function will set it back to false, allowing other CPU's to switch
/// This must be done, as no locks can be held on the stack during switch
pub static CONTEXT_SWITCH_LOCK: AtomicBool = AtomicBool::new(false);

const ST_RESERVED: u128 = 0xFFFF_FFFF_FFFF_0000_0000_0000_0000_0000;

pub const KFX_SIZE: usize = 512;
pub const KFX_ALIGN: usize = 16;

#[derive(Clone, Debug)]
#[repr(C)]
pub struct Context {
    /// RFLAGS register
    rflags: usize,
    /// RBX register
    rbx: usize,
    /// R12 register
    r12: usize,
    /// R13 register
    r13: usize,
    /// R14 register
    r14: usize,
    /// R15 register
    r15: usize,
    /// Base pointer
    rbp: usize,
    /// Stack pointer
    pub(crate) rsp: usize,
    /// FSBASE.
    ///
    /// NOTE: Same fsgsbase behavior as with gsbase.
    pub(crate) fsbase: usize,
    /// GSBASE.
    ///
    /// NOTE: Without fsgsbase, this register will strictly be equal to the register value when
    /// running. With fsgsbase, this is neither saved nor restored upon every syscall (there is no
    /// need to!), and thus it must be re-read from the register before copying this struct.
    pub(crate) gsbase: usize,
}

impl Context {
    pub fn new() -> Context {
        Context {
            rflags: 0,
            rbx: 0,
            r12: 0,
            r13: 0,
            r14: 0,
            r15: 0,
            rbp: 0,
            rsp: 0,
            fsbase: 0,
            gsbase: 0,
        }
    }

    pub fn set_stack(&mut self, address: usize) {
        self.rsp = address;
    }

    pub unsafe fn signal_stack(&mut self, handler: extern fn(usize), sig: u8) {
        self.push_stack(sig as usize);
        self.push_stack(handler as usize);
        self.push_stack(signal_handler_wrapper as usize);
    }

    pub unsafe fn push_stack(&mut self, value: usize) {
        self.rsp -= mem::size_of::<usize>();
        *(self.rsp as *mut usize) = value;
    }

    pub unsafe fn pop_stack(&mut self) -> usize {
        let value = *(self.rsp as *const usize);
        self.rsp += mem::size_of::<usize>();
        value
    }
}
impl super::Context {
    pub fn get_fx_regs(&self) -> FloatRegisters {
        let mut regs = unsafe { self.kfx.as_ptr().cast::<FloatRegisters>().read() };
        regs._reserved = 0;
        let mut new_st = regs.st_space;
        for st in &mut new_st {
            // Only allow access to the 80 lowest bits
            *st &= !ST_RESERVED;
        }
        regs.st_space = new_st;
        regs
    }

    pub fn set_fx_regs(&mut self, mut new: FloatRegisters) {
        {
            let old = unsafe { &*(self.kfx.as_ptr().cast::<FloatRegisters>()) };
            new._reserved = old._reserved;
            let old_st = new.st_space;
            let mut new_st = new.st_space;
            for (new_st, old_st) in new_st.iter_mut().zip(&old_st) {
                *new_st &= !ST_RESERVED;
                *new_st |= old_st & ST_RESERVED;
            }
            new.st_space = new_st;

            // Make sure we don't use `old` from now on
        }

        unsafe {
            self.kfx.as_mut_ptr().cast::<FloatRegisters>().write(new);
        }
    }
}

pub static EMPTY_CR3: Once<rmm::PhysicalAddress> = Once::new();

// SAFETY: EMPTY_CR3 must be initialized.
pub unsafe fn empty_cr3() -> rmm::PhysicalAddress {
    debug_assert!(EMPTY_CR3.poll().is_some());
    *EMPTY_CR3.get_unchecked()
}

/// Switch to the next context by restoring its stack and registers
pub unsafe fn switch_to(prev: &mut super::Context, next: &mut super::Context) {
    core::arch::asm!("
        fxsave64 [{prev_fx}]
        fxrstor64 [{next_fx}]
        ", prev_fx = in(reg) prev.kfx.as_mut_ptr(),
        next_fx = in(reg) next.kfx.as_ptr(),
    );

    {
        use x86::{bits64::segmentation::*, msr};

        // This is so much shorter in Rust!

        if cfg!(feature = "x86_fsgsbase") {
            prev.arch.fsbase = rdfsbase() as usize;
            wrfsbase(next.arch.fsbase as u64);
            swapgs();
            prev.arch.gsbase = rdgsbase() as usize;
            wrgsbase(next.arch.gsbase as u64);
            swapgs();
        } else {
            prev.arch.fsbase = msr::rdmsr(msr::IA32_FS_BASE) as usize;
            msr::wrmsr(msr::IA32_FS_BASE, next.arch.fsbase as u64);
            prev.arch.gsbase = msr::rdmsr(msr::IA32_KERNEL_GSBASE) as usize;
            msr::wrmsr(msr::IA32_KERNEL_GSBASE, next.arch.gsbase as u64);
        }
    }

    match next.addr_space {
        // Since Arc is essentially just wraps a pointer, in this case a regular pointer (as
        // opposed to dyn or slice fat pointers), and NonNull optimization exists, map_or will
        // hopefully be optimized down to checking prev and next pointers, as next cannot be null.
        Some(ref next_space) => if prev.addr_space.as_ref().map_or(true, |prev_space| !Arc::ptr_eq(&prev_space, &next_space)) {
            // Suppose we have two sibling threads A and B. A runs on CPU 0 and B on CPU 1. A
            // recently called yield and is now here about to switch back. Meanwhile, B is
            // currently creating a new mapping in their shared address space, for example a
            // message on a channel.
            //
            // Unless we acquire this lock, it may be possible that the TLB will not contain new
            // entries. While this can be caught and corrected in a page fault handler, this is not
            // true when entries are removed from a page table!
            next_space.read().table.utable.make_current();
        }
        None => {
            RmmA::set_table(TableKind::User, empty_cr3());
        }
    }
    switch_to_inner(&mut prev.arch, &mut next.arch)
}

// Check disassembly!
#[naked]
unsafe extern "sysv64" fn switch_to_inner(_prev: &mut Context, _next: &mut Context) {
    use Context as Cx;

    core::arch::asm!(
        // As a quick reminder for those who are unfamiliar with the System V ABI (extern "C"):
        //
        // - the current parameters are passed in the registers `rdi`, `rsi`,
        // - we can modify scratch registers, e.g. rax
        // - we cannot change callee-preserved registers arbitrarily, e.g. rbx, which is why we
        //   store them here in the first place.
        concat!("
        // Save old registers, and load new ones
        mov [rdi + {off_rbx}], rbx
        mov rbx, [rsi + {off_rbx}]

        mov [rdi + {off_r12}], r12
        mov r12, [rsi + {off_r12}]

        mov [rdi + {off_r13}], r13
        mov r13, [rsi + {off_r13}]

        mov [rdi + {off_r14}], r14
        mov r14, [rsi + {off_r14}]

        mov [rdi + {off_r15}], r15
        mov r15, [rsi + {off_r15}]

        mov [rdi + {off_rbp}], rbp
        mov rbp, [rsi + {off_rbp}]

        mov [rdi + {off_rsp}], rsp
        mov rsp, [rsi + {off_rsp}]

        // push RFLAGS (can only be modified via stack)
        pushfq
        // pop RFLAGS into `self.rflags`
        pop QWORD PTR [rdi + {off_rflags}]

        // push `next.rflags`
        push QWORD PTR [rsi + {off_rflags}]
        // pop into RFLAGS
        popfq

        // When we return, we cannot even guarantee that the return address on the stack, points to
        // the calling function, `context::switch`. Thus, we have to execute this Rust hook by
        // ourselves, which will unlock the contexts before the later switch.

        // Note that switch_finish_hook will be responsible for executing `ret`.
        jmp {switch_hook}

        "),

        off_rflags = const(offset_of!(Cx, rflags)),

        off_rbx = const(offset_of!(Cx, rbx)),
        off_r12 = const(offset_of!(Cx, r12)),
        off_r13 = const(offset_of!(Cx, r13)),
        off_r14 = const(offset_of!(Cx, r14)),
        off_r15 = const(offset_of!(Cx, r15)),
        off_rbp = const(offset_of!(Cx, rbp)),
        off_rsp = const(offset_of!(Cx, rsp)),

        switch_hook = sym crate::context::switch_finish_hook,
        options(noreturn),
    );
}
#[allow(dead_code)]
#[repr(packed)]
pub struct SignalHandlerStack {
    scratch: ScratchRegisters,
    handler: extern fn(usize),
    sig: usize,
    rip: usize,
}

#[naked]
unsafe extern fn signal_handler_wrapper() {
    #[inline(never)]
    unsafe extern "C" fn inner(stack: &SignalHandlerStack) {
        (stack.handler)(stack.sig);
    }

    // Push scratch registers
    core::arch::asm!(
        concat!(
            "push rax",
            push_scratch!(),
            "
            mov rdi, rsp
            call {inner}
            ",
            pop_scratch!(),
            "
            add rsp, 16
            ret
            "
        ),
        inner = sym inner,
        options(noreturn)
    );
}
<|endoftext|>


<commit_before>use std::sync::atomics::AtomicBool;
use std::ptr;
use Event;

#[cfg(feature = "window")]
use WindowBuilder;

#[cfg(feature = "headless")]
use HeadlessRendererBuilder;

pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor};

mod event;
mod ffi;
mod init;
mod monitor;

///
#[cfg(feature = "headless")]
pub struct HeadlessContext(Window);

#[cfg(feature = "headless")]
impl HeadlessContext {
    /// See the docs in the crate root file.
    pub fn new(builder: HeadlessRendererBuilder) -> Result<HeadlessContext, String> {
        let HeadlessRendererBuilder { dimensions, gl_version } = builder;
        init::new_window(Some(dimensions), "".to_string(), None, gl_version, true)
            .map(|w| HeadlessContext(w))
    }

    /// See the docs in the crate root file.
    pub unsafe fn make_current(&self) {
        self.0.make_current()
    }

    /// See the docs in the crate root file.
    pub fn get_proc_address(&self, addr: &str) -> *const () {
        self.0.get_proc_address(addr)
    }
}

/// The Win32 implementation of the main `Window` object.
pub struct Window {
    /// Main handle for the window.
    window: ffi::HWND,

    /// This represents a "draw context" for the surface of the window.
    hdc: ffi::HDC,

    /// OpenGL context.
    context: ffi::HGLRC,

    /// Binded to `opengl32.dll`.
    ///
    /// `wglGetProcAddress` returns null for GL 1.1 functions because they are
    ///  already defined by the system. This module contains them.
    gl_library: ffi::HMODULE,

    /// Receiver for the events dispatched by the window callback.
    events_receiver: Receiver<Event>,

    /// True if a `Closed` event has been received.
    is_closed: AtomicBool,
}

#[cfg(feature = "window")]
impl Window {
    /// See the docs in the crate root file.
    pub fn new(builder: WindowBuilder) -> Result<Window, String> {
        let WindowBuilder { dimensions, title, monitor, gl_version } = builder;
        init::new_window(dimensions, title, monitor, gl_version, false)
    }
}

impl Window {
    /// See the docs in the crate root file.
    pub fn is_closed(&self) -> bool {
        use std::sync::atomics::Relaxed;
        self.is_closed.load(Relaxed)
    }

    /// See the docs in the crate root file.
    ///
    /// Calls SetWindowText on the HWND.
    pub fn set_title(&self, text: &str) {
        unsafe {
            ffi::SetWindowTextW(self.window,
                text.utf16_units().chain(Some(0).into_iter())
                .collect::<Vec<u16>>().as_ptr() as ffi::LPCWSTR);
        }
    }

    /// See the docs in the crate root file.
    pub fn get_position(&self) -> Option<(int, int)> {
        use std::mem;

        let mut placement: ffi::WINDOWPLACEMENT = unsafe { mem::zeroed() };
        placement.length = mem::size_of::<ffi::WINDOWPLACEMENT>() as ffi::UINT;

        if unsafe { ffi::GetWindowPlacement(self.window, &mut placement) } == 0 {
            return None
        }

        let ref rect = placement.rcNormalPosition;
        Some((rect.left as int, rect.top as int))
    }

    /// See the docs in the crate root file.
    pub fn set_position(&self, x: int, y: int) {
        use libc;

        unsafe {
            ffi::SetWindowPos(self.window, ptr::null(), x as libc::c_int, y as libc::c_int,
                0, 0, ffi::SWP_NOZORDER | ffi::SWP_NOSIZE);
            ffi::UpdateWindow(self.window);
        }
    }

    /// See the docs in the crate root file.
    pub fn get_inner_size(&self) -> Option<(uint, uint)> {
        use std::mem;
        let mut rect: ffi::RECT = unsafe { mem::uninitialized() };

        if unsafe { ffi::GetClientRect(self.window, &mut rect) } == 0 {
            return None
        }

        Some((
            (rect.right - rect.left) as uint,
            (rect.bottom - rect.top) as uint
        ))
    }

    /// See the docs in the crate root file.
    pub fn get_outer_size(&self) -> Option<(uint, uint)> {
        use std::mem;
        let mut rect: ffi::RECT = unsafe { mem::uninitialized() };

        if unsafe { ffi::GetWindowRect(self.window, &mut rect) } == 0 {
            return None
        }

        Some((
            (rect.right - rect.left) as uint,
            (rect.bottom - rect.top) as uint
        ))
    }

    /// See the docs in the crate root file.
    pub fn set_inner_size(&self, x: uint, y: uint) {
        use libc;

        unsafe {
            ffi::SetWindowPos(self.window, ptr::null(), 0, 0, x as libc::c_int,
                y as libc::c_int, ffi::SWP_NOZORDER | ffi::SWP_NOREPOSITION);
            ffi::UpdateWindow(self.window);
        }
    }

    /// See the docs in the crate root file.
    // TODO: return iterator
    pub fn poll_events(&self) -> Vec<Event> {
        let mut events = Vec::new();
        loop {
            match self.events_receiver.try_recv() {
                Ok(ev) => events.push(ev),
                Err(_) => break
            }
        }

        // if one of the received events is `Closed`, setting `is_closed` to true
        if events.iter().find(|e| match e { &&::Closed => true, _ => false }).is_some() {
            use std::sync::atomics::Relaxed;
            self.is_closed.store(true, Relaxed);
        }

        events
    }

    /// See the docs in the crate root file.
    // TODO: return iterator
    pub fn wait_events(&self) -> Vec<Event> {
        match self.events_receiver.recv_opt() {
            Ok(ev) => {
                // if the received event is `Closed`, setting `is_closed` to true
                match ev {
                    ::Closed => {
                        use std::sync::atomics::Relaxed;
                        self.is_closed.store(true, Relaxed);
                    },
                    _ => ()
                };

                // looing for other possible events in the queue
                let mut result = self.poll_events();
                result.insert(0, ev);
                result
            },

            Err(_) => {
                use std::sync::atomics::Relaxed;
                self.is_closed.store(true, Relaxed);
                vec![]
            }
        }
    }

    /// See the docs in the crate root file.
    pub unsafe fn make_current(&self) {
        // TODO: check return value
        ffi::wgl::MakeCurrent(self.hdc, self.context);
    }

    /// See the docs in the crate root file.
    pub fn get_proc_address(&self, addr: &str) -> *const () {
        use std::c_str::ToCStr;

        unsafe {
            addr.with_c_str(|s| {
                let p = ffi::wgl::GetProcAddress(s) as *const ();
                if !p.is_null() { return p; }
                ffi::GetProcAddress(self.gl_library, s) as *const ()
            })
        }
    }

    /// See the docs in the crate root file.
    pub fn swap_buffers(&self) {
        unsafe {
            // calling glFlush is necessary on Windows 8
            ffi::glFlush();

            ffi::SwapBuffers(self.hdc);
        }
    }
}

#[cfg(feature = "window")]
#[unsafe_destructor]
impl Drop for Window {
    fn drop(&mut self) {
        use std::ptr;
        unsafe { ffi::wgl::MakeCurrent(ptr::null(), ptr::null()); }
        unsafe { ffi::wgl::DeleteContext(self.context); }
        unsafe { ffi::DestroyWindow(self.window); }
    }
}
<commit_msg>Fix win32 window not having a destructor in headless mode<commit_after>use std::sync::atomics::AtomicBool;
use std::ptr;
use Event;

#[cfg(feature = "window")]
use WindowBuilder;

#[cfg(feature = "headless")]
use HeadlessRendererBuilder;

pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor};

mod event;
mod ffi;
mod init;
mod monitor;

///
#[cfg(feature = "headless")]
pub struct HeadlessContext(Window);

#[cfg(feature = "headless")]
impl HeadlessContext {
    /// See the docs in the crate root file.
    pub fn new(builder: HeadlessRendererBuilder) -> Result<HeadlessContext, String> {
        let HeadlessRendererBuilder { dimensions, gl_version } = builder;
        init::new_window(Some(dimensions), "".to_string(), None, gl_version, true)
            .map(|w| HeadlessContext(w))
    }

    /// See the docs in the crate root file.
    pub unsafe fn make_current(&self) {
        self.0.make_current()
    }

    /// See the docs in the crate root file.
    pub fn get_proc_address(&self, addr: &str) -> *const () {
        self.0.get_proc_address(addr)
    }
}

/// The Win32 implementation of the main `Window` object.
pub struct Window {
    /// Main handle for the window.
    window: ffi::HWND,

    /// This represents a "draw context" for the surface of the window.
    hdc: ffi::HDC,

    /// OpenGL context.
    context: ffi::HGLRC,

    /// Binded to `opengl32.dll`.
    ///
    /// `wglGetProcAddress` returns null for GL 1.1 functions because they are
    ///  already defined by the system. This module contains them.
    gl_library: ffi::HMODULE,

    /// Receiver for the events dispatched by the window callback.
    events_receiver: Receiver<Event>,

    /// True if a `Closed` event has been received.
    is_closed: AtomicBool,
}

#[cfg(feature = "window")]
impl Window {
    /// See the docs in the crate root file.
    pub fn new(builder: WindowBuilder) -> Result<Window, String> {
        let WindowBuilder { dimensions, title, monitor, gl_version } = builder;
        init::new_window(dimensions, title, monitor, gl_version, false)
    }
}

impl Window {
    /// See the docs in the crate root file.
    pub fn is_closed(&self) -> bool {
        use std::sync::atomics::Relaxed;
        self.is_closed.load(Relaxed)
    }

    /// See the docs in the crate root file.
    ///
    /// Calls SetWindowText on the HWND.
    pub fn set_title(&self, text: &str) {
        unsafe {
            ffi::SetWindowTextW(self.window,
                text.utf16_units().chain(Some(0).into_iter())
                .collect::<Vec<u16>>().as_ptr() as ffi::LPCWSTR);
        }
    }

    /// See the docs in the crate root file.
    pub fn get_position(&self) -> Option<(int, int)> {
        use std::mem;

        let mut placement: ffi::WINDOWPLACEMENT = unsafe { mem::zeroed() };
        placement.length = mem::size_of::<ffi::WINDOWPLACEMENT>() as ffi::UINT;

        if unsafe { ffi::GetWindowPlacement(self.window, &mut placement) } == 0 {
            return None
        }

        let ref rect = placement.rcNormalPosition;
        Some((rect.left as int, rect.top as int))
    }

    /// See the docs in the crate root file.
    pub fn set_position(&self, x: int, y: int) {
        use libc;

        unsafe {
            ffi::SetWindowPos(self.window, ptr::null(), x as libc::c_int, y as libc::c_int,
                0, 0, ffi::SWP_NOZORDER | ffi::SWP_NOSIZE);
            ffi::UpdateWindow(self.window);
        }
    }

    /// See the docs in the crate root file.
    pub fn get_inner_size(&self) -> Option<(uint, uint)> {
        use std::mem;
        let mut rect: ffi::RECT = unsafe { mem::uninitialized() };

        if unsafe { ffi::GetClientRect(self.window, &mut rect) } == 0 {
            return None
        }

        Some((
            (rect.right - rect.left) as uint,
            (rect.bottom - rect.top) as uint
        ))
    }

    /// See the docs in the crate root file.
    pub fn get_outer_size(&self) -> Option<(uint, uint)> {
        use std::mem;
        let mut rect: ffi::RECT = unsafe { mem::uninitialized() };

        if unsafe { ffi::GetWindowRect(self.window, &mut rect) } == 0 {
            return None
        }

        Some((
            (rect.right - rect.left) as uint,
            (rect.bottom - rect.top) as uint
        ))
    }

    /// See the docs in the crate root file.
    pub fn set_inner_size(&self, x: uint, y: uint) {
        use libc;

        unsafe {
            ffi::SetWindowPos(self.window, ptr::null(), 0, 0, x as libc::c_int,
                y as libc::c_int, ffi::SWP_NOZORDER | ffi::SWP_NOREPOSITION);
            ffi::UpdateWindow(self.window);
        }
    }

    /// See the docs in the crate root file.
    // TODO: return iterator
    pub fn poll_events(&self) -> Vec<Event> {
        let mut events = Vec::new();
        loop {
            match self.events_receiver.try_recv() {
                Ok(ev) => events.push(ev),
                Err(_) => break
            }
        }

        // if one of the received events is `Closed`, setting `is_closed` to true
        if events.iter().find(|e| match e { &&::Closed => true, _ => false }).is_some() {
            use std::sync::atomics::Relaxed;
            self.is_closed.store(true, Relaxed);
        }

        events
    }

    /// See the docs in the crate root file.
    // TODO: return iterator
    pub fn wait_events(&self) -> Vec<Event> {
        match self.events_receiver.recv_opt() {
            Ok(ev) => {
                // if the received event is `Closed`, setting `is_closed` to true
                match ev {
                    ::Closed => {
                        use std::sync::atomics::Relaxed;
                        self.is_closed.store(true, Relaxed);
                    },
                    _ => ()
                };

                // looing for other possible events in the queue
                let mut result = self.poll_events();
                result.insert(0, ev);
                result
            },

            Err(_) => {
                use std::sync::atomics::Relaxed;
                self.is_closed.store(true, Relaxed);
                vec![]
            }
        }
    }

    /// See the docs in the crate root file.
    pub unsafe fn make_current(&self) {
        // TODO: check return value
        ffi::wgl::MakeCurrent(self.hdc, self.context);
    }

    /// See the docs in the crate root file.
    pub fn get_proc_address(&self, addr: &str) -> *const () {
        use std::c_str::ToCStr;

        unsafe {
            addr.with_c_str(|s| {
                let p = ffi::wgl::GetProcAddress(s) as *const ();
                if !p.is_null() { return p; }
                ffi::GetProcAddress(self.gl_library, s) as *const ()
            })
        }
    }

    /// See the docs in the crate root file.
    pub fn swap_buffers(&self) {
        unsafe {
            // calling glFlush is necessary on Windows 8
            ffi::glFlush();

            ffi::SwapBuffers(self.hdc);
        }
    }
}

#[unsafe_destructor]
impl Drop for Window {
    fn drop(&mut self) {
        use std::ptr;
        unsafe { ffi::wgl::MakeCurrent(ptr::null(), ptr::null()); }
        unsafe { ffi::wgl::DeleteContext(self.context); }
        unsafe { ffi::DestroyWindow(self.window); }
    }
}
<|endoftext|>


<commit_before>struct Droppable {
    name: &'static str,
}

// This `drop` implementation doesn't free resources; but instead reports its
// usage via a print to the console
impl Drop for Droppable {
    fn drop(&mut self) {
        println!("> Dropping {}", self.name);
    }
}

fn main() {
    let _a = Droppable { name: "a" };

    // block A
    {
        let _b = Droppable { name: "b" };

        // block B
        {
            let _c = Droppable { name: "c" };
            let _d = Droppable { name: "d" };

            println!("Exiting block B");
        }
        println!("Just exited block B");

        println!("Exiting block A");
    }
    println!("Just exited block A");

    println!("end of the main function");
}
<commit_msg>drop: show how to explicitly `drop` variables<commit_after>struct Droppable {
    name: &'static str,
}

// This `drop` implementation doesn't free resources; but instead reports its
// usage via a print to the console
impl Drop for Droppable {
    fn drop(&mut self) {
        println!("> Dropping {}", self.name);
    }
}

fn main() {
    let _a = Droppable { name: "a" };

    // block A
    {
        let _b = Droppable { name: "b" };

        // block B
        {
            let _c = Droppable { name: "c" };
            let _d = Droppable { name: "d" };

            println!("Exiting block B");
        }
        println!("Just exited block B");

        println!("Exiting block A");
    }
    println!("Just exited block A");

    // Variable can be manually dropped using the `drop` function
    drop(_a);
    // TODO ^ Try uncommenting this line

    println!("end of the main function");

    // `_a` *won't* be `drop`ed again here, because it already has been
    // (manually) `drop`ed
}
<|endoftext|>