From c1602ef5067891d6b6003bfeb8db757875047a47 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Sun, 7 Nov 2010 23:22:11 +0100 Subject: [PATCH] Simplify the timeout calculation. --- src/serial_posix.c | 212 ++++++++++++++++----------------------------- 1 file changed, 75 insertions(+), 137 deletions(-) diff --git a/src/serial_posix.c b/src/serial_posix.c index 5ba5018..3c4bff2 100644 --- a/src/serial_posix.c +++ b/src/serial_posix.c @@ -353,138 +353,72 @@ serial_set_queue_size (serial_t *device, unsigned int input, unsigned int output } -#define MYSELECT(nfds, fds, queue, timeout) \ - (queue == SERIAL_QUEUE_INPUT ? \ - select (nfds, fds, NULL, NULL, timeout) : \ - select (nfds, NULL, fds, NULL, timeout)) - - -static int -serial_poll_internal (int fd, int queue, long timeout, const struct timeval *timestamp) -{ - // Calculate the initial timeout, and obtain - // a timestamp for updating the timeout. - - struct timeval tvt, tve; - if (timeout > 0) { - // Calculate the initial timeout. - tvt.tv_sec = (timeout / 1000); - tvt.tv_usec = (timeout % 1000) * 1000; - // Calculate the timestamp. - if (timestamp == NULL) { - struct timeval now; - if (gettimeofday (&now, NULL) != 0) { - TRACE ("gettimeofday"); - return -1; - } - timeradd (&now, &tvt, &tve); - } else { - timeradd (timestamp, &tvt, &tve); - } - } else if (timeout == 0) { - timerclear (&tvt); - } - - // Wait until the file descriptor is ready for reading/writing, or - // the timeout expires. A file descriptor is considered ready for - // reading/writing when a call to an input/output function with - // O_NONBLOCK clear would not block, whether or not the function - // would transfer data successfully. - - fd_set fds; - FD_ZERO (&fds); - FD_SET (fd, &fds); - - int rc = 0; - while ((rc = MYSELECT (fd + 1, &fds, queue, timeout >= 0 ? &tvt : NULL)) == -1) { - if (errno != EINTR ) { - TRACE ("select"); - return -1; - } - - // According to the select() man pages, the file descriptor sets - // and the timeout become undefined after select() returns an error. - // We follow the advise and do not rely on their contents. - - FD_ZERO (&fds); - FD_SET (fd, &fds); - - // Calculate the remaining timeout. - - if (timeout > 0) { - struct timeval now; - if (gettimeofday (&now, NULL) != 0) { - TRACE ("gettimeofday"); - return -1; - } - if (timercmp (&now, &tve, >=)) - timerclear (&tvt); - else - timersub (&tve, &now, &tvt); - } else if (timeout == 0) { - timerclear (&tvt); - } - } - - return rc; -} - - int -serial_read (serial_t *device, void* data, unsigned int size) +serial_read (serial_t *device, void *data, unsigned int size) { if (device == NULL) return -1; // EINVAL (Invalid argument) + // The total timeout. long timeout = device->timeout; - struct timeval timestamp; - if (timeout > 0) { - if (gettimeofday (×tamp, NULL) != 0) { - TRACE ("gettimeofday"); - return -1; - } - } + // The absolute target time. + struct timeval tve; + int init = 1; unsigned int nbytes = 0; - for (;;) { - // Attempt to read data from the file descriptor. - int n = read (device->fd, (char*) data + nbytes, size - nbytes); - if (n < 0) { - if (errno == EINTR) - continue; // Retry. - if (errno != EAGAIN) { - TRACE ("read"); - return -1; // Error during read call. - } - } else { - nbytes += n; - if (!n || nbytes == size) - break; // Success or EOF. - } + while (nbytes < size) { + fd_set fds; + FD_ZERO (&fds); + FD_SET (device->fd, &fds); - // Wait until the file descriptor is ready for reading, or the timeout expires. - int rc = serial_poll_internal (device->fd, SERIAL_QUEUE_INPUT, timeout, timeout > 0 ? ×tamp : NULL); - if (rc < 0) { - return -1; // Error during select/poll call. - } else if (rc == 0) - break; // Timeout. - - // Calculate the remaining timeout. + struct timeval tvt; if (timeout > 0) { - struct timeval now, delta; + struct timeval now; if (gettimeofday (&now, NULL) != 0) { TRACE ("gettimeofday"); return -1; } - timersub (&now, ×tamp, &delta); - long elapsed = delta.tv_sec * 1000 + delta.tv_usec / 1000; - if (elapsed >= timeout) - timeout = 0; - else - timeout -= elapsed; - timestamp = now; - } + + if (init) { + // Calculate the initial timeout. + tvt.tv_sec = (timeout / 1000); + tvt.tv_usec = (timeout % 1000) * 1000; + // Calculate the target time. + timeradd (&now, &tvt, &tve); + } else { + // Calculate the remaining timeout. + if (timercmp (&now, &tve, <)) + timersub (&tve, &now, &tvt); + else + timerclear (&tvt); + } + init = 0; + } else if (timeout == 0) { + timerclear (&tvt); + } + + int rc = select (device->fd + 1, &fds, NULL, NULL, timeout >= 0 ? &tvt : NULL); + if (rc < 0) { + if (errno == EINTR) + continue; // Retry. + TRACE ("select"); + return -1; // Error during select call. + } else if (rc == 0) { + break; // Timeout. + } + + int n = read (device->fd, (char *) data + nbytes, size - nbytes); + if (n < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; // Retry. + TRACE ("read"); + return -1; // Error during read call. + } else if (n == 0) { + break; // EOF. + } + + nbytes += n; } return nbytes; @@ -492,34 +426,38 @@ serial_read (serial_t *device, void* data, unsigned int size) int -serial_write (serial_t *device, const void* data, unsigned int size) +serial_write (serial_t *device, const void *data, unsigned int size) { if (device == NULL) return -1; // EINVAL (Invalid argument) unsigned int nbytes = 0; - for (;;) { - // Attempt to write data to the file descriptor. - int n = write (device->fd, (char*) data + nbytes, size - nbytes); - if (n < 0) { + while (nbytes < size) { + fd_set fds; + FD_ZERO (&fds); + FD_SET (device->fd, &fds); + + int rc = select (device->fd + 1, NULL, &fds, NULL, NULL); + if (rc < 0) { if (errno == EINTR) continue; // Retry. - if (errno != EAGAIN) { - TRACE ("write"); - return -1; // Error during write call. - } - } else { - nbytes += n; - if (nbytes == size) - break; // Success. - } - - // Wait until the file descriptor is ready for writing, or the timeout expires. - int rc = serial_poll_internal (device->fd, SERIAL_QUEUE_OUTPUT, -1, NULL); - if (rc < 0) { - return -1; // Error during select/poll call. - } else if (rc == 0) + TRACE ("select"); + return -1; // Error during select call. + } else if (rc == 0) { break; // Timeout. + } + + int n = write (device->fd, (char *) data + nbytes, size - nbytes); + if (n < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; // Retry. + TRACE ("write"); + return -1; // Error during write call. + } else if (n == 0) { + break; // EOF. + } + + nbytes += n; } return nbytes;