<commit_before>use {Event, WindowBuilder};
use libc;
use std::{mem, ptr};
use std::sync::atomics::AtomicBool;

mod events;
mod ffi;

pub struct Window {
    display: *mut ffi::Display,
    window: ffi::Window,
    im: ffi::XIM,
    ic: ffi::XIC,
    context: ffi::GLXContext,
    is_closed: AtomicBool,
    wm_delete_window: ffi::Atom,
}

pub struct MonitorID(uint);

pub fn get_available_monitors() -> Vec<MonitorID> {
    unimplemented!()
}

pub fn get_primary_monitor() -> MonitorID {
    unimplemented!()
}

impl MonitorID {
    pub fn get_name(&self) -> Option<String> {
        Some("<Unknown>".to_string())
    }

    pub fn get_dimensions(&self) -> (uint, uint) {
        unimplemented!()
    }
}

impl Window {
    pub fn new(builder: WindowBuilder) -> Result<Window, String> {
        // TODO: temporary
        let dimensions = builder.dimensions;

        // calling XOpenDisplay
        let display = unsafe {
            let display = ffi::XOpenDisplay(ptr::null());
            if display.is_null() {
                return Err(format!("XOpenDisplay failed"));
            }
            display
        };

        // TODO: set error handler?

        // getting the FBConfig
        let fb_config = unsafe {
            static VISUAL_ATTRIBUTES: [libc::c_int, ..23] = [
                ffi::GLX_X_RENDERABLE,  1,
                ffi::GLX_DRAWABLE_TYPE, ffi::GLX_WINDOW_BIT,
                ffi::GLX_RENDER_TYPE,   ffi::GLX_RGBA_BIT,
                ffi::GLX_X_VISUAL_TYPE, ffi::GLX_TRUE_COLOR,
                ffi::GLX_RED_SIZE,      8,
                ffi::GLX_GREEN_SIZE,    8,
                ffi::GLX_BLUE_SIZE,     8,
                ffi::GLX_ALPHA_SIZE,    8,
                ffi::GLX_DEPTH_SIZE,    24,
                ffi::GLX_STENCIL_SIZE,  8,
                ffi::GLX_DOUBLEBUFFER,  1,
                0
            ];

            let mut num_fb: libc::c_int = mem::uninitialized();

            let fb = ffi::glXChooseFBConfig(display, ffi::XDefaultScreen(display),
                VISUAL_ATTRIBUTES.as_ptr(), &mut num_fb);
            if fb.is_null() {
                return Err(format!("glXChooseFBConfig failed"));
            }
            let preferred_fb = *fb;     // TODO: choose more wisely
            ffi::XFree(fb as *const libc::c_void);
            preferred_fb
        };

        // getting the visual infos
        let visual_infos = unsafe {
            let vi = ffi::glXGetVisualFromFBConfig(display, fb_config);
            if vi.is_null() {
                return Err(format!("glXChooseVisual failed"));
            }
            let vi_copy = *vi;
            ffi::XFree(vi as *const libc::c_void);
            vi_copy
        };

        // getting the root window
        let root = unsafe { ffi::XDefaultRootWindow(display) };

        // creating the color map
        let cmap = unsafe {
            let cmap = ffi::XCreateColormap(display, root,
                visual_infos.visual, ffi::AllocNone);
            // TODO: error checking?
            cmap
        };

        // creating
        let mut set_win_attr = {
            let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() };
            swa.colormap = cmap;
            swa.event_mask = ffi::ExposureMask | ffi::ResizeRedirectMask |
                ffi::VisibilityChangeMask | ffi::KeyPressMask | ffi::PointerMotionMask |
                ffi::KeyPressMask | ffi::KeyReleaseMask | ffi::ButtonPressMask |
                ffi::ButtonReleaseMask | ffi::KeymapStateMask;
            swa
        };

        // finally creating the window
        let window = unsafe {
            let dimensions = dimensions.unwrap_or((800, 600));

            let win = ffi::XCreateWindow(display, root, 50, 50, dimensions.val0() as libc::c_uint,
                dimensions.val1() as libc::c_uint, 0, visual_infos.depth, ffi::InputOutput,
                visual_infos.visual, ffi::CWColormap | ffi::CWEventMask,
                &mut set_win_attr);
            win
        };

        // creating window, step 2
        let wm_delete_window = unsafe {
            use std::c_str::ToCStr;

            ffi::XMapWindow(display, window);
            let mut wm_delete_window = ffi::XInternAtom(display,
                "WM_DELETE_WINDOW".to_c_str().as_ptr() as *const libc::c_char, 0);
            ffi::XSetWMProtocols(display, window, &mut wm_delete_window, 1);
            ffi::XStoreName(display, window, mem::transmute(builder.title.as_slice().as_ptr()));
            ffi::XFlush(display);

            wm_delete_window
        };

        // getting the pointer to glXCreateContextAttribs
        let create_context_attribs = unsafe {
            // creating the dummy context
            let dummy_context =
                ffi::glXCreateContext(display, &visual_infos, ptr::null(), 1);
            if dummy_context.is_null() {
                return Err(format!("glXCreateContext failed"));
            }

            if ffi::glXMakeCurrent(display, window, dummy_context) == 0 {
                return Err(format!("glXMakeCurrent with dummy context failed"));
            }

            // getting the pointer
            let fn_ptr = {
                let mut addr = unsafe { ffi::glXGetProcAddress(b"glXCreateContextAttribs".as_ptr()
                    as *const u8) } as *const ();

                if addr.is_null() {
                    addr = unsafe { ffi::glXGetProcAddress(b"glXCreateContextAttribsARB".as_ptr()
                        as *const u8) } as *const ();
                }

                addr.to_option().map(|addr| {
                    let addr: extern "system" fn(*mut ffi::Display, ffi::GLXFBConfig, ffi::GLXContext,
                        ffi::Bool, *const libc::c_int) -> ffi::GLXContext = mem::transmute(addr);
                    addr
                })
            };

            // cleaning up
            ffi::glXMakeCurrent(ptr::mut_null(), 0, ptr::null());
            ffi::glXDestroyContext(display, dummy_context);

            fn_ptr
        };

        // creating IM
        let im = unsafe {
            let im = ffi::XOpenIM(display, ptr::null(), ptr::mut_null(), ptr::mut_null());
            if im.is_null() {
                return Err(format!("XOpenIM failed"));
            }
            im
        };

        // creating input context
        let ic = unsafe {
            use std::c_str::ToCStr;

            let ic = ffi::XCreateIC(im, "inputStyle".to_c_str().as_ptr(),
                ffi::XIMPreeditNothing | ffi::XIMStatusNothing, "clientWindow".to_c_str().as_ptr(),
                window, ptr::null());
            if ic.is_null() {
                return Err(format!("XCreateIC failed"));
            }
            ffi::XSetICFocus(ic);
            ic
        };

        // creating GL context
        let context = unsafe {
            let mut attributes = Vec::new();

            if builder.gl_version.is_some() {
                let version = builder.gl_version.as_ref().unwrap();
                attributes.push(ffi::GLX_CONTEXT_MAJOR_VERSION);
                attributes.push(version.val0() as libc::c_int);
                attributes.push(ffi::GLX_CONTEXT_MINOR_VERSION);
                attributes.push(version.val1() as libc::c_int);
            }

            attributes.push(0);

            let context = if create_context_attribs.is_some() {
                let create_context_attribs = create_context_attribs.unwrap();
                create_context_attribs(display, fb_config, ptr::null(), 1,
                    attributes.as_ptr())
            } else {
                ffi::glXCreateNewContext(display, fb_config, ffi::GLX_RGBA_TYPE, ptr::null(), 1)
            };

            if context.is_null() {
                return Err(format!("GL context creation failed"));
            }

            context
        };

        // returning
        Ok(Window{
            display: display,
            window: window,
            im: im,
            ic: ic,
            context: context,
            is_closed: AtomicBool::new(false),
            wm_delete_window: wm_delete_window,
        })
    }

    pub fn is_closed(&self) -> bool {
        use std::sync::atomics::Relaxed;
        self.is_closed.load(Relaxed)
    }

    pub fn set_title(&self, title: &str) {
        unsafe {
            ffi::XStoreName(self.display, self.window,
                mem::transmute(title.as_slice().as_ptr()));
        }
    }

    fn get_geometry(&self) -> Option<(int, int, uint, uint)> {
        unsafe {
            use std::mem;

            let mut root: ffi::Window = mem::uninitialized();
            let mut x: libc::c_int = mem::uninitialized();
            let mut y: libc::c_int = mem::uninitialized();
            let mut width: libc::c_uint = mem::uninitialized();
            let mut height: libc::c_uint = mem::uninitialized();
            let mut border: libc::c_uint = mem::uninitialized();
            let mut depth: libc::c_uint = mem::uninitialized();

            if ffi::XGetGeometry(self.display, self.window,
                &mut root, &mut x, &mut y, &mut width, &mut height,
                &mut border, &mut depth) == 0
            {
                return None;
            }

            Some((x as int, y as int, width as uint, height as uint))
        }
    }

    pub fn get_position(&self) -> Option<(int, int)> {
        self.get_geometry().map(|(x, y, _, _)| (x, y))
    }

    pub fn set_position(&self, x: uint, y: uint) {
        unsafe { ffi::XMoveWindow(self.display, self.window, x as libc::c_int, y as libc::c_int) }
    }

    pub fn get_inner_size(&self) -> Option<(uint, uint)> {
        self.get_geometry().map(|(_, _, w, h)| (w, h))
    }

    pub fn get_outer_size(&self) -> Option<(uint, uint)> {
        unimplemented!()
    }

    pub fn set_inner_size(&self, x: uint, y: uint) {
        unimplemented!()
    }

    pub fn poll_events(&self) -> Vec<Event> {
        use std::mem;

        let mut events = Vec::new();

        loop {
            use std::num::Bounded;

            let mut xev = unsafe { mem::uninitialized() };
            let res = unsafe { ffi::XCheckMaskEvent(self.display, Bounded::max_value(), &mut xev) };

            if res == 0 {
                let res = unsafe { ffi::XCheckTypedEvent(self.display, ffi::ClientMessage, &mut xev) };

                if res == 0 {
                    break
                }
            }

            match xev.type_ {
                ffi::KeymapNotify => {
                    unsafe { ffi::XRefreshKeyboardMapping(&xev) }
                },

                ffi::ClientMessage => {
                    use Closed;
                    use std::sync::atomics::Relaxed;

                    let client_msg: &ffi::XClientMessageEvent = unsafe { mem::transmute(&xev) };

                    if client_msg.l[0] == self.wm_delete_window as libc::c_long {
                        self.is_closed.store(true, Relaxed);
                        events.push(Closed);
                    }
                },

                ffi::ResizeRequest => {
                    use Resized;
                    let rs_event: &ffi::XResizeRequestEvent = unsafe { mem::transmute(&xev) };
                    events.push(Resized(rs_event.width as uint, rs_event.height as uint));
                },

                ffi::MotionNotify => {
                    use CursorPositionChanged;
                    let event: &ffi::XMotionEvent = unsafe { mem::transmute(&xev) };
                    events.push(CursorPositionChanged(event.x as uint, event.y as uint));
                },

                ffi::KeyPress | ffi::KeyRelease => {
                    use {Pressed, Released, ReceivedCharacter};
                    let event: &mut ffi::XKeyEvent = unsafe { mem::transmute(&xev) };

                    if event.type_ == ffi::KeyPress {
                        let raw_ev: *mut ffi::XKeyEvent = event;
                        unsafe { ffi::XFilterEvent(mem::transmute(raw_ev), self.window) };
                    }

                    let keysym = unsafe { ffi::XKeycodeToKeysym(self.display, event.keycode as ffi::KeyCode, 0) };

                    let written = unsafe {
                        use std::str;

                        let mut buffer: [u8, ..16] = [mem::uninitialized(), ..16];
                        let raw_ev: *mut ffi::XKeyEvent = event;
                        let count = ffi::Xutf8LookupString(self.ic, mem::transmute(raw_ev),
                            mem::transmute(buffer.as_mut_ptr()),
                            buffer.len() as libc::c_int, ptr::mut_null(), ptr::mut_null());

                        str::from_utf8(buffer.as_slice().slice_to(count as uint)).unwrap_or("").to_string()
                    };

                    for chr in written.as_slice().chars() {
                        events.push(ReceivedCharacter(chr));
                    }

                    match events::keycode_to_element(keysym as libc::c_uint) {
                        Some(elem) if xev.type_ == ffi::KeyPress => {
                            events.push(Pressed(elem));
                        },
                        Some(elem) if xev.type_ == ffi::KeyRelease => {
                            events.push(Released(elem));
                        },
                        _ => ()
                    }
                    //
                },

                ffi::ButtonPress | ffi::ButtonRelease => {
                    use {Pressed, Released};
                    use events;
                    let event: &ffi::XButtonEvent = unsafe { mem::transmute(&xev) };

                    let elem = match event.button {
                        ffi::Button1 => Some(events::Button1),
                        ffi::Button2 => Some(events::Button2),
                        ffi::Button3 => Some(events::Button3),
                        ffi::Button4 => Some(events::Button4),
                        ffi::Button5 => Some(events::Button5),
                        _ => None
                    };

                    if elem.is_some() {
                        let elem = elem.unwrap();

                        if xev.type_ == ffi::ButtonPress {
                            events.push(Pressed(elem));
                        } else if xev.type_ == ffi::ButtonRelease {
                            events.push(Released(elem));
                        }
                    }
                },

                _ => ()
            }
        }

        events
    }

    pub fn wait_events(&self) -> Vec<Event> {
        use std::mem;

        loop {
            // this will block until an event arrives, but doesn't remove
            //  it from the queue
            let mut xev = unsafe { mem::uninitialized() };
            unsafe { ffi::XPeekEvent(self.display, &mut xev) };

            // calling poll_events()
            let ev = self.poll_events();
            if ev.len() >= 1 {
                return ev;
            }
        }
    }

    pub unsafe fn make_current(&self) {
        let res = ffi::glXMakeCurrent(self.display, self.window, self.context);
        if res == 0 {
            fail!("glXMakeCurrent failed");
        }
    }

    pub fn get_proc_address(&self, addr: &str) -> *const () {
        use std::c_str::ToCStr;
        use std::mem;

        unsafe {
            addr.with_c_str(|s| {
                let p = ffi::glXGetProcAddress(mem::transmute(s)) as *const ();
                if !p.is_null() { return p; }
                println!("{}", p);
                p
            })
        }
    }

    pub fn swap_buffers(&self) {
        unsafe { ffi::glXSwapBuffers(self.display, self.window) }
    }
}

impl Drop for Window {
    fn drop(&mut self) {
        unsafe { ffi::glXDestroyContext(self.display, self.context); }
        unsafe { ffi::XDestroyIC(self.ic); }
        unsafe { ffi::XCloseIM(self.im); }
        unsafe { ffi::XDestroyWindow(self.display, self.window); }
        unsafe { ffi::XCloseDisplay(self.display); }
    }
}
<commit_msg>Fix two warnings on X11<commit_after>use {Event, WindowBuilder};
use libc;
use std::{mem, ptr};
use std::sync::atomics::AtomicBool;

mod events;
mod ffi;

pub struct Window {
    display: *mut ffi::Display,
    window: ffi::Window,
    im: ffi::XIM,
    ic: ffi::XIC,
    context: ffi::GLXContext,
    is_closed: AtomicBool,
    wm_delete_window: ffi::Atom,
}

pub struct MonitorID(uint);

pub fn get_available_monitors() -> Vec<MonitorID> {
    unimplemented!()
}

pub fn get_primary_monitor() -> MonitorID {
    unimplemented!()
}

impl MonitorID {
    pub fn get_name(&self) -> Option<String> {
        Some("<Unknown>".to_string())
    }

    pub fn get_dimensions(&self) -> (uint, uint) {
        unimplemented!()
    }
}

impl Window {
    pub fn new(builder: WindowBuilder) -> Result<Window, String> {
        // TODO: temporary
        let dimensions = builder.dimensions;

        // calling XOpenDisplay
        let display = unsafe {
            let display = ffi::XOpenDisplay(ptr::null());
            if display.is_null() {
                return Err(format!("XOpenDisplay failed"));
            }
            display
        };

        // TODO: set error handler?

        // getting the FBConfig
        let fb_config = unsafe {
            static VISUAL_ATTRIBUTES: [libc::c_int, ..23] = [
                ffi::GLX_X_RENDERABLE,  1,
                ffi::GLX_DRAWABLE_TYPE, ffi::GLX_WINDOW_BIT,
                ffi::GLX_RENDER_TYPE,   ffi::GLX_RGBA_BIT,
                ffi::GLX_X_VISUAL_TYPE, ffi::GLX_TRUE_COLOR,
                ffi::GLX_RED_SIZE,      8,
                ffi::GLX_GREEN_SIZE,    8,
                ffi::GLX_BLUE_SIZE,     8,
                ffi::GLX_ALPHA_SIZE,    8,
                ffi::GLX_DEPTH_SIZE,    24,
                ffi::GLX_STENCIL_SIZE,  8,
                ffi::GLX_DOUBLEBUFFER,  1,
                0
            ];

            let mut num_fb: libc::c_int = mem::uninitialized();

            let fb = ffi::glXChooseFBConfig(display, ffi::XDefaultScreen(display),
                VISUAL_ATTRIBUTES.as_ptr(), &mut num_fb);
            if fb.is_null() {
                return Err(format!("glXChooseFBConfig failed"));
            }
            let preferred_fb = *fb;     // TODO: choose more wisely
            ffi::XFree(fb as *const libc::c_void);
            preferred_fb
        };

        // getting the visual infos
        let visual_infos = unsafe {
            let vi = ffi::glXGetVisualFromFBConfig(display, fb_config);
            if vi.is_null() {
                return Err(format!("glXChooseVisual failed"));
            }
            let vi_copy = *vi;
            ffi::XFree(vi as *const libc::c_void);
            vi_copy
        };

        // getting the root window
        let root = unsafe { ffi::XDefaultRootWindow(display) };

        // creating the color map
        let cmap = unsafe {
            let cmap = ffi::XCreateColormap(display, root,
                visual_infos.visual, ffi::AllocNone);
            // TODO: error checking?
            cmap
        };

        // creating
        let mut set_win_attr = {
            let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() };
            swa.colormap = cmap;
            swa.event_mask = ffi::ExposureMask | ffi::ResizeRedirectMask |
                ffi::VisibilityChangeMask | ffi::KeyPressMask | ffi::PointerMotionMask |
                ffi::KeyPressMask | ffi::KeyReleaseMask | ffi::ButtonPressMask |
                ffi::ButtonReleaseMask | ffi::KeymapStateMask;
            swa
        };

        // finally creating the window
        let window = unsafe {
            let dimensions = dimensions.unwrap_or((800, 600));

            let win = ffi::XCreateWindow(display, root, 50, 50, dimensions.val0() as libc::c_uint,
                dimensions.val1() as libc::c_uint, 0, visual_infos.depth, ffi::InputOutput,
                visual_infos.visual, ffi::CWColormap | ffi::CWEventMask,
                &mut set_win_attr);
            win
        };

        // creating window, step 2
        let wm_delete_window = unsafe {
            use std::c_str::ToCStr;

            ffi::XMapWindow(display, window);
            let mut wm_delete_window = ffi::XInternAtom(display,
                "WM_DELETE_WINDOW".to_c_str().as_ptr() as *const libc::c_char, 0);
            ffi::XSetWMProtocols(display, window, &mut wm_delete_window, 1);
            ffi::XStoreName(display, window, mem::transmute(builder.title.as_slice().as_ptr()));
            ffi::XFlush(display);

            wm_delete_window
        };

        // getting the pointer to glXCreateContextAttribs
        let create_context_attribs = unsafe {
            // creating the dummy context
            let dummy_context =
                ffi::glXCreateContext(display, &visual_infos, ptr::null(), 1);
            if dummy_context.is_null() {
                return Err(format!("glXCreateContext failed"));
            }

            if ffi::glXMakeCurrent(display, window, dummy_context) == 0 {
                return Err(format!("glXMakeCurrent with dummy context failed"));
            }

            // getting the pointer
            let fn_ptr = {
                let mut addr = ffi::glXGetProcAddress(b"glXCreateContextAttribs".as_ptr()
                    as *const u8) as *const ();

                if addr.is_null() {
                    addr = ffi::glXGetProcAddress(b"glXCreateContextAttribsARB".as_ptr()
                        as *const u8) as *const ();
                }

                addr.to_option().map(|addr| {
                    let addr: extern "system" fn(*mut ffi::Display, ffi::GLXFBConfig, ffi::GLXContext,
                        ffi::Bool, *const libc::c_int) -> ffi::GLXContext = mem::transmute(addr);
                    addr
                })
            };

            // cleaning up
            ffi::glXMakeCurrent(ptr::mut_null(), 0, ptr::null());
            ffi::glXDestroyContext(display, dummy_context);

            fn_ptr
        };

        // creating IM
        let im = unsafe {
            let im = ffi::XOpenIM(display, ptr::null(), ptr::mut_null(), ptr::mut_null());
            if im.is_null() {
                return Err(format!("XOpenIM failed"));
            }
            im
        };

        // creating input context
        let ic = unsafe {
            use std::c_str::ToCStr;

            let ic = ffi::XCreateIC(im, "inputStyle".to_c_str().as_ptr(),
                ffi::XIMPreeditNothing | ffi::XIMStatusNothing, "clientWindow".to_c_str().as_ptr(),
                window, ptr::null());
            if ic.is_null() {
                return Err(format!("XCreateIC failed"));
            }
            ffi::XSetICFocus(ic);
            ic
        };

        // creating GL context
        let context = unsafe {
            let mut attributes = Vec::new();

            if builder.gl_version.is_some() {
                let version = builder.gl_version.as_ref().unwrap();
                attributes.push(ffi::GLX_CONTEXT_MAJOR_VERSION);
                attributes.push(version.val0() as libc::c_int);
                attributes.push(ffi::GLX_CONTEXT_MINOR_VERSION);
                attributes.push(version.val1() as libc::c_int);
            }

            attributes.push(0);

            let context = if create_context_attribs.is_some() {
                let create_context_attribs = create_context_attribs.unwrap();
                create_context_attribs(display, fb_config, ptr::null(), 1,
                    attributes.as_ptr())
            } else {
                ffi::glXCreateNewContext(display, fb_config, ffi::GLX_RGBA_TYPE, ptr::null(), 1)
            };

            if context.is_null() {
                return Err(format!("GL context creation failed"));
            }

            context
        };

        // returning
        Ok(Window{
            display: display,
            window: window,
            im: im,
            ic: ic,
            context: context,
            is_closed: AtomicBool::new(false),
            wm_delete_window: wm_delete_window,
        })
    }

    pub fn is_closed(&self) -> bool {
        use std::sync::atomics::Relaxed;
        self.is_closed.load(Relaxed)
    }

    pub fn set_title(&self, title: &str) {
        unsafe {
            ffi::XStoreName(self.display, self.window,
                mem::transmute(title.as_slice().as_ptr()));
        }
    }

    fn get_geometry(&self) -> Option<(int, int, uint, uint)> {
        unsafe {
            use std::mem;

            let mut root: ffi::Window = mem::uninitialized();
            let mut x: libc::c_int = mem::uninitialized();
            let mut y: libc::c_int = mem::uninitialized();
            let mut width: libc::c_uint = mem::uninitialized();
            let mut height: libc::c_uint = mem::uninitialized();
            let mut border: libc::c_uint = mem::uninitialized();
            let mut depth: libc::c_uint = mem::uninitialized();

            if ffi::XGetGeometry(self.display, self.window,
                &mut root, &mut x, &mut y, &mut width, &mut height,
                &mut border, &mut depth) == 0
            {
                return None;
            }

            Some((x as int, y as int, width as uint, height as uint))
        }
    }

    pub fn get_position(&self) -> Option<(int, int)> {
        self.get_geometry().map(|(x, y, _, _)| (x, y))
    }

    pub fn set_position(&self, x: uint, y: uint) {
        unsafe { ffi::XMoveWindow(self.display, self.window, x as libc::c_int, y as libc::c_int) }
    }

    pub fn get_inner_size(&self) -> Option<(uint, uint)> {
        self.get_geometry().map(|(_, _, w, h)| (w, h))
    }

    pub fn get_outer_size(&self) -> Option<(uint, uint)> {
        unimplemented!()
    }

    pub fn set_inner_size(&self, x: uint, y: uint) {
        unimplemented!()
    }

    pub fn poll_events(&self) -> Vec<Event> {
        use std::mem;

        let mut events = Vec::new();

        loop {
            use std::num::Bounded;

            let mut xev = unsafe { mem::uninitialized() };
            let res = unsafe { ffi::XCheckMaskEvent(self.display, Bounded::max_value(), &mut xev) };

            if res == 0 {
                let res = unsafe { ffi::XCheckTypedEvent(self.display, ffi::ClientMessage, &mut xev) };

                if res == 0 {
                    break
                }
            }

            match xev.type_ {
                ffi::KeymapNotify => {
                    unsafe { ffi::XRefreshKeyboardMapping(&xev) }
                },

                ffi::ClientMessage => {
                    use Closed;
                    use std::sync::atomics::Relaxed;

                    let client_msg: &ffi::XClientMessageEvent = unsafe { mem::transmute(&xev) };

                    if client_msg.l[0] == self.wm_delete_window as libc::c_long {
                        self.is_closed.store(true, Relaxed);
                        events.push(Closed);
                    }
                },

                ffi::ResizeRequest => {
                    use Resized;
                    let rs_event: &ffi::XResizeRequestEvent = unsafe { mem::transmute(&xev) };
                    events.push(Resized(rs_event.width as uint, rs_event.height as uint));
                },

                ffi::MotionNotify => {
                    use CursorPositionChanged;
                    let event: &ffi::XMotionEvent = unsafe { mem::transmute(&xev) };
                    events.push(CursorPositionChanged(event.x as uint, event.y as uint));
                },

                ffi::KeyPress | ffi::KeyRelease => {
                    use {Pressed, Released, ReceivedCharacter};
                    let event: &mut ffi::XKeyEvent = unsafe { mem::transmute(&xev) };

                    if event.type_ == ffi::KeyPress {
                        let raw_ev: *mut ffi::XKeyEvent = event;
                        unsafe { ffi::XFilterEvent(mem::transmute(raw_ev), self.window) };
                    }

                    let keysym = unsafe { ffi::XKeycodeToKeysym(self.display, event.keycode as ffi::KeyCode, 0) };

                    let written = unsafe {
                        use std::str;

                        let mut buffer: [u8, ..16] = [mem::uninitialized(), ..16];
                        let raw_ev: *mut ffi::XKeyEvent = event;
                        let count = ffi::Xutf8LookupString(self.ic, mem::transmute(raw_ev),
                            mem::transmute(buffer.as_mut_ptr()),
                            buffer.len() as libc::c_int, ptr::mut_null(), ptr::mut_null());

                        str::from_utf8(buffer.as_slice().slice_to(count as uint)).unwrap_or("").to_string()
                    };

                    for chr in written.as_slice().chars() {
                        events.push(ReceivedCharacter(chr));
                    }

                    match events::keycode_to_element(keysym as libc::c_uint) {
                        Some(elem) if xev.type_ == ffi::KeyPress => {
                            events.push(Pressed(elem));
                        },
                        Some(elem) if xev.type_ == ffi::KeyRelease => {
                            events.push(Released(elem));
                        },
                        _ => ()
                    }
                    //
                },

                ffi::ButtonPress | ffi::ButtonRelease => {
                    use {Pressed, Released};
                    use events;
                    let event: &ffi::XButtonEvent = unsafe { mem::transmute(&xev) };

                    let elem = match event.button {
                        ffi::Button1 => Some(events::Button1),
                        ffi::Button2 => Some(events::Button2),
                        ffi::Button3 => Some(events::Button3),
                        ffi::Button4 => Some(events::Button4),
                        ffi::Button5 => Some(events::Button5),
                        _ => None
                    };

                    if elem.is_some() {
                        let elem = elem.unwrap();

                        if xev.type_ == ffi::ButtonPress {
                            events.push(Pressed(elem));
                        } else if xev.type_ == ffi::ButtonRelease {
                            events.push(Released(elem));
                        }
                    }
                },

                _ => ()
            }
        }

        events
    }

    pub fn wait_events(&self) -> Vec<Event> {
        use std::mem;

        loop {
            // this will block until an event arrives, but doesn't remove
            //  it from the queue
            let mut xev = unsafe { mem::uninitialized() };
            unsafe { ffi::XPeekEvent(self.display, &mut xev) };

            // calling poll_events()
            let ev = self.poll_events();
            if ev.len() >= 1 {
                return ev;
            }
        }
    }

    pub unsafe fn make_current(&self) {
        let res = ffi::glXMakeCurrent(self.display, self.window, self.context);
        if res == 0 {
            fail!("glXMakeCurrent failed");
        }
    }

    pub fn get_proc_address(&self, addr: &str) -> *const () {
        use std::c_str::ToCStr;
        use std::mem;

        unsafe {
            addr.with_c_str(|s| {
                let p = ffi::glXGetProcAddress(mem::transmute(s)) as *const ();
                if !p.is_null() { return p; }
                println!("{}", p);
                p
            })
        }
    }

    pub fn swap_buffers(&self) {
        unsafe { ffi::glXSwapBuffers(self.display, self.window) }
    }
}

impl Drop for Window {
    fn drop(&mut self) {
        unsafe { ffi::glXDestroyContext(self.display, self.context); }
        unsafe { ffi::XDestroyIC(self.ic); }
        unsafe { ffi::XCloseIM(self.im); }
        unsafe { ffi::XDestroyWindow(self.display, self.window); }
        unsafe { ffi::XCloseDisplay(self.display); }
    }
}
<|endoftext|>


<commit_before>use ::odata::Parameters;

/// Item path
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Path {
	/// The Home folder
	Home,
	/// The Parent favorite item
	Favorites,
	/// The Parent Shared Folders items
	AllShared,
	/// The Parent Connectors item
	Connectors,
	/// The FileBox item
	Box,
	/// The Top item
	Top,
	/// The item identified by ID
	Id(String),
	/// The absolute path
	Absolute(String),
	/// The relative path
	Relative(String, String),
	/// Parent
	Parent(String),
}

impl Path {
	/// Test if path points to the home folder
	pub fn is_home(&self) -> bool {
		match self {
			&Path::Home => true,
			_ => false
		}
	}

	/// Test if path points to the favorites folder
	pub fn is_favorites(&self) -> bool {
		match self {
			&Path::Favorites => true,
			_ => false
		}
	}

	/// Test if path points to the all shared folder
	pub fn is_allshared(&self) -> bool {
		match self {
			&Path::AllShared => true,
			_ => false
		}
	}

	/// Test if path points to the connectors folder
	pub fn is_connectors(&self) -> bool {
		match self {
			&Path::Connectors => true,
			_ => false
		}
	}

	/// Test if path points to the filebox folder
	pub fn is_box(&self) -> bool {
		match self {
			&Path::Box => true,
			_ => false
		}
	}

	/// Test if path points to the top folder
	pub fn is_top(&self) -> bool {
		match self {
			&Path::Top => true,
			_ => false
		}
	}

	/// Test if path holds the ID
	pub fn is_id(&self) -> bool {
		match self {
			&Path::Id(_) => true,
			_ => false
		}
	}

	/// Test if path is absolute
	pub fn is_absolute(&self) -> bool {
		match self {
			&Path::Absolute(_) => true,
			_ => false
		}
	}

	/// Test if path is relative
	pub fn is_relative(&self) -> bool {
		match self {
			&Path::Relative(_, _) => true,
			_ => false
		}
	}

	/// Test if path points to the parent item
	pub fn is_parent(&self) -> bool {
		match self {
			&Path::Parent(_) => true,
			_ => false
		}
	}

	/// Used internally to build OAuth URIs.
	pub fn entity_and_parameters(&self, segment: Option<&str>, parameters: Option<Parameters>) -> String {
		let segment = match segment {
			Some(segment) => segment,
			None => ""
		};

		let parameters: String = match parameters {
			Some(opts) => opts.to_string(),
			None => "".to_owned()
		};

		match self {
			&Path::Home => format!("Items(home){}?{}", segment, parameters),
			&Path::Favorites => format!("Items(favorites){}?{}", segment, parameters),
			&Path::AllShared => format!("Items(allshared){}?{}", segment, parameters),
			&Path::Connectors => format!("Items(connectors){}?{}", segment, parameters),
			&Path::Box => format!("Items(box){}?{}", segment, parameters),
			&Path::Top => format!("Items(top){}?{}", segment, parameters),
			&Path::Id(ref id) => format!("Items({}){}?{}", id, segment, parameters),
			&Path::Absolute(ref path) => format!("Items/ByPath?path={}&{}", path, parameters),
			&Path::Relative(ref id, ref path) => format!("Items({})/ByPath?path={}&{}", id, path, parameters),
			&Path::Parent(ref id) => format!("Items({})/Parent&{}", id, parameters),
		}
	}
}

#[cfg(test)]
mod tests {
	use super::Path;

	#[test]
	fn path_home() {
		let path = Path::Home;
		assert!(path.is_home());
	}

	#[test]
	fn path_favorites() {
		let path = Path::Favorites;
		assert!(path.is_favorites());
	}

	#[test]
	fn path_allshared() {
		let path = Path::AllShared;
		assert!(path.is_allshared());
	}

	#[test]
	fn path_connectors() {
		let path = Path::Connectors;
		assert!(path.is_connectors());
	}

	#[test]
	fn path_box() {
		let path = Path::Box;
		assert!(path.is_box());
	}

	#[test]
	fn path_top() {
		let path = Path::Top;
		assert!(path.is_top());
	}

	#[test]
	fn path_id() {
		let path = Path::Id("id".to_owned());
		assert!(path.is_id());
	}

	#[test]
	fn path_absolute() {
		let path = Path::Absolute("path".to_owned());
		assert!(path.is_absolute());
	}

	#[test]
	fn path_relative() {
		let path = Path::Relative("this".to_owned(), "that".to_owned());
		assert!(path.is_relative());
	}

	#[test]
	fn path_parent() {
		let path = Path::Parent("up".to_owned());
		assert!(path.is_parent());
	}
}
<commit_msg>Parse path from JSON<commit_after>use serde_json::Value;
use ::{Error, Result};
use ::odata::Parameters;

/// Item path
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Path {
	/// The Home folder
	Home,
	/// The Parent favorite item
	Favorites,
	/// The Parent Shared Folders items
	AllShared,
	/// The Parent Connectors item
	Connectors,
	/// The FileBox item
	Box,
	/// The Top item
	Top,
	/// The item identified by ID
	Id(String),
	/// The absolute path
	Absolute(String),
	/// The relative path
	Relative(String, String),
	/// Parent
	Parent(String),
}

impl Path {
	/// Test if path points to the home folder
	pub fn is_home(&self) -> bool {
		match self {
			&Path::Home => true,
			_ => false
		}
	}

	/// Test if path points to the favorites folder
	pub fn is_favorites(&self) -> bool {
		match self {
			&Path::Favorites => true,
			_ => false
		}
	}

	/// Test if path points to the all shared folder
	pub fn is_allshared(&self) -> bool {
		match self {
			&Path::AllShared => true,
			_ => false
		}
	}

	/// Test if path points to the connectors folder
	pub fn is_connectors(&self) -> bool {
		match self {
			&Path::Connectors => true,
			_ => false
		}
	}

	/// Test if path points to the filebox folder
	pub fn is_box(&self) -> bool {
		match self {
			&Path::Box => true,
			_ => false
		}
	}

	/// Test if path points to the top folder
	pub fn is_top(&self) -> bool {
		match self {
			&Path::Top => true,
			_ => false
		}
	}

	/// Test if path holds the ID
	pub fn is_id(&self) -> bool {
		match self {
			&Path::Id(_) => true,
			_ => false
		}
	}

	/// Test if path is absolute
	pub fn is_absolute(&self) -> bool {
		match self {
			&Path::Absolute(_) => true,
			_ => false
		}
	}

	/// Test if path is relative
	pub fn is_relative(&self) -> bool {
		match self {
			&Path::Relative(_, _) => true,
			_ => false
		}
	}

	/// Test if path points to the parent item
	pub fn is_parent(&self) -> bool {
		match self {
			&Path::Parent(_) => true,
			_ => false
		}
	}

	/// Used internally to build OAuth URIs.
	pub fn entity_and_parameters(&self, segment: Option<&str>, parameters: Option<Parameters>) -> String {
		let segment = segment.map_or("", |s| s);
		let parameters = parameters.map_or(String::from(""), |p| p.to_string());

		match self {
			&Path::Home => format!("Items(home){}?{}", segment, parameters),
			&Path::Favorites => format!("Items(favorites){}?{}", segment, parameters),
			&Path::AllShared => format!("Items(allshared){}?{}", segment, parameters),
			&Path::Connectors => format!("Items(connectors){}?{}", segment, parameters),
			&Path::Box => format!("Items(box){}?{}", segment, parameters),
			&Path::Top => format!("Items(top){}?{}", segment, parameters),
			&Path::Id(ref id) => format!("Items({}){}?{}", id, segment, parameters),
			&Path::Absolute(ref path) => format!("Items/ByPath?path={}&{}", path, parameters),
			&Path::Relative(ref id, ref path) => format!("Items({})/ByPath?path={}&{}", id, path, parameters),
			&Path::Parent(ref id) => format!("Items({})/Parent&{}", id, parameters),
		}
	}

	// Parse the path from the result JSON
	pub fn from_json(value: Value) -> Result<Path> {
		value.find("Id")
			.ok_or(Error::new("Cannot find Item ID"))
			.map(|id| Path::Id(id.as_string().unwrap().to_owned()))
	}
}

#[cfg(test)]
mod tests {
	use super::Path;

	#[test]
	fn path_home() {
		let path = Path::Home;
		assert!(path.is_home());
	}

	#[test]
	fn path_favorites() {
		let path = Path::Favorites;
		assert!(path.is_favorites());
	}

	#[test]
	fn path_allshared() {
		let path = Path::AllShared;
		assert!(path.is_allshared());
	}

	#[test]
	fn path_connectors() {
		let path = Path::Connectors;
		assert!(path.is_connectors());
	}

	#[test]
	fn path_box() {
		let path = Path::Box;
		assert!(path.is_box());
	}

	#[test]
	fn path_top() {
		let path = Path::Top;
		assert!(path.is_top());
	}

	#[test]
	fn path_id() {
		let path = Path::Id("id".to_owned());
		assert!(path.is_id());
	}

	#[test]
	fn path_absolute() {
		let path = Path::Absolute("path".to_owned());
		assert!(path.is_absolute());
	}

	#[test]
	fn path_relative() {
		let path = Path::Relative("this".to_owned(), "that".to_owned());
		assert!(path.is_relative());
	}

	#[test]
	fn path_parent() {
		let path = Path::Parent("up".to_owned());
		assert!(path.is_parent());
	}
}
<|endoftext|>


<commit_before>// These `thumbv*` targets cover the ARM Cortex-M family of processors which are widely used in
// microcontrollers. Namely, all these processors:
//
// - Cortex-M0
// - Cortex-M0+
// - Cortex-M1
// - Cortex-M3
// - Cortex-M4(F)
// - Cortex-M7(F)
// - Cortex-M23
// - Cortex-M33
//
// We have opted for these instead of one target per processor (e.g., `cortex-m0`, `cortex-m3`,
// etc) because the differences between some processors like the cortex-m0 and cortex-m1 are almost
// non-existent from the POV of codegen so it doesn't make sense to have separate targets for them.
// And if differences exist between two processors under the same target, rustc flags can be used to
// optimize for one processor or the other.
//
// Also, we have not chosen a single target (`arm-none-eabi`) like GCC does because this makes
// difficult to integrate Rust code and C code. Targeting the Cortex-M4 requires different gcc flags
// than the ones you would use for the Cortex-M0 and with a single target it'd be impossible to
// differentiate one processor from the other.
//
// About arm vs thumb in the name. The Cortex-M devices only support the Thumb instruction set,
// which is more compact (higher code density), and not the ARM instruction set. That's why LLVM
// triples use thumb instead of arm. We follow suit because having thumb in the name let us
// differentiate these targets from our other `arm(v7)-*-*-gnueabi(hf)` targets in the context of
// build scripts / gcc flags.

use crate::spec::{PanicStrategy, TargetOptions};
use std::default::Default;

pub fn opts() -> TargetOptions {
    // See rust-lang/rfcs#1645 for a discussion about these defaults
    TargetOptions {
        executables: true,
        // In most cases, LLD is good enough
        linker: Some("rust-lld".to_string()),
        // Because these devices have very little resources having an unwinder is too onerous so we
        // default to "abort" because the "unwind" strategy is very rare.
        panic_strategy: PanicStrategy::Abort,
        // Similarly, one almost always never wants to use relocatable code because of the extra
        // costs it involves.
        relocation_model: "static".to_string(),
        abi_blacklist: super::arm_base::abi_blacklist(),
        // When this section is added a volatile load to its start address is also generated. This
        // volatile load is a footgun as it can end up loading an invalid memory address, depending
        // on how the user set up their linker scripts. This section adds pretty printer for stuff
        // like std::Vec, which is not that used in no-std context, so it's best to left it out
        // until we figure a way to add the pretty printers without requiring a volatile load cf.
        // rust-lang/rust#44993.
        emit_debug_gdb_scripts: false,
        // LLVM is eager to trash the link register when calling `nounwind` functions, which
        // breaks debugging. Preserve LR by default to prevent that from happening.
        eliminate_frame_pointer: false,
        ..Default::default()
    }
}
<commit_msg>Fix typo<commit_after>// These `thumbv*` targets cover the ARM Cortex-M family of processors which are widely used in
// microcontrollers. Namely, all these processors:
//
// - Cortex-M0
// - Cortex-M0+
// - Cortex-M1
// - Cortex-M3
// - Cortex-M4(F)
// - Cortex-M7(F)
// - Cortex-M23
// - Cortex-M33
//
// We have opted for these instead of one target per processor (e.g., `cortex-m0`, `cortex-m3`,
// etc) because the differences between some processors like the cortex-m0 and cortex-m1 are almost
// non-existent from the POV of codegen so it doesn't make sense to have separate targets for them.
// And if differences exist between two processors under the same target, rustc flags can be used to
// optimize for one processor or the other.
//
// Also, we have not chosen a single target (`arm-none-eabi`) like GCC does because this makes
// difficult to integrate Rust code and C code. Targeting the Cortex-M4 requires different gcc flags
// than the ones you would use for the Cortex-M0 and with a single target it'd be impossible to
// differentiate one processor from the other.
//
// About arm vs thumb in the name. The Cortex-M devices only support the Thumb instruction set,
// which is more compact (higher code density), and not the ARM instruction set. That's why LLVM
// triples use thumb instead of arm. We follow suit because having thumb in the name let us
// differentiate these targets from our other `arm(v7)-*-*-gnueabi(hf)` targets in the context of
// build scripts / gcc flags.

use crate::spec::{PanicStrategy, TargetOptions};
use std::default::Default;

pub fn opts() -> TargetOptions {
    // See rust-lang/rfcs#1645 for a discussion about these defaults
    TargetOptions {
        executables: true,
        // In most cases, LLD is good enough
        linker: Some("rust-lld".to_string()),
        // Because these devices have very little resources having an unwinder is too onerous so we
        // default to "abort" because the "unwind" strategy is very rare.
        panic_strategy: PanicStrategy::Abort,
        // Similarly, one almost always never wants to use relocatable code because of the extra
        // costs it involves.
        relocation_model: "static".to_string(),
        abi_blacklist: super::arm_base::abi_blacklist(),
        // When this section is added a volatile load to its start address is also generated. This
        // volatile load is a footgun as it can end up loading an invalid memory address, depending
        // on how the user set up their linker scripts. This section adds pretty printer for stuff
        // like std::Vec, which is not that used in no-std context, so it's best to left it out
        // until we figure a way to add the pretty printers without requiring a volatile load cf.
        // rust-lang/rust#44993.
        emit_debug_gdb_scripts: false,
        // LLVM is eager to trash the link register when calling `noreturn` functions, which
        // breaks debugging. Preserve LR by default to prevent that from happening.
        eliminate_frame_pointer: false,
        ..Default::default()
    }
}
<|endoftext|>


<commit_before>//! An asynchronous, pipelined, PostgreSQL client.
//!
//! # Example
//!
//! ```no_run
//! use tokio_postgres::{NoTls, Error};
//!
//! # #[cfg(not(feature = "runtime"))] fn main() {}
//! # #[cfg(feature = "runtime")]
//! #[tokio::main] // By default, tokio_postgres uses the tokio crate as its runtime.
//! async fn main() -> Result<(), Error> {
//!     // Connect to the database.
//!     let (client, connection) =
//!         tokio_postgres::connect("host=localhost user=postgres", NoTls).await?;
//!
//!     // The connection object performs the actual communication with the database,
//!     // so spawn it off to run on its own.
//!     tokio::spawn(async move {
//!         if let Err(e) = connection.await {
//!             eprintln!("connection error: {}", e);
//!         }
//!     });
//!
//!     // Now we can execute a simple statement that just returns its parameter.
//!     let rows = client
//!         .query("SELECT $1::TEXT", &[&"hello world"])
//!         .await?;
//!
//!     // And then check that we got back the same string we sent over.
//!     let value: &str = rows[0].get(0);
//!     assert_eq!(value, "hello world");
//!
//!     Ok(())
//! }
//! ```
//!
//! # Behavior
//!
//! Calling a method like `Client::query` on its own does nothing. The associated request is not sent to the database
//! until the future returned by the method is first polled. Requests are executed in the order that they are first
//! polled, not in the order that their futures are created.
//!
//! # Pipelining
//!
//! The client supports *pipelined* requests. Pipelining can improve performance in use cases in which multiple,
//! independent queries need to be executed. In a traditional workflow, each query is sent to the server after the
//! previous query completes. In contrast, pipelining allows the client to send all of the queries to the server up
//! front, minimizing time spent by one side waiting for the other to finish sending data:
//!
//! ```not_rust
//!             Sequential                              Pipelined
//! | Client         | Server          |    | Client         | Server          |
//! |----------------|-----------------|    |----------------|-----------------|
//! | send query 1   |                 |    | send query 1   |                 |
//! |                | process query 1 |    | send query 2   | process query 1 |
//! | receive rows 1 |                 |    | send query 3   | process query 2 |
//! | send query 2   |                 |    | receive rows 1 | process query 3 |
//! |                | process query 2 |    | receive rows 2 |                 |
//! | receive rows 2 |                 |    | receive rows 3 |                 |
//! | send query 3   |                 |
//! |                | process query 3 |
//! | receive rows 3 |                 |
//! ```
//!
//! In both cases, the PostgreSQL server is executing the queries sequentially - pipelining just allows both sides of
//! the connection to work concurrently when possible.
//!
//! Pipelining happens automatically when futures are polled concurrently (for example, by using the futures `join`
//! combinator):
//!
//! ```rust
//! use futures::future;
//! use std::future::Future;
//! use tokio_postgres::{Client, Error, Statement};
//!
//! async fn pipelined_prepare(
//!     client: &Client,
//! ) -> Result<(Statement, Statement), Error>
//! {
//!     future::try_join(
//!         client.prepare("SELECT * FROM foo"),
//!         client.prepare("INSERT INTO bar (id, name) VALUES ($1, $2)")
//!     ).await
//! }
//! ```
//!
//! # Runtime
//!
//! The client works with arbitrary `AsyncRead + AsyncWrite` streams. Convenience APIs are provided to handle the
//! connection process, but these are gated by the `runtime` Cargo feature, which is enabled by default. If disabled,
//! all dependence on the tokio runtime is removed.
//!
//! # SSL/TLS support
//!
//! TLS support is implemented via external libraries. `Client::connect` and `Config::connect` take a TLS implementation
//! as an argument. The `NoTls` type in this crate can be used when TLS is not required. Otherwise, the
//! `postgres-openssl` and `postgres-native-tls` crates provide implementations backed by the `openssl` and `native-tls`
//! crates, respectively.
//!
//! # Features
//!
//! The following features can be enabled from `Cargo.toml`:
//!
//! | Feature | Description | Extra dependencies | Default |
//! | ------- | ----------- | ------------------ | ------- |
//! | `runtime` | Enable convenience API for the connection process based on the `tokio` crate. | [tokio](https://crates.io/crates/tokio) 0.2 with the features `dns`, `net` and `time` | yes |
//! | `with-bit-vec-0_6` | Enable support for the `bit-vec` crate. | [bit-vec](https://crates.io/crates/bit-vec) 0.6 | no |
//! | `with-chrono-0_4` | Enable support for the `chrono` crate. | [chrono](https://crates.io/crates/chrono) 0.4 | no |
//! | `with-eui48-0_4` | Enable support for the `eui48` crate. | [eui48](https://crates.io/crates/eui48) 0.4 | no |
//! | `with-geo-types-0_4` | Enable support for the 0.4 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.4.0) 0.4 | no |
//! | `with-geo-types-0_5` | Enable support for the 0.5 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.5.0) 0.5 | no |
//! | `with-serde_json-1` | Enable support for the `serde_json` crate. | [serde_json](https://crates.io/crates/serde_json) 1.0 | no |
//! | `with-uuid-0_8` | Enable support for the `uuid` crate. | [uuid](https://crates.io/crates/uuid) 0.8 | no |
//! | `with-time-0_2` | Enable support for the `time` crate. | [time](https://crates.io/crates/time) 0.2 | no |
#![doc(html_root_url = "https://docs.rs/tokio-postgres/0.5")]
#![warn(rust_2018_idioms, clippy::all, missing_docs)]

pub use crate::cancel_token::CancelToken;
pub use crate::client::Client;
pub use crate::config::Config;
pub use crate::connection::Connection;
pub use crate::copy_in::CopyInSink;
pub use crate::copy_out::CopyOutStream;
use crate::error::DbError;
pub use crate::error::Error;
pub use crate::generic_client::GenericClient;
pub use crate::portal::Portal;
pub use crate::query::RowStream;
pub use crate::row::{Row, SimpleQueryRow};
pub use crate::simple_query::SimpleQueryStream;
#[cfg(feature = "runtime")]
pub use crate::socket::Socket;
pub use crate::statement::{Column, Statement};
#[cfg(feature = "runtime")]
use crate::tls::MakeTlsConnect;
pub use crate::tls::NoTls;
pub use crate::to_statement::ToStatement;
pub use crate::transaction::Transaction;
pub use crate::transaction_builder::{IsolationLevel, TransactionBuilder};
use crate::types::ToSql;

pub mod binary_copy;
mod bind;
#[cfg(feature = "runtime")]
mod cancel_query;
mod cancel_query_raw;
mod cancel_token;
mod client;
mod codec;
pub mod config;
#[cfg(feature = "runtime")]
mod connect;
mod connect_raw;
#[cfg(feature = "runtime")]
mod connect_socket;
mod connect_tls;
mod connection;
mod copy_in;
mod copy_out;
pub mod error;
mod generic_client;
mod maybe_tls_stream;
mod portal;
mod prepare;
mod query;
pub mod row;
mod simple_query;
#[cfg(feature = "runtime")]
mod socket;
mod statement;
pub mod tls;
mod to_statement;
mod transaction;
mod transaction_builder;
pub mod types;

/// A convenience function which parses a connection string and connects to the database.
///
/// See the documentation for [`Config`] for details on the connection string format.
///
/// Requires the `runtime` Cargo feature (enabled by default).
///
/// [`Config`]: config/struct.Config.html
#[cfg(feature = "runtime")]
pub async fn connect<T>(
    config: &str,
    tls: T,
) -> Result<(Client, Connection<Socket, T::Stream>), Error>
where
    T: MakeTlsConnect<Socket>,
{
    let config = config.parse::<Config>()?;
    config.connect(tls).await
}

/// An asynchronous notification.
#[derive(Clone, Debug)]
pub struct Notification {
    process_id: i32,
    channel: String,
    payload: String,
}

impl Notification {
    /// The process ID of the notifying backend process.
    pub fn process_id(&self) -> i32 {
        self.process_id
    }

    /// The name of the channel that the notify has been raised on.
    pub fn channel(&self) -> &str {
        &self.channel
    }

    /// The "payload" string passed from the notifying process.
    pub fn payload(&self) -> &str {
        &self.payload
    }
}

/// An asynchronous message from the server.
#[allow(clippy::large_enum_variant)]
#[non_exhaustive]
pub enum AsyncMessage {
    /// A notice.
    ///
    /// Notices use the same format as errors, but aren't "errors" per-se.
    Notice(DbError),
    /// A notification.
    ///
    /// Connections can subscribe to notifications with the `LISTEN` command.
    Notification(Notification),
}

/// Message returned by the `SimpleQuery` stream.
#[non_exhaustive]
pub enum SimpleQueryMessage {
    /// A row of data.
    Row(SimpleQueryRow),
    /// A statement in the query has completed.
    ///
    /// The number of rows modified or selected is returned.
    CommandComplete(u64),
}

fn slice_iter<'a>(
    s: &'a [&'a (dyn ToSql + Sync)],
) -> impl ExactSizeIterator<Item = &'a dyn ToSql> + 'a {
    s.iter().map(|s| *s as _)
}
<commit_msg>Add Debug and Clone to `AsyncMessage`<commit_after>//! An asynchronous, pipelined, PostgreSQL client.
//!
//! # Example
//!
//! ```no_run
//! use tokio_postgres::{NoTls, Error};
//!
//! # #[cfg(not(feature = "runtime"))] fn main() {}
//! # #[cfg(feature = "runtime")]
//! #[tokio::main] // By default, tokio_postgres uses the tokio crate as its runtime.
//! async fn main() -> Result<(), Error> {
//!     // Connect to the database.
//!     let (client, connection) =
//!         tokio_postgres::connect("host=localhost user=postgres", NoTls).await?;
//!
//!     // The connection object performs the actual communication with the database,
//!     // so spawn it off to run on its own.
//!     tokio::spawn(async move {
//!         if let Err(e) = connection.await {
//!             eprintln!("connection error: {}", e);
//!         }
//!     });
//!
//!     // Now we can execute a simple statement that just returns its parameter.
//!     let rows = client
//!         .query("SELECT $1::TEXT", &[&"hello world"])
//!         .await?;
//!
//!     // And then check that we got back the same string we sent over.
//!     let value: &str = rows[0].get(0);
//!     assert_eq!(value, "hello world");
//!
//!     Ok(())
//! }
//! ```
//!
//! # Behavior
//!
//! Calling a method like `Client::query` on its own does nothing. The associated request is not sent to the database
//! until the future returned by the method is first polled. Requests are executed in the order that they are first
//! polled, not in the order that their futures are created.
//!
//! # Pipelining
//!
//! The client supports *pipelined* requests. Pipelining can improve performance in use cases in which multiple,
//! independent queries need to be executed. In a traditional workflow, each query is sent to the server after the
//! previous query completes. In contrast, pipelining allows the client to send all of the queries to the server up
//! front, minimizing time spent by one side waiting for the other to finish sending data:
//!
//! ```not_rust
//!             Sequential                              Pipelined
//! | Client         | Server          |    | Client         | Server          |
//! |----------------|-----------------|    |----------------|-----------------|
//! | send query 1   |                 |    | send query 1   |                 |
//! |                | process query 1 |    | send query 2   | process query 1 |
//! | receive rows 1 |                 |    | send query 3   | process query 2 |
//! | send query 2   |                 |    | receive rows 1 | process query 3 |
//! |                | process query 2 |    | receive rows 2 |                 |
//! | receive rows 2 |                 |    | receive rows 3 |                 |
//! | send query 3   |                 |
//! |                | process query 3 |
//! | receive rows 3 |                 |
//! ```
//!
//! In both cases, the PostgreSQL server is executing the queries sequentially - pipelining just allows both sides of
//! the connection to work concurrently when possible.
//!
//! Pipelining happens automatically when futures are polled concurrently (for example, by using the futures `join`
//! combinator):
//!
//! ```rust
//! use futures::future;
//! use std::future::Future;
//! use tokio_postgres::{Client, Error, Statement};
//!
//! async fn pipelined_prepare(
//!     client: &Client,
//! ) -> Result<(Statement, Statement), Error>
//! {
//!     future::try_join(
//!         client.prepare("SELECT * FROM foo"),
//!         client.prepare("INSERT INTO bar (id, name) VALUES ($1, $2)")
//!     ).await
//! }
//! ```
//!
//! # Runtime
//!
//! The client works with arbitrary `AsyncRead + AsyncWrite` streams. Convenience APIs are provided to handle the
//! connection process, but these are gated by the `runtime` Cargo feature, which is enabled by default. If disabled,
//! all dependence on the tokio runtime is removed.
//!
//! # SSL/TLS support
//!
//! TLS support is implemented via external libraries. `Client::connect` and `Config::connect` take a TLS implementation
//! as an argument. The `NoTls` type in this crate can be used when TLS is not required. Otherwise, the
//! `postgres-openssl` and `postgres-native-tls` crates provide implementations backed by the `openssl` and `native-tls`
//! crates, respectively.
//!
//! # Features
//!
//! The following features can be enabled from `Cargo.toml`:
//!
//! | Feature | Description | Extra dependencies | Default |
//! | ------- | ----------- | ------------------ | ------- |
//! | `runtime` | Enable convenience API for the connection process based on the `tokio` crate. | [tokio](https://crates.io/crates/tokio) 0.2 with the features `dns`, `net` and `time` | yes |
//! | `with-bit-vec-0_6` | Enable support for the `bit-vec` crate. | [bit-vec](https://crates.io/crates/bit-vec) 0.6 | no |
//! | `with-chrono-0_4` | Enable support for the `chrono` crate. | [chrono](https://crates.io/crates/chrono) 0.4 | no |
//! | `with-eui48-0_4` | Enable support for the `eui48` crate. | [eui48](https://crates.io/crates/eui48) 0.4 | no |
//! | `with-geo-types-0_4` | Enable support for the 0.4 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.4.0) 0.4 | no |
//! | `with-geo-types-0_5` | Enable support for the 0.5 version of the `geo-types` crate. | [geo-types](https://crates.io/crates/geo-types/0.5.0) 0.5 | no |
//! | `with-serde_json-1` | Enable support for the `serde_json` crate. | [serde_json](https://crates.io/crates/serde_json) 1.0 | no |
//! | `with-uuid-0_8` | Enable support for the `uuid` crate. | [uuid](https://crates.io/crates/uuid) 0.8 | no |
//! | `with-time-0_2` | Enable support for the `time` crate. | [time](https://crates.io/crates/time) 0.2 | no |
#![doc(html_root_url = "https://docs.rs/tokio-postgres/0.5")]
#![warn(rust_2018_idioms, clippy::all, missing_docs)]

pub use crate::cancel_token::CancelToken;
pub use crate::client::Client;
pub use crate::config::Config;
pub use crate::connection::Connection;
pub use crate::copy_in::CopyInSink;
pub use crate::copy_out::CopyOutStream;
use crate::error::DbError;
pub use crate::error::Error;
pub use crate::generic_client::GenericClient;
pub use crate::portal::Portal;
pub use crate::query::RowStream;
pub use crate::row::{Row, SimpleQueryRow};
pub use crate::simple_query::SimpleQueryStream;
#[cfg(feature = "runtime")]
pub use crate::socket::Socket;
pub use crate::statement::{Column, Statement};
#[cfg(feature = "runtime")]
use crate::tls::MakeTlsConnect;
pub use crate::tls::NoTls;
pub use crate::to_statement::ToStatement;
pub use crate::transaction::Transaction;
pub use crate::transaction_builder::{IsolationLevel, TransactionBuilder};
use crate::types::ToSql;

pub mod binary_copy;
mod bind;
#[cfg(feature = "runtime")]
mod cancel_query;
mod cancel_query_raw;
mod cancel_token;
mod client;
mod codec;
pub mod config;
#[cfg(feature = "runtime")]
mod connect;
mod connect_raw;
#[cfg(feature = "runtime")]
mod connect_socket;
mod connect_tls;
mod connection;
mod copy_in;
mod copy_out;
pub mod error;
mod generic_client;
mod maybe_tls_stream;
mod portal;
mod prepare;
mod query;
pub mod row;
mod simple_query;
#[cfg(feature = "runtime")]
mod socket;
mod statement;
pub mod tls;
mod to_statement;
mod transaction;
mod transaction_builder;
pub mod types;

/// A convenience function which parses a connection string and connects to the database.
///
/// See the documentation for [`Config`] for details on the connection string format.
///
/// Requires the `runtime` Cargo feature (enabled by default).
///
/// [`Config`]: config/struct.Config.html
#[cfg(feature = "runtime")]
pub async fn connect<T>(
    config: &str,
    tls: T,
) -> Result<(Client, Connection<Socket, T::Stream>), Error>
where
    T: MakeTlsConnect<Socket>,
{
    let config = config.parse::<Config>()?;
    config.connect(tls).await
}

/// An asynchronous notification.
#[derive(Clone, Debug)]
pub struct Notification {
    process_id: i32,
    channel: String,
    payload: String,
}

impl Notification {
    /// The process ID of the notifying backend process.
    pub fn process_id(&self) -> i32 {
        self.process_id
    }

    /// The name of the channel that the notify has been raised on.
    pub fn channel(&self) -> &str {
        &self.channel
    }

    /// The "payload" string passed from the notifying process.
    pub fn payload(&self) -> &str {
        &self.payload
    }
}

/// An asynchronous message from the server.
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum AsyncMessage {
    /// A notice.
    ///
    /// Notices use the same format as errors, but aren't "errors" per-se.
    Notice(DbError),
    /// A notification.
    ///
    /// Connections can subscribe to notifications with the `LISTEN` command.
    Notification(Notification),
}

/// Message returned by the `SimpleQuery` stream.
#[non_exhaustive]
pub enum SimpleQueryMessage {
    /// A row of data.
    Row(SimpleQueryRow),
    /// A statement in the query has completed.
    ///
    /// The number of rows modified or selected is returned.
    CommandComplete(u64),
}

fn slice_iter<'a>(
    s: &'a [&'a (dyn ToSql + Sync)],
) -> impl ExactSizeIterator<Item = &'a dyn ToSql> + 'a {
    s.iter().map(|s| *s as _)
}
<|endoftext|>


<commit_before>use crate::sync::watch;

use std::sync::Mutex;

/// A barrier enables multiple threads to synchronize the beginning of some computation.
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::sync::Barrier;
/// use std::sync::Arc;
///
/// let mut handles = Vec::with_capacity(10);
/// let barrier = Arc::new(Barrier::new(10));
/// for _ in 0..10 {
///     let c = barrier.clone();
///     // The same messages will be printed together.
///     // You will NOT see any interleaving.
///     handles.push(tokio::spawn(async move {
///         println!("before wait");
///         let wait_result = c.wait().await;
///         println!("after wait");
///         wait_result
///     }));
/// }
///
/// // Will not resolve until all "after wait" messages have been printed
/// let mut num_leaders = 0;
/// for handle in handles {
///     let wait_result = handle.await.unwrap();
///     if wait_result.is_leader() {
///         num_leaders += 1;
///     }
/// }
///
/// // Exactly one barrier will resolve as the "leader"
/// assert_eq!(num_leaders, 1);
/// # }
/// ```
#[derive(Debug)]
pub struct Barrier {
    state: Mutex<BarrierState>,
    wait: watch::Receiver<usize>,
    n: usize,
}

#[derive(Debug)]
struct BarrierState {
    waker: watch::Sender<usize>,
    arrived: usize,
    generation: usize,
}

impl Barrier {
    /// Creates a new barrier that can block a given number of threads.
    ///
    /// A barrier will block `n`-1 threads which call [`Barrier::wait`] and then wake up all
    /// threads at once when the `n`th thread calls `wait`.
    pub fn new(mut n: usize) -> Barrier {
        let (waker, wait) = crate::sync::watch::channel(0);

        if n == 0 {
            // if n is 0, it's not clear what behavior the user wants.
            // in std::sync::Barrier, an n of 0 exhibits the same behavior as n == 1, where every
            // .wait() immediately unblocks, so we adopt that here as well.
            n = 1;
        }

        Barrier {
            state: Mutex::new(BarrierState {
                waker,
                arrived: 0,
                generation: 1,
            }),
            n,
            wait,
        }
    }

    /// Does not resolve until all tasks have rendezvoused here.
    ///
    /// Barriers are re-usable after all threads have rendezvoused once, and can
    /// be used continuously.
    ///
    /// A single (arbitrary) future will receive a [`BarrierWaitResult`] that returns `true` from
    /// [`BarrierWaitResult::is_leader`] when returning from this function, and all other threads
    /// will receive a result that will return `false` from `is_leader`.
    pub async fn wait(&self) -> BarrierWaitResult {
        // NOTE: we are taking a _synchronous_ lock here.
        // It is okay to do so because the critical section is fast and never yields, so it cannot
        // deadlock even if another future is concurrently holding the lock.
        // It is _desireable_ to do so as synchronous Mutexes are, at least in theory, faster than
        // the asynchronous counter-parts, so we should use them where possible [citation needed].
        // NOTE: the extra scope here is so that the compiler doesn't think `state` is held across
        // a yield point, and thus marks the returned future as !Send.
        let generation = {
            let mut state = self.state.lock().unwrap();
            let generation = state.generation;
            state.arrived += 1;
            if state.arrived == self.n {
                // we are the leader for this generation
                // wake everyone, increment the generation, and return
                state
                    .waker
                    .send(state.generation)
                    .expect("there is at least one receiver");
                state.arrived = 0;
                state.generation += 1;
                return BarrierWaitResult(true);
            }

            generation
        };

        // we're going to have to wait for the last of the generation to arrive
        let mut wait = self.wait.clone();

        loop {
            let _ = wait.changed().await;

            // note that the first time through the loop, this _will_ yield a generation
            // immediately, since we cloned a receiver that has never seen any values.
            if *wait.borrow() >= generation {
                break;
            }
        }

        BarrierWaitResult(false)
    }
}

/// A `BarrierWaitResult` is returned by `wait` when all threads in the `Barrier` have rendezvoused.
#[derive(Debug, Clone)]
pub struct BarrierWaitResult(bool);

impl BarrierWaitResult {
    /// Returns `true` if this thread from wait is the "leader thread".
    ///
    /// Only one thread will have `true` returned from their result, all other threads will have
    /// `false` returned.
    pub fn is_leader(&self) -> bool {
        self.0
    }
}
<commit_msg>sync: Barrier doc should use task, not thread (#3780)<commit_after>use crate::sync::watch;

use std::sync::Mutex;

/// A barrier enables multiple tasks to synchronize the beginning of some computation.
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use tokio::sync::Barrier;
/// use std::sync::Arc;
///
/// let mut handles = Vec::with_capacity(10);
/// let barrier = Arc::new(Barrier::new(10));
/// for _ in 0..10 {
///     let c = barrier.clone();
///     // The same messages will be printed together.
///     // You will NOT see any interleaving.
///     handles.push(tokio::spawn(async move {
///         println!("before wait");
///         let wait_result = c.wait().await;
///         println!("after wait");
///         wait_result
///     }));
/// }
///
/// // Will not resolve until all "after wait" messages have been printed
/// let mut num_leaders = 0;
/// for handle in handles {
///     let wait_result = handle.await.unwrap();
///     if wait_result.is_leader() {
///         num_leaders += 1;
///     }
/// }
///
/// // Exactly one barrier will resolve as the "leader"
/// assert_eq!(num_leaders, 1);
/// # }
/// ```
#[derive(Debug)]
pub struct Barrier {
    state: Mutex<BarrierState>,
    wait: watch::Receiver<usize>,
    n: usize,
}

#[derive(Debug)]
struct BarrierState {
    waker: watch::Sender<usize>,
    arrived: usize,
    generation: usize,
}

impl Barrier {
    /// Creates a new barrier that can block a given number of tasks.
    ///
    /// A barrier will block `n`-1 tasks which call [`Barrier::wait`] and then wake up all
    /// tasks at once when the `n`th task calls `wait`.
    pub fn new(mut n: usize) -> Barrier {
        let (waker, wait) = crate::sync::watch::channel(0);

        if n == 0 {
            // if n is 0, it's not clear what behavior the user wants.
            // in std::sync::Barrier, an n of 0 exhibits the same behavior as n == 1, where every
            // .wait() immediately unblocks, so we adopt that here as well.
            n = 1;
        }

        Barrier {
            state: Mutex::new(BarrierState {
                waker,
                arrived: 0,
                generation: 1,
            }),
            n,
            wait,
        }
    }

    /// Does not resolve until all tasks have rendezvoused here.
    ///
    /// Barriers are re-usable after all tasks have rendezvoused once, and can
    /// be used continuously.
    ///
    /// A single (arbitrary) future will receive a [`BarrierWaitResult`] that returns `true` from
    /// [`BarrierWaitResult::is_leader`] when returning from this function, and all other tasks
    /// will receive a result that will return `false` from `is_leader`.
    pub async fn wait(&self) -> BarrierWaitResult {
        // NOTE: we are taking a _synchronous_ lock here.
        // It is okay to do so because the critical section is fast and never yields, so it cannot
        // deadlock even if another future is concurrently holding the lock.
        // It is _desireable_ to do so as synchronous Mutexes are, at least in theory, faster than
        // the asynchronous counter-parts, so we should use them where possible [citation needed].
        // NOTE: the extra scope here is so that the compiler doesn't think `state` is held across
        // a yield point, and thus marks the returned future as !Send.
        let generation = {
            let mut state = self.state.lock().unwrap();
            let generation = state.generation;
            state.arrived += 1;
            if state.arrived == self.n {
                // we are the leader for this generation
                // wake everyone, increment the generation, and return
                state
                    .waker
                    .send(state.generation)
                    .expect("there is at least one receiver");
                state.arrived = 0;
                state.generation += 1;
                return BarrierWaitResult(true);
            }

            generation
        };

        // we're going to have to wait for the last of the generation to arrive
        let mut wait = self.wait.clone();

        loop {
            let _ = wait.changed().await;

            // note that the first time through the loop, this _will_ yield a generation
            // immediately, since we cloned a receiver that has never seen any values.
            if *wait.borrow() >= generation {
                break;
            }
        }

        BarrierWaitResult(false)
    }
}

/// A `BarrierWaitResult` is returned by `wait` when all tasks in the `Barrier` have rendezvoused.
#[derive(Debug, Clone)]
pub struct BarrierWaitResult(bool);

impl BarrierWaitResult {
    /// Returns `true` if this task from wait is the "leader task".
    ///
    /// Only one task will have `true` returned from their result, all other tasks will have
    /// `false` returned.
    pub fn is_leader(&self) -> bool {
        self.0
    }
}
<|endoftext|>


<commit_before>//! Job management (mostly for windows)
//!
//! Most of the time when you're running cargo you expect Ctrl-C to actually
//! terminate the entire tree of processes in play, not just the one at the top
//! (cago). This currently works "by default" on Unix platforms because Ctrl-C
//! actually sends a signal to the *process group* rather than the parent
//! process, so everything will get torn down. On Windows, however, this does
//! not happen and Ctrl-C just kills cargo.
//!
//! To achieve the same semantics on Windows we use Job Objects to ensure that
//! all processes die at the same time. Job objects have a mode of operation
//! where when all handles to the object are closed it causes all child
//! processes associated with the object to be terminated immediately.
//! Conveniently whenever a process in the job object spawns a new process the
//! child will be associated with the job object as well. This means if we add
//! ourselves to the job object we create then everything will get torn down!

pub use self::imp::Setup;

pub fn setup() -> Option<Setup> {
    unsafe { imp::setup() }
}

#[cfg(unix)]
mod imp {
    use std::env;
    use libc;

    pub type Setup = ();

    pub unsafe fn setup() -> Option<()> {
        // There's a test case for the behavior of
        // when-cargo-is-killed-subprocesses-are-also-killed, but that requires
        // one cargo spawned to become its own session leader, so we do that
        // here.
        if env::var("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE").is_ok() {
            libc::setsid();
        }
        Some(())
    }
}

#[cfg(windows)]
mod imp {
    extern crate winapi;

    use std::ffi::OsString;
    use std::io;
    use std::mem;
    use std::os::windows::prelude::*;
    use std::ptr;

    use self::winapi::shared::basetsd::*;
    use self::winapi::shared::minwindef::*;
    use self::winapi::shared::minwindef::{FALSE, TRUE};
    use self::winapi::um::handleapi::*;
    use self::winapi::um::jobapi2::*;
    use self::winapi::um::jobapi::*;
    use self::winapi::um::processthreadsapi::*;
    use self::winapi::um::psapi::*;
    use self::winapi::um::synchapi::*;
    use self::winapi::um::winbase::*;
    use self::winapi::um::winnt::*;
    use self::winapi::um::winnt::HANDLE;

    pub struct Setup {
        job: Handle,
    }

    pub struct Handle {
        inner: HANDLE,
    }

    fn last_err() -> io::Error {
        io::Error::last_os_error()
    }

    pub unsafe fn setup() -> Option<Setup> {
        // Creates a new job object for us to use and then adds ourselves to it.
        // Note that all errors are basically ignored in this function,
        // intentionally. Job objects are "relatively new" in Windows,
        // particularly the ability to support nested job objects. Older
        // Windows installs don't support this ability. We probably don't want
        // to force Cargo to abort in this situation or force others to *not*
        // use job objects, so we instead just ignore errors and assume that
        // we're otherwise part of someone else's job object in this case.

        let job = CreateJobObjectW(ptr::null_mut(), ptr::null());
        if job.is_null() {
            return None;
        }
        let job = Handle { inner: job };

        // Indicate that when all handles to the job object are gone that all
        // process in the object should be killed. Note that this includes our
        // entire process tree by default because we've added ourselves and and
        // our children will reside in the job once we spawn a process.
        let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
        info = mem::zeroed();
        info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
        let r = SetInformationJobObject(
            job.inner,
            JobObjectExtendedLimitInformation,
            &mut info as *mut _ as LPVOID,
            mem::size_of_val(&info) as DWORD,
        );
        if r == 0 {
            return None;
        }

        // Assign our process to this job object, meaning that our children will
        // now live or die based on our existence.
        let me = GetCurrentProcess();
        let r = AssignProcessToJobObject(job.inner, me);
        if r == 0 {
            return None;
        }

        Some(Setup { job })
    }

    impl Drop for Setup {
        fn drop(&mut self) {
            // This is a litte subtle. By default if we are terminated then all
            // processes in our job object are terminated as well, but we
            // intentionally want to whitelist some processes to outlive our job
            // object (see below).
            //
            // To allow for this, we manually kill processes instead of letting
            // the job object kill them for us. We do this in a loop to handle
            // processes spawning other processes.
            //
            // Finally once this is all done we know that the only remaining
            // ones are ourselves and the whitelisted processes. The destructor
            // here then configures our job object to *not* kill everything on
            // close, then closes the job object.
            unsafe {
                while self.kill_remaining() {
                    info!("killed some, going for more");
                }

                let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
                info = mem::zeroed();
                let r = SetInformationJobObject(
                    self.job.inner,
                    JobObjectExtendedLimitInformation,
                    &mut info as *mut _ as LPVOID,
                    mem::size_of_val(&info) as DWORD,
                );
                if r == 0 {
                    info!("failed to configure job object to defaults: {}", last_err());
                }
            }
        }
    }

    impl Setup {
        unsafe fn kill_remaining(&mut self) -> bool {
            #[repr(C)]
            struct Jobs {
                header: JOBOBJECT_BASIC_PROCESS_ID_LIST,
                list: [ULONG_PTR; 1024],
            }

            let mut jobs: Jobs = mem::zeroed();
            let r = QueryInformationJobObject(
                self.job.inner,
                JobObjectBasicProcessIdList,
                &mut jobs as *mut _ as LPVOID,
                mem::size_of_val(&jobs) as DWORD,
                ptr::null_mut(),
            );
            if r == 0 {
                info!("failed to query job object: {}", last_err());
                return false;
            }

            let mut killed = false;
            let list = &jobs.list[..jobs.header.NumberOfProcessIdsInList as usize];
            assert!(!list.is_empty());
            info!("found {} remaining processes", list.len() - 1);

            let list = list.iter()
                .filter(|&&id| {
                    // let's not kill ourselves
                    id as DWORD != GetCurrentProcessId()
                })
                .filter_map(|&id| {
                    // Open the process with the necessary rights, and if this
                    // fails then we probably raced with the process exiting so we
                    // ignore the problem.
                    let flags = PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE | SYNCHRONIZE;
                    let p = OpenProcess(flags, FALSE, id as DWORD);
                    if p.is_null() {
                        None
                    } else {
                        Some(Handle { inner: p })
                    }
                })
                .filter(|p| {
                    // Test if this process was actually in the job object or not.
                    // If it's not then we likely raced with something else
                    // recycling this PID, so we just skip this step.
                    let mut res = 0;
                    let r = IsProcessInJob(p.inner, self.job.inner, &mut res);
                    if r == 0 {
                        info!("failed to test is process in job: {}", last_err());
                        return false;
                    }
                    res == TRUE
                });

            for p in list {
                // Load the file which this process was spawned from. We then
                // later use this for identification purposes.
                let mut buf = [0; 1024];
                let r = GetProcessImageFileNameW(p.inner, buf.as_mut_ptr(), buf.len() as DWORD);
                if r == 0 {
                    info!("failed to get image name: {}", last_err());
                    continue;
                }
                let s = OsString::from_wide(&buf[..r as usize]);
                info!("found remaining: {:?}", s);

                // And here's where we find the whole purpose for this
                // function!  Currently, our only whitelisted process is
                // `mspdbsrv.exe`, and more details about that can be found
                // here:
                //
                //      https://github.com/rust-lang/rust/issues/33145
                //
                // The gist of it is that all builds on one machine use the
                // same `mspdbsrv.exe` instance. If we were to kill this
                // instance then we could erroneously cause other builds to
                // fail.
                if let Some(s) = s.to_str() {
                    if s.contains("mspdbsrv") {
                        info!("\toops, this is mspdbsrv");
                        continue;
                    }
                }

                // Ok, this isn't mspdbsrv, let's kill the process. After we
                // kill it we wait on it to ensure that the next time around in
                // this function we're not going to see it again.
                let r = TerminateProcess(p.inner, 1);
                if r == 0 {
                    info!("\tfailed to kill subprocess: {}", last_err());
                    info!("\tassuming subprocess is dead...");
                } else {
                    info!("\tterminated subprocess");
                }
                let r = WaitForSingleObject(p.inner, INFINITE);
                if r != 0 {
                    info!("failed to wait for process to die: {}", last_err());
                    return false;
                }
                killed = true;
            }

            killed
        }
    }

    impl Drop for Handle {
        fn drop(&mut self) {
            unsafe {
                CloseHandle(self.inner);
            }
        }
    }
}
<commit_msg>Fix typo<commit_after>//! Job management (mostly for windows)
//!
//! Most of the time when you're running cargo you expect Ctrl-C to actually
//! terminate the entire tree of processes in play, not just the one at the top
//! (cago). This currently works "by default" on Unix platforms because Ctrl-C
//! actually sends a signal to the *process group* rather than the parent
//! process, so everything will get torn down. On Windows, however, this does
//! not happen and Ctrl-C just kills cargo.
//!
//! To achieve the same semantics on Windows we use Job Objects to ensure that
//! all processes die at the same time. Job objects have a mode of operation
//! where when all handles to the object are closed it causes all child
//! processes associated with the object to be terminated immediately.
//! Conveniently whenever a process in the job object spawns a new process the
//! child will be associated with the job object as well. This means if we add
//! ourselves to the job object we create then everything will get torn down!

pub use self::imp::Setup;

pub fn setup() -> Option<Setup> {
    unsafe { imp::setup() }
}

#[cfg(unix)]
mod imp {
    use std::env;
    use libc;

    pub type Setup = ();

    pub unsafe fn setup() -> Option<()> {
        // There's a test case for the behavior of
        // when-cargo-is-killed-subprocesses-are-also-killed, but that requires
        // one cargo spawned to become its own session leader, so we do that
        // here.
        if env::var("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE").is_ok() {
            libc::setsid();
        }
        Some(())
    }
}

#[cfg(windows)]
mod imp {
    extern crate winapi;

    use std::ffi::OsString;
    use std::io;
    use std::mem;
    use std::os::windows::prelude::*;
    use std::ptr;

    use self::winapi::shared::basetsd::*;
    use self::winapi::shared::minwindef::*;
    use self::winapi::shared::minwindef::{FALSE, TRUE};
    use self::winapi::um::handleapi::*;
    use self::winapi::um::jobapi2::*;
    use self::winapi::um::jobapi::*;
    use self::winapi::um::processthreadsapi::*;
    use self::winapi::um::psapi::*;
    use self::winapi::um::synchapi::*;
    use self::winapi::um::winbase::*;
    use self::winapi::um::winnt::*;
    use self::winapi::um::winnt::HANDLE;

    pub struct Setup {
        job: Handle,
    }

    pub struct Handle {
        inner: HANDLE,
    }

    fn last_err() -> io::Error {
        io::Error::last_os_error()
    }

    pub unsafe fn setup() -> Option<Setup> {
        // Creates a new job object for us to use and then adds ourselves to it.
        // Note that all errors are basically ignored in this function,
        // intentionally. Job objects are "relatively new" in Windows,
        // particularly the ability to support nested job objects. Older
        // Windows installs don't support this ability. We probably don't want
        // to force Cargo to abort in this situation or force others to *not*
        // use job objects, so we instead just ignore errors and assume that
        // we're otherwise part of someone else's job object in this case.

        let job = CreateJobObjectW(ptr::null_mut(), ptr::null());
        if job.is_null() {
            return None;
        }
        let job = Handle { inner: job };

        // Indicate that when all handles to the job object are gone that all
        // process in the object should be killed. Note that this includes our
        // entire process tree by default because we've added ourselves and
        // our children will reside in the job once we spawn a process.
        let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
        info = mem::zeroed();
        info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
        let r = SetInformationJobObject(
            job.inner,
            JobObjectExtendedLimitInformation,
            &mut info as *mut _ as LPVOID,
            mem::size_of_val(&info) as DWORD,
        );
        if r == 0 {
            return None;
        }

        // Assign our process to this job object, meaning that our children will
        // now live or die based on our existence.
        let me = GetCurrentProcess();
        let r = AssignProcessToJobObject(job.inner, me);
        if r == 0 {
            return None;
        }

        Some(Setup { job })
    }

    impl Drop for Setup {
        fn drop(&mut self) {
            // This is a litte subtle. By default if we are terminated then all
            // processes in our job object are terminated as well, but we
            // intentionally want to whitelist some processes to outlive our job
            // object (see below).
            //
            // To allow for this, we manually kill processes instead of letting
            // the job object kill them for us. We do this in a loop to handle
            // processes spawning other processes.
            //
            // Finally once this is all done we know that the only remaining
            // ones are ourselves and the whitelisted processes. The destructor
            // here then configures our job object to *not* kill everything on
            // close, then closes the job object.
            unsafe {
                while self.kill_remaining() {
                    info!("killed some, going for more");
                }

                let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION;
                info = mem::zeroed();
                let r = SetInformationJobObject(
                    self.job.inner,
                    JobObjectExtendedLimitInformation,
                    &mut info as *mut _ as LPVOID,
                    mem::size_of_val(&info) as DWORD,
                );
                if r == 0 {
                    info!("failed to configure job object to defaults: {}", last_err());
                }
            }
        }
    }

    impl Setup {
        unsafe fn kill_remaining(&mut self) -> bool {
            #[repr(C)]
            struct Jobs {
                header: JOBOBJECT_BASIC_PROCESS_ID_LIST,
                list: [ULONG_PTR; 1024],
            }

            let mut jobs: Jobs = mem::zeroed();
            let r = QueryInformationJobObject(
                self.job.inner,
                JobObjectBasicProcessIdList,
                &mut jobs as *mut _ as LPVOID,
                mem::size_of_val(&jobs) as DWORD,
                ptr::null_mut(),
            );
            if r == 0 {
                info!("failed to query job object: {}", last_err());
                return false;
            }

            let mut killed = false;
            let list = &jobs.list[..jobs.header.NumberOfProcessIdsInList as usize];
            assert!(!list.is_empty());
            info!("found {} remaining processes", list.len() - 1);

            let list = list.iter()
                .filter(|&&id| {
                    // let's not kill ourselves
                    id as DWORD != GetCurrentProcessId()
                })
                .filter_map(|&id| {
                    // Open the process with the necessary rights, and if this
                    // fails then we probably raced with the process exiting so we
                    // ignore the problem.
                    let flags = PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE | SYNCHRONIZE;
                    let p = OpenProcess(flags, FALSE, id as DWORD);
                    if p.is_null() {
                        None
                    } else {
                        Some(Handle { inner: p })
                    }
                })
                .filter(|p| {
                    // Test if this process was actually in the job object or not.
                    // If it's not then we likely raced with something else
                    // recycling this PID, so we just skip this step.
                    let mut res = 0;
                    let r = IsProcessInJob(p.inner, self.job.inner, &mut res);
                    if r == 0 {
                        info!("failed to test is process in job: {}", last_err());
                        return false;
                    }
                    res == TRUE
                });

            for p in list {
                // Load the file which this process was spawned from. We then
                // later use this for identification purposes.
                let mut buf = [0; 1024];
                let r = GetProcessImageFileNameW(p.inner, buf.as_mut_ptr(), buf.len() as DWORD);
                if r == 0 {
                    info!("failed to get image name: {}", last_err());
                    continue;
                }
                let s = OsString::from_wide(&buf[..r as usize]);
                info!("found remaining: {:?}", s);

                // And here's where we find the whole purpose for this
                // function!  Currently, our only whitelisted process is
                // `mspdbsrv.exe`, and more details about that can be found
                // here:
                //
                //      https://github.com/rust-lang/rust/issues/33145
                //
                // The gist of it is that all builds on one machine use the
                // same `mspdbsrv.exe` instance. If we were to kill this
                // instance then we could erroneously cause other builds to
                // fail.
                if let Some(s) = s.to_str() {
                    if s.contains("mspdbsrv") {
                        info!("\toops, this is mspdbsrv");
                        continue;
                    }
                }

                // Ok, this isn't mspdbsrv, let's kill the process. After we
                // kill it we wait on it to ensure that the next time around in
                // this function we're not going to see it again.
                let r = TerminateProcess(p.inner, 1);
                if r == 0 {
                    info!("\tfailed to kill subprocess: {}", last_err());
                    info!("\tassuming subprocess is dead...");
                } else {
                    info!("\tterminated subprocess");
                }
                let r = WaitForSingleObject(p.inner, INFINITE);
                if r != 0 {
                    info!("failed to wait for process to die: {}", last_err());
                    return false;
                }
                killed = true;
            }

            killed
        }
    }

    impl Drop for Handle {
        fn drop(&mut self) {
            unsafe {
                CloseHandle(self.inner);
            }
        }
    }
}
<|endoftext|>