Windows nonblocking write: 1 byte at a time to know when buffer fills.

This commit is contained in:
Martin Ling 2013-11-25 15:21:19 +00:00
parent a3cb91f5e1
commit 0765af5644
1 changed files with 23 additions and 26 deletions

View File

@ -64,7 +64,7 @@ struct sp_port {
#ifdef _WIN32 #ifdef _WIN32
HANDLE hdl; HANDLE hdl;
OVERLAPPED write_ovl; OVERLAPPED write_ovl;
BYTE *write_buf; BYTE pending_byte;
BOOL writing; BOOL writing;
#else #else
int fd; int fd;
@ -627,7 +627,6 @@ enum sp_return sp_open(struct sp_port *port, enum sp_mode flags)
sp_close(port); sp_close(port);
RETURN_FAIL("CreateEvent() failed"); RETURN_FAIL("CreateEvent() failed");
} }
port->write_buf = NULL;
port->writing = FALSE; port->writing = FALSE;
} }
@ -698,9 +697,6 @@ enum sp_return sp_close(struct sp_port *port)
RETURN_FAIL("CloseHandle() failed"); RETURN_FAIL("CloseHandle() failed");
port->hdl = INVALID_HANDLE_VALUE; port->hdl = INVALID_HANDLE_VALUE;
if (port->nonblocking) { if (port->nonblocking) {
if (port->writing)
/* Write should have been stopped by closing the port, so safe to free buffer. */
free(port->write_buf);
/* Close event handle created for overlapped writes. */ /* Close event handle created for overlapped writes. */
if (CloseHandle(port->write_ovl.hEvent) == 0) if (CloseHandle(port->write_ovl.hEvent) == 0)
RETURN_FAIL("CloseHandle() failed"); RETURN_FAIL("CloseHandle() failed");
@ -791,6 +787,7 @@ enum sp_return sp_write(struct sp_port *port, const void *buf, size_t count)
#ifdef _WIN32 #ifdef _WIN32
DWORD written = 0; DWORD written = 0;
BYTE *ptr = (BYTE *) buf;
if (port->nonblocking) { if (port->nonblocking) {
/* Non-blocking write. */ /* Non-blocking write. */
@ -800,8 +797,6 @@ enum sp_return sp_write(struct sp_port *port, const void *buf, size_t count)
if (HasOverlappedIoCompleted(&port->write_ovl)) { if (HasOverlappedIoCompleted(&port->write_ovl)) {
DEBUG("Previous write completed"); DEBUG("Previous write completed");
port->writing = 0; port->writing = 0;
free(port->write_buf);
port->write_buf = NULL;
} else { } else {
DEBUG("Previous write not complete"); DEBUG("Previous write not complete");
/* Can't take a new write until the previous one finishes. */ /* Can't take a new write until the previous one finishes. */
@ -809,29 +804,31 @@ enum sp_return sp_write(struct sp_port *port, const void *buf, size_t count)
} }
} }
/* Copy user buffer. */ /* Keep writing data until the OS has to actually start an async IO for it.
if (!(port->write_buf = malloc(count))) * At that point we know the buffer is full. */
RETURN_ERROR(SP_ERR_MEM, "buffer copy malloc failed"); while (written < count)
memcpy(port->write_buf, buf, count); {
/* Copy first byte of user buffer. */
port->pending_byte = *ptr++;
/* Start asynchronous write. */ /* Start asynchronous write. */
if (WriteFile(port->hdl, buf, count, NULL, &port->write_ovl) == 0) { if (WriteFile(port->hdl, &port->pending_byte, 1, NULL, &port->write_ovl) == 0) {
if (GetLastError() == ERROR_IO_PENDING) { if (GetLastError() == ERROR_IO_PENDING) {
DEBUG("Asynchronous write started"); DEBUG("Asynchronous write started");
port->writing = 1; port->writing = 1;
RETURN_VALUE("%d", count); RETURN_VALUE("%d", ++written);
} else {
/* Actual failure of some kind. */
RETURN_FAIL("WriteFile() failed");
}
} else { } else {
free(port->write_buf); DEBUG("Single byte written immediately.");
port->write_buf = NULL; written++;
/* Actual failure of some kind. */
RETURN_FAIL("WriteFile() failed");
} }
} else {
DEBUG("Write completed immediately");
free(port->write_buf);
port->write_buf = NULL;
RETURN_VALUE("%d", count);
} }
DEBUG("All bytes written immediately.");
} else { } else {
/* Blocking write. */ /* Blocking write. */
if (WriteFile(port->hdl, buf, count, &written, NULL) == 0) { if (WriteFile(port->hdl, buf, count, &written, NULL) == 0) {