Skip to content

Instantly share code, notes, and snippets.

@rprichard
Last active January 4, 2017 22:52
Show Gist options
  • Save rprichard/37bfd1dd6a2ef91ba7af49efbe0461ed to your computer and use it in GitHub Desktop.
Save rprichard/37bfd1dd6a2ef91ba7af49efbe0461ed to your computer and use it in GitHub Desktop.
Rust output collection bug
#include <windows.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <vector>
volatile LONG counter = 0;
static DWORD spew(void*) {
OVERLAPPED over = {};
const HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL);
assert(event != NULL);
const DWORD kBufSize = 1024 * 32;
std::vector<void*> buffers;
for (int i = 0; i < 100; ++i) {
void *buf = VirtualAlloc(NULL, kBufSize, MEM_COMMIT, PAGE_READWRITE);
assert(buf != NULL);
memset(buf, 'A', kBufSize);
buffers.push_back(buf);
}
while (!buffers.empty()) {
void *const buf = buffers.back();
buffers.pop_back();
DWORD actual = 0;
#if 1
BOOL ret = WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
buf, kBufSize, &actual, NULL);
#else
over = OVERLAPPED {};
over.hEvent = event;
BOOL ret = WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
buf, kBufSize, &actual, &over);
if (!ret && GetLastError() == ERROR_IO_PENDING) {
ret = GetOverlappedResult(GetStdHandle(STD_OUTPUT_HANDLE),
&over, &actual, TRUE);
}
#endif
assert(ret && "WriteFile failed");
//fprintf(stderr, "child: WriteFile reported %u bytes written\n", actual);
InterlockedExchangeAdd(&counter, actual);
const BOOL freeRet = VirtualFree(buf, 0, MEM_RELEASE);
assert(freeRet);
}
return 0;
}
static HANDLE create_thread() {
HANDLE h = CreateThread(NULL, 0, spew, NULL, 0, NULL);
assert(h != NULL);
return h;
}
int main() {
std::vector<HANDLE> handles;
for (int i = 0; i < 20; ++i) {
handles.push_back(create_thread());
}
for (HANDLE h : handles) {
WaitForSingleObject(h, INFINITE);
}
fprintf(stderr, "child: WriteFile reported %u bytes written total\n", counter);
}
use std::process::{Command, Stdio};
fn main() {
let c = Command::new(".\\child.exe")
// .stdout(Stdio::null())
// .stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.output()
.expect("failed to execute child");
let data = c.stdout;
println!("parent: child exited with: {}", c.status);
println!("parent: child wrote {} bytes, expected {}",
data.len(), 1024 * 32 * 100 * 20);
for b in data {
if b != b'A' {
println!("parent: found wrong byte: {}", b);
}
}
}
With normal non-overlapped I/O in the child, we see this:
C:\rprichard\mess\iotest>parent
child: WriteFile reported 37715968 bytes written total
parent: child exited with: exit code: 0
parent: child wrote 65372160 bytes, expected 65536000
C:\rprichard\mess\iotest>parent
child: WriteFile reported 35848192 bytes written total
parent: child exited with: exit code: 0
parent: child wrote 65110016 bytes, expected 65536000
C:\rprichard\mess\iotest>parent
child: WriteFile reported 34766848 bytes written total
parent: child exited with: exit code: 0
parent: child wrote 65503232 bytes, expected 65536000
C:\rprichard\mess\iotest>parent
child: WriteFile reported 35454976 bytes written total
parent: child exited with: exit code: 0
parent: child wrote 65372160 bytes, expected 65536000
If the child instead uses overlapped I/O, we see:
C:\rprichard\mess\iotest>parent
child: WriteFile reported 65536000 bytes written total
parent: child exited with: exit code: 0
parent: child wrote 65536000 bytes, expected 65536000
C:\rprichard\mess\iotest>parent
child: WriteFile reported 65536000 bytes written total
parent: child exited with: exit code: 0
parent: child wrote 65536000 bytes, expected 65536000
C:\rprichard\mess\iotest>parent
child: WriteFile reported 65536000 bytes written total
parent: child exited with: exit code: 0
parent: child wrote 65536000 bytes, expected 65536000
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment