Use the new timer for the timeout calculations

Replace the gettimeofday() based implementation with the new monotonic
timers. This makes the implementation more robust against unexpected
adjustments of the clock.
This commit is contained in:
Jef Driesen
2018-02-23 20:20:34 +01:00
parent a8adb16c0d
commit eb4c150024

View File

@@ -30,7 +30,6 @@
#include <fcntl.h> // fcntl #include <fcntl.h> // fcntl
#include <termios.h> // tcgetattr, tcsetattr, cfsetispeed, cfsetospeed, tcflush, tcsendbreak #include <termios.h> // tcgetattr, tcsetattr, cfsetispeed, cfsetospeed, tcflush, tcsendbreak
#include <sys/ioctl.h> // ioctl #include <sys/ioctl.h> // ioctl
#include <sys/time.h> // gettimeofday
#include <time.h> // nanosleep #include <time.h> // nanosleep
#ifdef HAVE_LINUX_SERIAL_H #ifdef HAVE_LINUX_SERIAL_H
#include <linux/serial.h> #include <linux/serial.h>
@@ -60,6 +59,7 @@
#include "iostream-private.h" #include "iostream-private.h"
#include "iterator-private.h" #include "iterator-private.h"
#include "descriptor-private.h" #include "descriptor-private.h"
#include "timer.h"
#define DIRNAME "/dev" #define DIRNAME "/dev"
@@ -99,6 +99,7 @@ typedef struct dc_serial_t {
*/ */
int fd; int fd;
int timeout; int timeout;
dc_timer_t *timer;
/* /*
* Serial port settings are saved into this variable immediately * Serial port settings are saved into this variable immediately
* after the port is opened. These settings are restored when the * after the port is opened. These settings are restored when the
@@ -290,6 +291,13 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name)
device->baudrate = 0; device->baudrate = 0;
device->nbits = 0; device->nbits = 0;
// Create a high resolution timer.
status = dc_timer_new (&device->timer);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to create a high resolution timer.");
goto error_free;
}
// Open the device in non-blocking mode, to return immediately // Open the device in non-blocking mode, to return immediately
// without waiting for the modem connection to complete. // without waiting for the modem connection to complete.
device->fd = open (name, O_RDWR | O_NOCTTY | O_NONBLOCK); device->fd = open (name, O_RDWR | O_NOCTTY | O_NONBLOCK);
@@ -297,7 +305,7 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name)
int errcode = errno; int errcode = errno;
SYSERROR (context, errcode); SYSERROR (context, errcode);
status = syserror (errcode); status = syserror (errcode);
goto error_free; goto error_timer_free;
} }
#ifndef ENABLE_PTY #ifndef ENABLE_PTY
@@ -327,6 +335,8 @@ dc_serial_open (dc_iostream_t **out, dc_context_t *context, const char *name)
error_close: error_close:
close (device->fd); close (device->fd);
error_timer_free:
dc_timer_free (device->timer);
error_free: error_free:
dc_iostream_deallocate ((dc_iostream_t *) device); dc_iostream_deallocate ((dc_iostream_t *) device);
return status; return status;
@@ -361,6 +371,8 @@ dc_serial_close (dc_iostream_t *abstract)
dc_status_set_error(&status, syserror (errcode)); dc_status_set_error(&status, syserror (errcode));
} }
dc_timer_free (device->timer);
return status; return status;
} }
@@ -682,11 +694,8 @@ dc_serial_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual
dc_serial_t *device = (dc_serial_t *) abstract; dc_serial_t *device = (dc_serial_t *) abstract;
size_t nbytes = 0; size_t nbytes = 0;
// The total timeout.
int timeout = device->timeout;
// The absolute target time. // The absolute target time.
struct timeval tve; dc_usecs_t target = 0;
int init = 1; int init = 1;
while (nbytes < size) { while (nbytes < size) {
@@ -694,35 +703,40 @@ dc_serial_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual
FD_ZERO (&fds); FD_ZERO (&fds);
FD_SET (device->fd, &fds); FD_SET (device->fd, &fds);
struct timeval tvt; struct timeval tv, *ptv = NULL;
if (timeout > 0) { if (device->timeout > 0) {
struct timeval now; dc_usecs_t timeout = 0;
if (gettimeofday (&now, NULL) != 0) {
int errcode = errno; dc_usecs_t now = 0;
SYSERROR (abstract->context, errcode); status = dc_timer_now (device->timer, &now);
status = syserror (errcode); if (status != DC_STATUS_SUCCESS) {
goto out; goto out;
} }
if (init) { if (init) {
// Calculate the initial timeout. // Calculate the initial timeout.
tvt.tv_sec = (timeout / 1000); timeout = device->timeout * 1000;
tvt.tv_usec = (timeout % 1000) * 1000;
// Calculate the target time. // Calculate the target time.
timeradd (&now, &tvt, &tve); target = now + timeout;
init = 0;
} else { } else {
// Calculate the remaining timeout. // Calculate the remaining timeout.
if (timercmp (&now, &tve, <)) if (now < target) {
timersub (&tve, &now, &tvt); timeout = target - now;
else } else {
timerclear (&tvt); timeout = 0;
}
} }
init = 0; tv.tv_sec = timeout / 1000000;
} else if (timeout == 0) { tv.tv_usec = timeout % 1000000;
timerclear (&tvt); ptv = &tv;
} else if (device->timeout == 0) {
tv.tv_sec = 0;
tv.tv_usec = 0;
ptv = &tv;
} }
int rc = select (device->fd + 1, &fds, NULL, NULL, timeout >= 0 ? &tvt : NULL); int rc = select (device->fd + 1, &fds, NULL, NULL, ptv);
if (rc < 0) { if (rc < 0) {
int errcode = errno; int errcode = errno;
if (errcode == EINTR) if (errcode == EINTR)