{"action":"created","comment":{"url":"https://api.github.com/repos/servo/ipc-channel/pulls/comments/87716664","pull_request_review_id":8318558,"id":87716664,"diff_hunk":"@@ -0,0 +1,1482 @@\n+// Copyright 2015 The Servo Project Developers. See the COPYRIGHT\n+// file at the top-level directory of this distribution.\n+//\n+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or\n+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license\n+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your\n+// option. This file may not be copied, modified, or distributed\n+// except according to those terms.\n+\n+use std::cell::RefCell;\n+use std::cmp::PartialEq;\n+use std::default::Default;\n+use std::ffi::CString;\n+use std::io::{Error, ErrorKind};\n+use std::marker::{Send, Sync};\n+use std::mem;\n+use std::ops::Deref;\n+use std::env;\n+use std::ptr;\n+use std::slice;\n+\n+use libc::{intptr_t, memcmp};\n+\n+use uuid::Uuid;\n+\n+use serde;\n+use bincode;\n+use bincode::serde::DeserializeError;\n+\n+use winapi;\n+use winapi::{HANDLE, INVALID_HANDLE_VALUE, LPVOID};\n+use kernel32;\n+\n+// some debug bump macros to better track what's going on in case of errors\n+lazy_static! {\n+    static ref CURRENT_PROCESS_ID: winapi::ULONG = unsafe { kernel32::GetCurrentProcessId() };\n+    static ref CURRENT_PROCESS_HANDLE: intptr_t = unsafe { kernel32::GetCurrentProcess() as intptr_t };\n+\n+    static ref DD_ENABLED: bool = env::var_os(\"IPC_CHANNEL_WIN_DEBUG_DUMP\").is_some();\n+    static ref DD2_ENABLED: bool = env::var_os(\"IPC_CHANNEL_WIN_DEBUG_MORE_DUMP\").is_some();\n+}\n+\n+macro_rules! dd { ($($rest:tt)*) => { if *DD_ENABLED { println!($($rest)*); } } }\n+macro_rules! dd2 { ($($rest:tt)*) => { if *DD_ENABLED || *DD2_ENABLED { println!($($rest)*); } } }\n+\n+// We stick these in WinErrors to indicate various things\n+// that aren't quite errors.\n+const MAGIC_CODE_BASE: u32 = 0xdeadbee0u32;\n+const MAGIC_NO_OUTSTANDING_DATA_CODE: u32 = MAGIC_CODE_BASE + 0;\n+const MAGIC_CHANNEL_CLOSED_CODE: u32 = MAGIC_CODE_BASE + 1;\n+\n+// When we create the pipe, how big of a write buffer do we specify?\n+// This is reserved in the nonpaged pool.  The fragment size is the\n+// max we can write to the pipe without fragmentation, and the\n+// buffer size is what we tell the pipe it is, so we have room\n+// for out of band data etc.  We can probably do way less than\n+// 16k extra.\n+const MAX_FRAGMENT_SIZE: usize = 64 * 1024;\n+const WRITE_BUFFER_SIZE: usize = MAX_FRAGMENT_SIZE + 16*1024;\n+\n+// When we read, how big of a heap buffer do we allocate?\n+const READ_BUFFER_SIZE: usize = 128 * 1024; //128k;\n+const READ_BUFFER_MAX_GROWTH: usize = 1 * 1024 * 1024; // 1MB max growth\n+\n+// The message header is 2xu32 -- first one is the length of the data,\n+// second one is the length of any out of band data.  If the first\n+// field is INVALID_HEADER_DATA_SIZE, then the second one signifies\n+// a control code (currently only MAGIC_CHANNEL_CLOSED_CODE is used)\n+const HEADER_SIZE: usize = 8; //mem::size_of::<u32>()*2;\n+const INVALID_HEADER_DATA_SIZE: u32 = 0xffffffffu32;\n+\n+#[allow(non_snake_case)]\n+fn GetLastError() -> u32 {\n+    unsafe {\n+        kernel32::GetLastError()\n+    }\n+}\n+\n+pub fn channel() -> Result<(OsIpcSender, OsIpcReceiver),WinError> {\n+    let mut receiver = try!(OsIpcReceiver::new());\n+    let sender = try!(receiver.sender());\n+    Ok((sender, receiver))\n+}\n+\n+// If we have any channel handles or shmem segments,\n+// then we'll send an OsIpcOutOfBandMessage after\n+// the data message.\n+#[derive(Debug)]\n+struct OsIpcOutOfBandMessage {\n+    channel_is_sender: Vec<bool>,\n+    channel_handles: Vec<intptr_t>,\n+    shmem_sizes: Vec<u64>,\n+    shmem_handles: Vec<intptr_t>,\n+    big_data_receiver_handle: intptr_t,\n+}\n+\n+impl OsIpcOutOfBandMessage {\n+    fn new() -> OsIpcOutOfBandMessage {\n+        OsIpcOutOfBandMessage {\n+            channel_is_sender: vec![],\n+            channel_handles: vec![],\n+            shmem_sizes: vec![],\n+            shmem_handles: vec![],\n+            big_data_receiver_handle: 0,\n+        }\n+    }\n+\n+    fn needs_to_be_sent(&self) -> bool {\n+        !self.channel_handles.is_empty() ||\n+        !self.shmem_handles.is_empty() ||\n+        self.big_data_receiver_handle != 0\n+    }\n+\n+    fn add_sender(&mut self, handle: HANDLE) {\n+        self.channel_is_sender.push(true);\n+        self.channel_handles.push(handle as intptr_t);\n+    }\n+\n+    fn add_receiver(&mut self, handle: HANDLE) {\n+        self.channel_is_sender.push(false);\n+        self.channel_handles.push(handle as intptr_t);\n+    }\n+\n+    fn add_shmem(&mut self, handle: HANDLE, size: usize) {\n+        self.shmem_sizes.push(size as u64);\n+        self.shmem_handles.push(handle as intptr_t);\n+    }\n+\n+    fn set_big_data_receiver_handle(&mut self, handle: HANDLE) {\n+        self.big_data_receiver_handle = handle as intptr_t;\n+    }\n+}\n+\n+impl serde::Serialize for OsIpcOutOfBandMessage {\n+    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>\n+        where S: serde::Serializer\n+    {\n+        ((&self.channel_is_sender, &self.channel_handles,\n+          &self.shmem_sizes, &self.shmem_handles,\n+          &self.big_data_receiver_handle)).serialize(serializer)\n+    }\n+}\n+\n+impl serde::Deserialize for OsIpcOutOfBandMessage {\n+    fn deserialize<D>(deserializer: &mut D) -> Result<OsIpcOutOfBandMessage, D::Error>\n+        where D: serde::Deserializer\n+    {\n+        let (channel_is_sender, channel_handles,\n+             shmem_sizes, shmem_handles, big_data_receiver_handle) =\n+            try!(serde::Deserialize::deserialize(deserializer));\n+        Ok(OsIpcOutOfBandMessage {\n+            channel_is_sender: channel_is_sender, channel_handles: channel_handles,\n+            shmem_sizes: shmem_sizes, shmem_handles: shmem_handles,\n+            big_data_receiver_handle: big_data_receiver_handle\n+        })\n+    }\n+}\n+\n+fn make_pipe_id() -> Uuid {\n+    Uuid::new_v4()\n+}\n+\n+fn make_pipe_name(pipe_id: &Uuid) -> CString {\n+    CString::new(format!(\"\\\\\\\\.\\\\pipe\\\\rust-ipc-{}\", pipe_id.to_string())).unwrap()\n+}\n+\n+// Duplicate a given handle from this process to the target one, passing the\n+// given flags to DuplicateHandle.\n+//\n+// Unlike win32 DuplicateHandle, this will preserve INVALID_HANDLE_VALUE (which is\n+// also the pseudohandle for the current process).\n+#[allow(dead_code)]\n+fn dup_handle_to_process_with_flags(handle: HANDLE, other_process: HANDLE, flags: winapi::DWORD)\n+                                    -> Result<HANDLE,WinError>\n+{\n+    if handle == INVALID_HANDLE_VALUE {\n+        return Ok(INVALID_HANDLE_VALUE);\n+    }\n+\n+    unsafe {\n+        let mut new_handle: HANDLE = INVALID_HANDLE_VALUE;\n+        let ok = kernel32::DuplicateHandle(*CURRENT_PROCESS_HANDLE as HANDLE, handle,\n+                                           other_process, &mut new_handle,\n+                                           0, winapi::FALSE, flags);\n+        if ok == winapi::FALSE {\n+            Err(WinError::last(\"DuplicateHandle\"))\n+        } else {\n+            Ok(new_handle)\n+        }\n+    }\n+}\n+\n+// duplicate a handle in the current process\n+fn dup_handle(handle: HANDLE) -> Result<HANDLE,WinError> {\n+    dup_handle_to_process(handle, unsafe { kernel32::GetCurrentProcess() })\n+}\n+\n+// duplicate a handle to the target process\n+fn dup_handle_to_process(handle: HANDLE, other_process: HANDLE) -> Result<HANDLE,WinError> {\n+    dup_handle_to_process_with_flags(handle, other_process, winapi::DUPLICATE_SAME_ACCESS)\n+}\n+\n+// duplicate a handle to the target process, closing the source handle\n+fn move_handle_to_process(handle: HANDLE, other_process: HANDLE) -> Result<HANDLE,WinError> {\n+    dup_handle_to_process_with_flags(handle, other_process, winapi::DUPLICATE_CLOSE_SOURCE | winapi::DUPLICATE_SAME_ACCESS)\n+}\n+\n+// get the current process handle as a handle in another process\n+#[allow(dead_code)]\n+fn current_process_handle_to_process(other_process: HANDLE) -> Result<HANDLE,WinError> {\n+    unsafe {\n+        let mut new_handle: HANDLE = INVALID_HANDLE_VALUE;\n+        let ok = kernel32::DuplicateHandle(kernel32::GetCurrentProcess(), kernel32::GetCurrentProcess(),\n+                                           other_process, &mut new_handle,\n+                                           winapi::PROCESS_DUP_HANDLE, winapi::FALSE, 0);\n+        if ok == winapi::FALSE {\n+            Err(WinError::last(\"DuplicateHandle\"))\n+        } else {\n+            Ok(new_handle)\n+        }\n+    }\n+}\n+\n+fn create_overlapped() -> Box<winapi::OVERLAPPED> {\n+    Box::new(winapi::OVERLAPPED {\n+        Internal: 0,\n+        InternalHigh: 0,\n+        Offset: 0,\n+        OffsetHigh: 0,\n+        hEvent: 0 as HANDLE\n+    })\n+}\n+\n+fn reset_overlapped(ov: &mut winapi::OVERLAPPED) {\n+    ov.Internal = 0;\n+    ov.InternalHigh = 0;\n+    ov.Offset = 0;\n+    ov.OffsetHigh = 0;\n+    ov.hEvent = 0 as HANDLE;\n+}\n+\n+\n+#[derive(Debug, PartialEq)]\n+struct WinHandle {\n+    h: HANDLE\n+}\n+\n+unsafe impl Send for WinHandle { }\n+unsafe impl Sync for WinHandle { }\n+\n+impl Drop for WinHandle {\n+    fn drop(&mut self) {\n+        self.close();\n+    }\n+}\n+\n+impl Default for WinHandle {\n+    fn default() -> WinHandle {\n+        WinHandle { h: INVALID_HANDLE_VALUE }\n+    }\n+}\n+\n+impl Deref for WinHandle {\n+    type Target = HANDLE;\n+\n+    #[inline]\n+    fn deref(&self) -> &HANDLE {\n+        &self.h\n+    }\n+}\n+\n+impl WinHandle {\n+    fn new(h: HANDLE) -> WinHandle {\n+        WinHandle { h: h }\n+    }\n+\n+    #[allow(dead_code)]\n+    fn invalid() -> WinHandle {\n+        WinHandle { h: INVALID_HANDLE_VALUE }\n+    }\n+\n+    fn is_valid(&self) -> bool {\n+        self.h != INVALID_HANDLE_VALUE\n+    }\n+\n+    fn close(&mut self) {\n+        unsafe {\n+            dd!(\"WinHandle drop: {:?}\", self.h);\n+            let h = self.take();\n+            kernel32::CloseHandle(h);\n+        }\n+    }\n+\n+    fn take(&mut self) -> HANDLE {\n+        mem::replace(&mut self.h, INVALID_HANDLE_VALUE)\n+    }\n+}\n+\n+// MessageReader implements blocking/nonblocking reads of messages\n+// from the handle\n+#[derive(Debug)]\n+struct MessageReader {\n+    // The reader handle; same as the one on OsIpcReceiver, here for\n+    // convenience\n+    handle: HANDLE,\n+\n+    // The OVERLAPPED struct for async IO on this receiver; we'll only\n+    // ever have one in flight\n+    ov: Box<winapi::OVERLAPPED>,\n+\n+    // A read buffer for any pending reads\n+    read_buf: Vec<u8>,\n+\n+    // If we have already issued an async read\n+    read_in_progress: bool,\n+\n+    // If we received a BROKEN_PIPE or other error\n+    // indicating that the remote end has closed the pipe\n+    closed: bool,\n+\n+    // this is ONLY here for debugging purposes.\n+    // (for printfs so we know which iocp this is part of)\n+    iocp: HANDLE,\n+}\n+\n+impl MessageReader {\n+    fn new(handle: HANDLE) -> MessageReader {\n+        MessageReader {\n+            handle: handle,\n+            ov: create_overlapped(),\n+            read_buf: Vec::with_capacity(READ_BUFFER_SIZE),\n+            read_in_progress: false,\n+            closed: false,\n+            iocp: 0 as HANDLE,\n+        }\n+    }\n+\n+    fn ov_ptr(&self) -> *mut winapi::OVERLAPPED {\n+        self.ov.deref() as *const _ as *mut winapi::OVERLAPPED\n+    }\n+\n+    fn reset_overlapped(&mut self) {\n+        reset_overlapped(&mut self.ov);\n+    }\n+\n+    fn cancel_io(&mut self) {\n+        unsafe {\n+            if self.read_in_progress {\n+                kernel32::CancelIoEx(self.handle, self.ov_ptr());\n+                self.read_in_progress = false;\n+            }\n+        }\n+    }\n+\n+    fn ensure_is_reading(&mut self) -> Result<(),WinError> {\n+        self.start_read()\n+    }\n+\n+    // Called when we receive an IO Completion Packet for this handle.\n+    fn notify_completion(&mut self, err: u32) -> Result<(),WinError> {\n+        dd2!(\"[$ {:?}:{:?}] notify_completion\", self.iocp, self.handle);\n+\n+        if err == winapi::ERROR_BROKEN_PIPE {\n+            assert!(!self.closed, \"we shouldn't get an async BROKEN_PIPE after we already got one\");\n+            self.closed = true;\n+            return Ok(());\n+        }\n+\n+        let nbytes = self.ov.InternalHigh as u32;\n+        let offset = self.ov.Offset;\n+\n+        assert!(offset == 0);\n+\n+        // if the remote end closed...\n+        if err != winapi::ERROR_SUCCESS {\n+            // This should never happen\n+            panic!(\"[$ {:?}:{:?}] *** notify_completion: unhandled error reported! {}\", self.iocp, self.handle, err);\n+        }\n+\n+        unsafe {\n+            let new_size = self.read_buf.len() + nbytes as usize;\n+            dd!(\"nbytes: {}, offset {}, buf len {}->{}, capacity {}\",\n+                nbytes, offset, self.read_buf.len(), new_size, self.read_buf.capacity());\n+            assert!(new_size <= self.read_buf.capacity());\n+            self.read_buf.set_len(new_size);\n+        }\n+\n+        Ok(())\n+    }\n+\n+    // kick off an asynchronous read\n+    fn start_read(&mut self) -> Result<(),WinError> {\n+        if self.read_in_progress || self.closed {\n+            return Ok(());\n+        }\n+\n+        dd2!(\"[$ {:?}:{:?}] start_read ov {:?}\", self.iocp, self.handle, self.ov_ptr());\n+\n+        let buf_len = self.read_buf.len();\n+        let mut buf_cap = self.read_buf.capacity();\n+        let mut bytes_read: u32 = 0;\n+\n+        // if the buffer is full, add more capacity\n+        if buf_cap == buf_len {\n+            self.read_buf.reserve(match buf_cap {\n+                0 => READ_BUFFER_SIZE,\n+                1...READ_BUFFER_MAX_GROWTH => buf_cap,\n+                _ => READ_BUFFER_MAX_GROWTH\n+            });\n+            buf_cap = self.read_buf.capacity();\n+        }\n+\n+        // issue the read to the buffer, at the current length offset\n+        unsafe {\n+            self.reset_overlapped();\n+            let buf_ptr = self.read_buf.as_mut_ptr() as LPVOID;\n+            let max_read_bytes = buf_cap - buf_len;\n+            let ok = kernel32::ReadFile(self.handle,\n+                                        buf_ptr.offset(buf_len as isize),\n+                                        max_read_bytes as u32,\n+                                        &mut bytes_read,\n+                                        self.ov_ptr());\n+\n+            // ReadFile can return TRUE; if it does, an IO completion\n+            // packet is still posted to any port, and the OVERLAPPED\n+            // structure has the IO operation flagged as complete.\n+            if ok == winapi::FALSE {\n+                let err = GetLastError();\n+                if err == winapi::ERROR_BROKEN_PIPE {\n+                    dd!(\"[$ {:?}:{:?}] BROKEN_PIPE straight from ReadFile\", self.iocp, self.handle);\n+                    self.closed = true;\n+                    return Ok(());\n+                }\n+\n+                if err == winapi::ERROR_IO_PENDING {\n+                    self.read_in_progress = true;\n+                    return Ok(());\n+                }\n+\n+                Err(WinError::from_system(err, \"ReadFile\"))\n+            } else {\n+                self.read_in_progress = true;\n+                Ok(())\n+            }\n+        }\n+    }\n+\n+    fn message_length(&self) -> Option<(usize,usize)> {\n+        dd!(\"[$ {:?}:{:?}] message_length read_buf: {} bytes\", self.iocp, self.handle, self.read_buf.len());\n+        if self.read_buf.len() < HEADER_SIZE {\n+            return None;\n+        }\n+\n+        let buf_header: &[u32] = unsafe { slice::from_raw_parts(self.read_buf.as_ptr() as *const u32, 2) };\n+        let data_bytes = buf_header[0] as usize;\n+        let oob_bytes = buf_header[1] as usize;\n+        let bytes_needed = HEADER_SIZE + data_bytes + oob_bytes;\n+        if self.read_buf.len() >= bytes_needed {\n+            Some((data_bytes, oob_bytes))\n+        } else {\n+            None\n+        }\n+    }\n+\n+    // Err(false) -> something really failed\n+    // Err(true) -> no message\n+    // FIXME This is dumb, we should probably make this return Result<Option<(...)>,WinError>\n+    // so that we can pass through the error that's lost in map_err below\n+    fn get_message(&mut self) -> Result<(Vec<u8>, Vec<OsOpaqueIpcChannel>, Vec<OsIpcSharedMemory>),bool> {\n+        let message_lengths = self.message_length();\n+        if message_lengths.is_none() {\n+            return Err(false)\n+        }\n+        let (data_bytes, oob_bytes) = message_lengths.unwrap();\n+\n+        let bytes_needed = HEADER_SIZE + data_bytes as usize + oob_bytes as usize;\n+\n+        // remove this message's bytes from read_buf, or just take read_buf\n+        // if it contains exactly one message\n+        let msg_buf = if self.read_buf.len() == bytes_needed {\n+            mem::replace(&mut self.read_buf, Vec::with_capacity(READ_BUFFER_SIZE))","path":"src/platform/windows/mod.rs","position":null,"original_position":481,"commit_id":"7e410806e9b18b9cf7243cd040f06fefe5267e51","original_commit_id":"cdeacfd1742c4cbf07acde66ca24c6f22d054b8e","user":{"login":"vvuk","id":194965,"avatar_url":"https://avatars.githubusercontent.com/u/194965?v=3","gravatar_id":"","url":"https://api.github.com/users/vvuk","html_url":"https://github.com/vvuk","followers_url":"https://api.github.com/users/vvuk/followers","following_url":"https://api.github.com/users/vvuk/following{/other_user}","gists_url":"https://api.github.com/users/vvuk/gists{/gist_id}","starred_url":"https://api.github.com/users/vvuk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vvuk/subscriptions","organizations_url":"https://api.github.com/users/vvuk/orgs","repos_url":"https://api.github.com/users/vvuk/repos","events_url":"https://api.github.com/users/vvuk/events{/privacy}","received_events_url":"https://api.github.com/users/vvuk/received_events","type":"User","site_admin":false},"body":"So this is interesting -- `Vec.drain` by itself is faster (~5%) for small sizes, likely due to saving the branch -- but then loses dramatically as the payload size goes up...  especially in the case of out of band channels where every packet will basically saturate the read buffer size.\r\n\r\ndrain only:\r\n\r\n```\r\ntest create_channel ... bench:      18,137 ns/iter (+/- 3,548)\r\ntest size_00_1      ... bench:       1,876 ns/iter (+/- 591)\r\ntest size_01_2      ... bench:       1,835 ns/iter (+/- 518)\r\ntest size_02_4      ... bench:       1,886 ns/iter (+/- 540)\r\ntest size_03_8      ... bench:       1,809 ns/iter (+/- 319)\r\ntest size_04_16     ... bench:       1,818 ns/iter (+/- 328)\r\ntest size_05_32     ... bench:       1,842 ns/iter (+/- 268)\r\ntest size_06_64     ... bench:       1,914 ns/iter (+/- 425)\r\ntest size_07_128    ... bench:       1,980 ns/iter (+/- 443)\r\ntest size_08_256    ... bench:       2,151 ns/iter (+/- 296)\r\ntest size_09_512    ... bench:       2,422 ns/iter (+/- 456)\r\ntest size_10_1k     ... bench:       2,978 ns/iter (+/- 1,594)\r\ntest size_11_2k     ... bench:       4,069 ns/iter (+/- 723)\r\ntest size_12_4k     ... bench:       5,926 ns/iter (+/- 712)\r\ntest size_13_8k     ... bench:       9,481 ns/iter (+/- 1,384)\r\ntest size_14_16k    ... bench:      28,817 ns/iter (+/- 5,601)\r\ntest size_15_32k    ... bench:      59,549 ns/iter (+/- 19,252)\r\ntest size_16_64k    ... bench:      97,283 ns/iter (+/- 28,744)\r\ntest size_17_128k   ... bench:     462,368 ns/iter (+/- 106,780)\r\ntest size_18_256k   ... bench:     645,383 ns/iter (+/- 231,427)\r\ntest size_19_512k   ... bench:   1,504,386 ns/iter (+/- 302,640)\r\ntest size_20_1m     ... bench:   2,852,327 ns/iter (+/- 672,506)\r\ntest size_21_2m     ... bench:   5,843,816 ns/iter (+/- 1,271,229)\r\ntest size_22_4m     ... bench:  11,921,589 ns/iter (+/- 2,496,657)\r\ntest size_23_8m     ... bench:  24,080,137 ns/iter (+/- 2,982,565)\r\n```\r\n\r\nwith mem::replace optimization:\r\n\r\n```\r\ntest create_channel ... bench:      18,163 ns/iter (+/- 4,020)\r\ntest size_00_1      ... bench:       1,998 ns/iter (+/- 424)\r\ntest size_01_2      ... bench:       1,978 ns/iter (+/- 197)\r\ntest size_02_4      ... bench:       2,009 ns/iter (+/- 349)\r\ntest size_03_8      ... bench:       1,987 ns/iter (+/- 566)\r\ntest size_04_16     ... bench:       2,029 ns/iter (+/- 208)\r\ntest size_05_32     ... bench:       2,028 ns/iter (+/- 313)\r\ntest size_06_64     ... bench:       2,026 ns/iter (+/- 375)\r\ntest size_07_128    ... bench:       2,002 ns/iter (+/- 327)\r\ntest size_08_256    ... bench:       2,044 ns/iter (+/- 495)\r\ntest size_09_512    ... bench:       2,250 ns/iter (+/- 558)\r\ntest size_10_1k     ... bench:       2,395 ns/iter (+/- 491)\r\ntest size_11_2k     ... bench:       2,492 ns/iter (+/- 671)\r\ntest size_12_4k     ... bench:       3,134 ns/iter (+/- 694)\r\ntest size_13_8k     ... bench:       3,771 ns/iter (+/- 654)\r\ntest size_14_16k    ... bench:      17,574 ns/iter (+/- 5,131)\r\ntest size_15_32k    ... bench:      22,185 ns/iter (+/- 6,685)\r\ntest size_16_64k    ... bench:      33,540 ns/iter (+/- 8,507)\r\ntest size_17_128k   ... bench:     380,691 ns/iter (+/- 94,913)\r\ntest size_18_256k   ... bench:     418,281 ns/iter (+/- 126,120)\r\ntest size_19_512k   ... bench:     961,175 ns/iter (+/- 221,804)\r\ntest size_20_1m     ... bench:   1,673,877 ns/iter (+/- 429,866)\r\ntest size_21_2m     ... bench:   3,382,619 ns/iter (+/- 787,396)\r\ntest size_22_4m     ... bench:   7,339,462 ns/iter (+/- 1,451,900)\r\ntest size_23_8m     ... bench:  14,835,211 ns/iter (+/- 2,320,673)\r\n```\r\n","created_at":"2016-11-13T17:18:34Z","updated_at":"2016-11-13T17:18:34Z","html_url":"https://github.com/servo/ipc-channel/pull/108#discussion_r87716664","pull_request_url":"https://api.github.com/repos/servo/ipc-channel/pulls/108","_links":{"self":{"href":"https://api.github.com/repos/servo/ipc-channel/pulls/comments/87716664"},"html":{"href":"https://github.com/servo/ipc-channel/pull/108#discussion_r87716664"},"pull_request":{"href":"https://api.github.com/repos/servo/ipc-channel/pulls/108"}}},"pull_request":{"url":"https://api.github.com/repos/servo/ipc-channel/pulls/108","id":88001841,"html_url":"https://github.com/servo/ipc-channel/pull/108","diff_url":"https://github.com/servo/ipc-channel/pull/108.diff","patch_url":"https://github.com/servo/ipc-channel/pull/108.patch","issue_url":"https://api.github.com/repos/servo/ipc-channel/issues/108","number":108,"state":"open","locked":false,"title":"Implement ipc-channel on Windows","user":{"login":"vvuk","id":194965,"avatar_url":"https://avatars.githubusercontent.com/u/194965?v=3","gravatar_id":"","url":"https://api.github.com/users/vvuk","html_url":"https://github.com/vvuk","followers_url":"https://api.github.com/users/vvuk/followers","following_url":"https://api.github.com/users/vvuk/following{/other_user}","gists_url":"https://api.github.com/users/vvuk/gists{/gist_id}","starred_url":"https://api.github.com/users/vvuk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vvuk/subscriptions","organizations_url":"https://api.github.com/users/vvuk/orgs","repos_url":"https://api.github.com/users/vvuk/repos","events_url":"https://api.github.com/users/vvuk/events{/privacy}","received_events_url":"https://api.github.com/users/vvuk/received_events","type":"User","site_admin":false},"body":"This implementation uses named pipes on Windows, with auto-generated uuid\nnames.  It takes advantage of DuplicateHandle to clone handles to target\nprocesses when sending handles cross-process.  Shared memory is implemented\nusing anonymous file mappings.  select() and friends are implemented using\nIO Completion Ports.  I learned a lot about Windows IO doing this, more than I wanted to :)\n\nAll the current tests pass.  I have not tried actually running servo with multiprocess ipc -- that's next, but I wanted to get this up for review first.  There's a branch (win32-unsquashed) that has a large sequence of commits that led to this, in case that's interesting to anyone... it contains a couple of abandoned approaches early on though.\n","created_at":"2016-10-05T01:11:08Z","updated_at":"2016-11-13T17:18:34Z","closed_at":null,"merged_at":null,"merge_commit_sha":"8ab891357bee28b44ce80afed7eee74dea5805f1","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/servo/ipc-channel/pulls/108/commits","review_comments_url":"https://api.github.com/repos/servo/ipc-channel/pulls/108/comments","review_comment_url":"https://api.github.com/repos/servo/ipc-channel/pulls/comments{/number}","comments_url":"https://api.github.com/repos/servo/ipc-channel/issues/108/comments","statuses_url":"https://api.github.com/repos/servo/ipc-channel/statuses/7e410806e9b18b9cf7243cd040f06fefe5267e51","head":{"label":"vvuk:win32","ref":"win32","sha":"7e410806e9b18b9cf7243cd040f06fefe5267e51","user":{"login":"vvuk","id":194965,"avatar_url":"https://avatars.githubusercontent.com/u/194965?v=3","gravatar_id":"","url":"https://api.github.com/users/vvuk","html_url":"https://github.com/vvuk","followers_url":"https://api.github.com/users/vvuk/followers","following_url":"https://api.github.com/users/vvuk/following{/other_user}","gists_url":"https://api.github.com/users/vvuk/gists{/gist_id}","starred_url":"https://api.github.com/users/vvuk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vvuk/subscriptions","organizations_url":"https://api.github.com/users/vvuk/orgs","repos_url":"https://api.github.com/users/vvuk/repos","events_url":"https://api.github.com/users/vvuk/events{/privacy}","received_events_url":"https://api.github.com/users/vvuk/received_events","type":"User","site_admin":false},"repo":{"id":42744189,"name":"ipc-channel","full_name":"vvuk/ipc-channel","owner":{"login":"vvuk","id":194965,"avatar_url":"https://avatars.githubusercontent.com/u/194965?v=3","gravatar_id":"","url":"https://api.github.com/users/vvuk","html_url":"https://github.com/vvuk","followers_url":"https://api.github.com/users/vvuk/followers","following_url":"https://api.github.com/users/vvuk/following{/other_user}","gists_url":"https://api.github.com/users/vvuk/gists{/gist_id}","starred_url":"https://api.github.com/users/vvuk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vvuk/subscriptions","organizations_url":"https://api.github.com/users/vvuk/orgs","repos_url":"https://api.github.com/users/vvuk/repos","events_url":"https://api.github.com/users/vvuk/events{/privacy}","received_events_url":"https://api.github.com/users/vvuk/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/vvuk/ipc-channel","description":"A multiprocess drop-in replacement for Rust channels","fork":true,"url":"https://api.github.com/repos/vvuk/ipc-channel","forks_url":"https://api.github.com/repos/vvuk/ipc-channel/forks","keys_url":"https://api.github.com/repos/vvuk/ipc-channel/keys{/key_id}","collaborators_url":"https://api.github.com/repos/vvuk/ipc-channel/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/vvuk/ipc-channel/teams","hooks_url":"https://api.github.com/repos/vvuk/ipc-channel/hooks","issue_events_url":"https://api.github.com/repos/vvuk/ipc-channel/issues/events{/number}","events_url":"https://api.github.com/repos/vvuk/ipc-channel/events","assignees_url":"https://api.github.com/repos/vvuk/ipc-channel/assignees{/user}","branches_url":"https://api.github.com/repos/vvuk/ipc-channel/branches{/branch}","tags_url":"https://api.github.com/repos/vvuk/ipc-channel/tags","blobs_url":"https://api.github.com/repos/vvuk/ipc-channel/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/vvuk/ipc-channel/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/vvuk/ipc-channel/git/refs{/sha}","trees_url":"https://api.github.com/repos/vvuk/ipc-channel/git/trees{/sha}","statuses_url":"https://api.github.com/repos/vvuk/ipc-channel/statuses/{sha}","languages_url":"https://api.github.com/repos/vvuk/ipc-channel/languages","stargazers_url":"https://api.github.com/repos/vvuk/ipc-channel/stargazers","contributors_url":"https://api.github.com/repos/vvuk/ipc-channel/contributors","subscribers_url":"https://api.github.com/repos/vvuk/ipc-channel/subscribers","subscription_url":"https://api.github.com/repos/vvuk/ipc-channel/subscription","commits_url":"https://api.github.com/repos/vvuk/ipc-channel/commits{/sha}","git_commits_url":"https://api.github.com/repos/vvuk/ipc-channel/git/commits{/sha}","comments_url":"https://api.github.com/repos/vvuk/ipc-channel/comments{/number}","issue_comment_url":"https://api.github.com/repos/vvuk/ipc-channel/issues/comments{/number}","contents_url":"https://api.github.com/repos/vvuk/ipc-channel/contents/{+path}","compare_url":"https://api.github.com/repos/vvuk/ipc-channel/compare/{base}...{head}","merges_url":"https://api.github.com/repos/vvuk/ipc-channel/merges","archive_url":"https://api.github.com/repos/vvuk/ipc-channel/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/vvuk/ipc-channel/downloads","issues_url":"https://api.github.com/repos/vvuk/ipc-channel/issues{/number}","pulls_url":"https://api.github.com/repos/vvuk/ipc-channel/pulls{/number}","milestones_url":"https://api.github.com/repos/vvuk/ipc-channel/milestones{/number}","notifications_url":"https://api.github.com/repos/vvuk/ipc-channel/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/vvuk/ipc-channel/labels{/name}","releases_url":"https://api.github.com/repos/vvuk/ipc-channel/releases{/id}","deployments_url":"https://api.github.com/repos/vvuk/ipc-channel/deployments","created_at":"2015-09-18T19:54:22Z","updated_at":"2015-09-18T19:54:22Z","pushed_at":"2016-11-13T17:04:07Z","git_url":"git://github.com/vvuk/ipc-channel.git","ssh_url":"git@github.com:vvuk/ipc-channel.git","clone_url":"https://github.com/vvuk/ipc-channel.git","svn_url":"https://github.com/vvuk/ipc-channel","homepage":"","size":476,"stargazers_count":0,"watchers_count":0,"language":"Rust","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"servo:master","ref":"master","sha":"357abb967bc4c703af5d4a9f95e2958317e9e8a1","user":{"login":"servo","id":2566135,"avatar_url":"https://avatars.githubusercontent.com/u/2566135?v=3","gravatar_id":"","url":"https://api.github.com/users/servo","html_url":"https://github.com/servo","followers_url":"https://api.github.com/users/servo/followers","following_url":"https://api.github.com/users/servo/following{/other_user}","gists_url":"https://api.github.com/users/servo/gists{/gist_id}","starred_url":"https://api.github.com/users/servo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/servo/subscriptions","organizations_url":"https://api.github.com/users/servo/orgs","repos_url":"https://api.github.com/users/servo/repos","events_url":"https://api.github.com/users/servo/events{/privacy}","received_events_url":"https://api.github.com/users/servo/received_events","type":"Organization","site_admin":false},"repo":{"id":37943142,"name":"ipc-channel","full_name":"servo/ipc-channel","owner":{"login":"servo","id":2566135,"avatar_url":"https://avatars.githubusercontent.com/u/2566135?v=3","gravatar_id":"","url":"https://api.github.com/users/servo","html_url":"https://github.com/servo","followers_url":"https://api.github.com/users/servo/followers","following_url":"https://api.github.com/users/servo/following{/other_user}","gists_url":"https://api.github.com/users/servo/gists{/gist_id}","starred_url":"https://api.github.com/users/servo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/servo/subscriptions","organizations_url":"https://api.github.com/users/servo/orgs","repos_url":"https://api.github.com/users/servo/repos","events_url":"https://api.github.com/users/servo/events{/privacy}","received_events_url":"https://api.github.com/users/servo/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/servo/ipc-channel","description":"A multiprocess drop-in replacement for Rust channels","fork":false,"url":"https://api.github.com/repos/servo/ipc-channel","forks_url":"https://api.github.com/repos/servo/ipc-channel/forks","keys_url":"https://api.github.com/repos/servo/ipc-channel/keys{/key_id}","collaborators_url":"https://api.github.com/repos/servo/ipc-channel/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/servo/ipc-channel/teams","hooks_url":"https://api.github.com/repos/servo/ipc-channel/hooks","issue_events_url":"https://api.github.com/repos/servo/ipc-channel/issues/events{/number}","events_url":"https://api.github.com/repos/servo/ipc-channel/events","assignees_url":"https://api.github.com/repos/servo/ipc-channel/assignees{/user}","branches_url":"https://api.github.com/repos/servo/ipc-channel/branches{/branch}","tags_url":"https://api.github.com/repos/servo/ipc-channel/tags","blobs_url":"https://api.github.com/repos/servo/ipc-channel/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/servo/ipc-channel/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/servo/ipc-channel/git/refs{/sha}","trees_url":"https://api.github.com/repos/servo/ipc-channel/git/trees{/sha}","statuses_url":"https://api.github.com/repos/servo/ipc-channel/statuses/{sha}","languages_url":"https://api.github.com/repos/servo/ipc-channel/languages","stargazers_url":"https://api.github.com/repos/servo/ipc-channel/stargazers","contributors_url":"https://api.github.com/repos/servo/ipc-channel/contributors","subscribers_url":"https://api.github.com/repos/servo/ipc-channel/subscribers","subscription_url":"https://api.github.com/repos/servo/ipc-channel/subscription","commits_url":"https://api.github.com/repos/servo/ipc-channel/commits{/sha}","git_commits_url":"https://api.github.com/repos/servo/ipc-channel/git/commits{/sha}","comments_url":"https://api.github.com/repos/servo/ipc-channel/comments{/number}","issue_comment_url":"https://api.github.com/repos/servo/ipc-channel/issues/comments{/number}","contents_url":"https://api.github.com/repos/servo/ipc-channel/contents/{+path}","compare_url":"https://api.github.com/repos/servo/ipc-channel/compare/{base}...{head}","merges_url":"https://api.github.com/repos/servo/ipc-channel/merges","archive_url":"https://api.github.com/repos/servo/ipc-channel/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/servo/ipc-channel/downloads","issues_url":"https://api.github.com/repos/servo/ipc-channel/issues{/number}","pulls_url":"https://api.github.com/repos/servo/ipc-channel/pulls{/number}","milestones_url":"https://api.github.com/repos/servo/ipc-channel/milestones{/number}","notifications_url":"https://api.github.com/repos/servo/ipc-channel/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/servo/ipc-channel/labels{/name}","releases_url":"https://api.github.com/repos/servo/ipc-channel/releases{/id}","deployments_url":"https://api.github.com/repos/servo/ipc-channel/deployments","created_at":"2015-06-23T20:28:18Z","updated_at":"2016-11-13T05:46:08Z","pushed_at":"2016-11-13T17:04:07Z","git_url":"git://github.com/servo/ipc-channel.git","ssh_url":"git@github.com:servo/ipc-channel.git","clone_url":"https://github.com/servo/ipc-channel.git","svn_url":"https://github.com/servo/ipc-channel","homepage":"","size":504,"stargazers_count":120,"watchers_count":120,"language":"Rust","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":27,"mirror_url":null,"open_issues_count":26,"forks":27,"open_issues":26,"watchers":120,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/servo/ipc-channel/pulls/108"},"html":{"href":"https://github.com/servo/ipc-channel/pull/108"},"issue":{"href":"https://api.github.com/repos/servo/ipc-channel/issues/108"},"comments":{"href":"https://api.github.com/repos/servo/ipc-channel/issues/108/comments"},"review_comments":{"href":"https://api.github.com/repos/servo/ipc-channel/pulls/108/comments"},"review_comment":{"href":"https://api.github.com/repos/servo/ipc-channel/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/servo/ipc-channel/pulls/108/commits"},"statuses":{"href":"https://api.github.com/repos/servo/ipc-channel/statuses/7e410806e9b18b9cf7243cd040f06fefe5267e51"}}}}
{"actor":{"display_login":"vvuk"}}