61

I want to use serial com port for communication and I want to implement a timeout every time I call the read() function call.

int filedesc = open( "dev/ttyS0", O_RDWR );

read( filedesc, buff, len );

EDIT:

I'm using Linux OS. How to implement using select function call?

Rachid K.
  • 4,490
  • 3
  • 11
  • 30
domlao
  • 15,663
  • 34
  • 95
  • 134

5 Answers5

89

select() takes 5 parameters, first the highest file descriptor + 1, then a fd_set for read, one for write and one for exceptions. The last parameter is a struct timeval, used for timeout. It return -1 on error, 0 on timeout or the number of file descriptors in the sets that are set.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>

int main(void)
{
  fd_set set;
  struct timeval timeout;
  int rv;
  char buff[100];
  int len = 100;
  int filedesc = open( "dev/ttyS0", O_RDWR );

  FD_ZERO(&set); /* clear the set */
  FD_SET(filedesc, &set); /* add our file descriptor to the set */

  timeout.tv_sec = 0;
  timeout.tv_usec = 10000;

  rv = select(filedesc + 1, &set, NULL, NULL, &timeout);
  if(rv == -1)
    perror("select"); /* an error accured */
  else if(rv == 0)
    printf("timeout"); /* a timeout occured */
  else
    read( filedesc, buff, len ); /* there was data to read */
  close(filedesc);
}
ceph3us
  • 7,326
  • 3
  • 36
  • 43
Puppe
  • 4,995
  • 26
  • 27
  • 5
    This solution is not good enough because if we are waiting for 5 bytes but received in time only 1 the select will be ok with it and then we will block forever at the read. – Roskoto Sep 28 '12 at 13:05
  • 17
    No it wouldn't. If only one byte was available and you try to read 5, read() would not block, it would return 1 (number of bytes read). From the read man-page: "It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal." – Puppe Oct 10 '12 at 07:09
  • 1
    and how to "select" time and again, the second time will return immediately. I want to know why ? – kangear Feb 01 '15 at 04:02
  • 3
    @kangear see [`man select`](http://linux.die.net/man/2/select) and it will explain `timeval` timeout functionality. In short, it says _"Consider timeout to be undefined after select() returns."_ and that means you should not reuse it if you're not really sure what (and where) you are doing. – Sampo Sarrala - codidact.org Oct 18 '15 at 16:37
  • instead of select I would use poll. Code with poll is simpler also works OK for descriptors over 1024. However I have heard old versions of MacOS have bug in poll. – Nick Jan 22 '17 at 12:56
  • Another comment for timeout struct - the way it is done will trigger a bug under free bsd and MacOS. you need to set seconds and miliaeconds separately. – Nick Jan 22 '17 at 12:59
  • 1
    They original poster stated he was running linux, and the question is tagged with linux so I gave an answer that works on linux. – Puppe Jan 31 '17 at 05:54
  • The question asked for reading with timeout, not for a full implementation, his read call didn't check the return value either. Adding a check of the return value would make the example less clear I think. – Puppe Oct 06 '20 at 17:43
  • Note that the file descriptor may need to be opened with O_NONBLOCK to prevent blocking when fewer than the expected number of bytes are read. – Brian A. Henning Nov 28 '22 at 20:12
  • @BrianA.Henning That is incorrect, at least accorting to [man read](https://linux.die.net/man/2/read). read() attempts to read **up to** count bytes from file descriptor fd into the buffer starting at buf. On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now. – Puppe Nov 29 '22 at 06:54
  • @Puppe That wording doesn't exclude the case of read() blocking until the number of bytes requested have arrived. It probably depends on the behavior of the underlying device, but the OP refers to a TTY. At least in my case, a TTY opened with open(path, O_RDONLY) will block. See the section in that manpage regarding O_NONBLOCK and EAGAIN. – Brian A. Henning Nov 29 '22 at 18:07
  • @BrianA.Henning Not when used together with select, the man page for select specifically says "Those listed in readfds will be watched to see if characters become available for reading (more precisely, to see if a read will not block;". If read after select saying it won't, there is a bug or the implementation is not POSIX compliant. – Puppe Nov 30 '22 at 05:24
  • @Puppe The guarantee from `select` is only that characters are available; it makes no guarantees about _how many_ are available. `select` will return as soon as one character becomes available; If I try to read _more_ characters than are available (by passing `read` a _count_ greater than 1), the `read` will still block until _all_ the requested characters are read, unless O_NONBLOCK is set on the descriptor. You cannot tell `select` to wait until _n_ characters arrive, and `read` doesn't know whether `select` was called to know whether to block (and that's why O_NONBLOCK exists). – Brian A. Henning Nov 30 '22 at 14:50
  • In short, `select` blocks until _some_ data is available, and `read` blocks until _enough_ data is available (unless O_NONBLOCK is set). _some_ may not be _enough_. – Brian A. Henning Nov 30 '22 at 14:54
  • 1
    @BrianA.Henning is right. I ran tests to verify. Whether `read` returns with fewer bytes than requested is influenced by multiple factors. O_NONBLOCK is one factor. The tty's `VMIN` and `VTIME` are other factors. `select` ignores these factors and will _always_ return as soon as a single byte is available, even if `VMIN` is set to something greater than 1 (I tested). If you ask for more than `VMIN` bytes in your call to `read`, it will block until at least `VMIN` bytes are available, even after `select` returns (again, I tested). This contradicts how I read the documentation for `select()`. – Darryl Mar 14 '23 at 23:04
50

As an alternative to select(), for the specific case of a serial port (terminal) you can use tcsetattr() to put the file descriptor into non-canonical mode, with a read timeout.

To do this, unset the ICANON flag, and set the VTIME control character:

struct termios termios;

tcgetattr(filedesc, &termios);
termios.c_lflag &= ~ICANON; /* Set non-canonical mode */
termios.c_cc[VTIME] = 100; /* Set timeout of 10.0 seconds */
tcsetattr(filedesc, TCSANOW, &termios);

Note VTIME is measured in tenths of a second, and that the type used for it is typically an unsigned char, meaning that the maximum timeout is 25.5 seconds.

caf
  • 233,326
  • 40
  • 323
  • 462
  • 10
    In my case I also had to set VMIN to 0: `termios.c_cc[VMIN] = 0`. – João M. S. Silva Dec 04 '14 at 22:28
  • Supposing that we have two devices TxDev and RxDev communicating via UART at t=T1, TxDev start to send data, at this time RxDev doesn't reach the read instruction yet does the first Bytes sent by TxDev get lost?, or the RxDev has a buffer where it stores received data even read is not called ? – fedi Jul 01 '16 at 08:03
  • 1
    @fedi: Under Linux they'll be buffered by the kernel. – caf Jul 01 '16 at 12:37
  • Note that if you don't set VMIN to 0, then VTIME is the time *since the most recent byte was received* that will trigger a timeout. If you set VMIN to 0 then VTIME is the *overall* time (time since you called `read`) that will trigger a timeout. – Darryl Mar 14 '23 at 23:16
10

If you set the socket do operate in non-blocking mode, each call to read will read only the data currently available (if any). So this is effectively equal to an immediate timeout.

You can set non-blocking mode on a socket with a function like this:

int setnonblock(int sock) {
   int flags;
   flags = fcntl(sock, F_GETFL, 0);
   if (-1 == flags)
      return -1;
   return fcntl(sock, F_SETFL, flags | O_NONBLOCK);
}

(For more information about reading from non-blocking sockets see the read man page)

sth
  • 222,467
  • 53
  • 283
  • 367
4

You don't say what the OS is but if you're running under Linux, you could use the select call. It returns if there is something to read on the file descriptor or you can set it up so that it will timeout if there is nothing to read. The return code indicates which.

sizzzzlerz
  • 4,277
  • 3
  • 27
  • 35
4

The code below uses millisec timeouts per character. I use it in one of my project to read from COM port.

size_t TimeoutRead (int port, void*buf, size_t size, int mlsec_timeout)
{
    struct pollfd fd = { .fd = port, .events = POLLIN };

    size_t      bytesread = 0;

    while (poll (&fd, 1, mlsec_timeout) == 1)
    {
        int chunksize = read (port, buf + bytesread, size);
        if (chunksize == -1)
            return -1;

        bytesread += chunksize;
        size -= chunksize;

        if (size == 0)
            return bytesread;
    }

    // TODO: IsTimeout = true;
    return bytesread;
}
rick-rick-rick
  • 203
  • 3
  • 5