|
Home > Archive > Unix Programming > September 2007 > Recalculate timeout after select() when reading from serial port?
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
Recalculate timeout after select() when reading from serial port?
|
|
| Jef Driesen 2007-09-07, 7:24 am |
| I'm writing an application that needs to communicate with a device
attached to a serial port. For reading data, I want to be able to
specify a total timeout value.
The code I'm using now (see below) assumes that select() modifies the
timeout structure to indicate the remaining time. This works on my linux
system, but according to the man pages, this is not portable. So I want
to recalculate the timeout myself. My first idea was to use the
gettimeofday() function. But that function does calculate the elapsed
real time, not the system time spend in the select call. This results in
a different behavior when the select call is interrupted by a signal
(which is detected as in the helper function myselect() below). Is there
a better way to recalculate the timeout?
int g_timeout = 1000O; // Timeout in milliseconds
int serial_read (int fd, void* buffer, unsigned int count)
{
// Initialize the file descriptor set.
fd_set fds;
FD_ZERO (&fds);
FD_SET (fd, &fds);
// Initialize the timeout.
struct timeval tv
tv.tv_sec = (g_timeout / 1000);
tv.tv_usec = (g_timeout % 1000) * 1000;
int nbytes = 0;
while (nbytes < count) {
// See if there is data available.
int rc = select (fd + 1, &fds, NULL, NULL, &tv);
// TODO: Recalculate timeout here!
if (rc < 0) {
return -1; // Error during select call.
} else if (rc == 0)
break; // Timeout.
// Read the available data.
int n = read (fd, buffer + nbytes, count - nbytes);
if (n < 0) {
return -1; // Error during read call.
} else if (n == 0)
break; // EOF reached.
// Increase the number of bytes read.
nbytes += n;
}
// Return the number of bytes read.
return nbytes;
}
int myread (int fd, void* buffer, unsigned int count)
{
int rc = 0;
do {
rc = read (fd, buffer, count);
} while (rc < 0 && errno == EINTR)
return rc;
}
int myselect (int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout)
{
int rc = 0;
do {
rc = select (nfds, readfds, writefds, exceptfds, timeout);
// TODO: Recalculate timeout here!
} while (rc < 0 && errno == EINTR)
return rc;
}
| |
| Alex Fraser 2007-09-07, 7:36 pm |
| "Jef Driesen" <jefdriesen@hotmail.com.invalid> wrote in message
news:fbr7lf$r2o$1@ikaria.belnet.be...
> I'm writing an application that needs to communicate with a device
> attached to a serial port. For reading data, I want to be able to specify
> a total timeout value.
>
> The code I'm using now (see below) assumes that select() modifies the
> timeout structure to indicate the remaining time. This works on my linux
> system, but according to the man pages, this is not portable.
If you're not multiplexing multiple descriptors, the simplest solution is to
use alarm() and a blocking read(). The SIGALRM handler need only set a flag,
which you can test if read() indicates EINTR.
> So I want to recalculate the timeout myself. My first idea was to use the
> gettimeofday() function. But that function does calculate the elapsed real
> time, not the system time spend in the select call. This results in a
> different behavior when the select call is interrupted by a signal (which
> is detected as in the helper function myselect() below).
Using gettimeofday() should generally work fine (I write "generally" because
the time it returns is not necessarily monotonic). How did you try to use
it? You should call gettimeofday() once and compute when you should timeout
(expiry = now + timeout), then call it just before each call to select() and
compute the remaining time (select_timeout = expiry - now).
> Is there a better way to recalculate the timeout?
I did once wonder about using setitimer()/getitimer() to make a
high-resolution, monotonic counter (because monotonicity is usually
desirable for timeouts), but the principle is no different to using
gettimeofday().
Alex
| |
| Chris Friesen 2007-09-07, 7:36 pm |
| Alex Fraser wrote:
> Using gettimeofday() should generally work fine (I write "generally" because
> the time it returns is not necessarily monotonic).
clock_gettime() can be used to get a monotonic timestamp.
Chris
| |
| Alex Fraser 2007-09-07, 7:36 pm |
| "Chris Friesen" <cbf123@mail.usask.ca> wrote in message
news:46E1A63B.8020403@mail.usask.ca...
> Alex Fraser wrote:
>
> clock_gettime() can be used to get a monotonic timestamp.
I was under the impression that CLOCK_MONOTONIC is not widely supported,
while the alternative I considered (interval timers) was reasonably well
supported.
I would be very interested to hear if this is wrong.
Alex
| |
| Jef Driesen 2007-09-10, 7:17 am |
| Alex Fraser wrote:
> "Jef Driesen" <jefdriesen@hotmail.com.invalid> wrote in message
> news:fbr7lf$r2o$1@ikaria.belnet.be...
>
> If you're not multiplexing multiple descriptors, the simplest solution is to
> use alarm() and a blocking read(). The SIGALRM handler need only set a flag,
> which you can test if read() indicates EINTR.
As far as I know, a blocking read from a serial port is quite difficult
to do. I have no idea how to change the settings c_cc[VMIN] and
c_cc[VTIME] of the termios structure to do that. The termios structure
is also not very flexible to implement timeouts, and that's why I used
select().
Note: My application needs to run on windows too, and for that reason
I'm writing a tiny wrapper library for the serial communication, hiding
the platform specific stuff. For a consistent result on all platforms,
the behavior of the timeouts should be as close as possible to the win32
COMMTIMEOUTS [1].
[1] http://msdn2.microsoft.com/en-US/library/aa363190.aspx
>
> Using gettimeofday() should generally work fine (I write "generally" because
> the time it returns is not necessarily monotonic). How did you try to use
> it? You should call gettimeofday() once and compute when you should timeout
> (expiry = now + timeout), then call it just before each call to select() and
> compute the remaining time (select_timeout = expiry - now).
I implemented it almost exactly as you wrote it, with the exception that
I calculate the remaining time *after* the select call.
>
> I did once wonder about using setitimer()/getitimer() to make a
> high-resolution, monotonic counter (because monotonicity is usually
> desirable for timeouts), but the principle is no different to using
> gettimeofday().
|
|
|
|
|