Waiting for data on a file descriptor

As is often the case, polling for PPS data is a bad idea. It's much better to wait for PPS data on a file descriptor, using one of the following mechanisms:

A blocking read() is the simplest mechanism. Generally, you use ionotify() if you want to combine input from file descriptors with QNX Neutrino messaging in the same process. Use poll() when you're handling multiple file descriptors from sockets, pipes, serial ports, and so on. These mechanisms are described below; for examples, see Subscribers in the Examples appendix.

CAUTION:
By default, reads to PPS objects are nonblocking; that is, PPS defaults a normal open() to O_NONBLOCK, so that reads made by the client that opened the object don't block.

This behavior is atypical for most filesystems. It's done this way so that standard utilities won't hang waiting for a change when they make a read() call on a file. For example, with the default behavior, you could tar up the entire state of PPS using the standard tar utility. Without this default behavior, tar would never make it past the first file opened and read.

Using blocking reads

A blocking read() waits until the object or its attributes change, and then returns data. To have reads block, you need to open the object with the ?wait pathname open option, appended as a suffix to the pathname for the object. For example, to open the PlayList object:
  • for the default nonblocking reads, use the pathname: "/pps/media/PlayList"
  • for blocking reads, use the pathname plus the option: "/pps/media/PlayList?wait"

For more information on the ?wait option, see Pathname open options.”

A typical loop in a subscriber would live in its own thread. For a subscriber that opened the object with the ?wait option, this loop might do the following:

/* Assume that the object was opened with the ?wait option
   No error checking in this example. */
for(;;) {
    read(fd, buf, sizeof(buf)); // Read waits until the object changes.
    process(buf);
}

If you opened an object without the ?wait option and want to change to blocking reads, you can clear the O_NONBLOCK bit by using fcntl():

flags = fcntl(fd, F_GETFL);
flags &= ~O_NONBLOCK;
fcntl(fd, F_SETFL, flags);

or ioctl():

int i=0;
ioctl(fd,FIONBIO,&i);

Using ionotify()

The PPS service implements ionotify() functionality, allowing subscribers to request notification via a pulse, signal, semaphore, etc. On notification of a change, you must issue a read() to the object file to get the contents of the object. For example:

/* Process events while there are some */
while ( ( flags = ionotify( fd, _NOTIFY_ACTION_POLLARM, 
                            _NOTIFY_COND_INPUT, event ) != -1 )
          && (flags & _NOTIFY_COND_INPUT) )
{
    nbytes = read(fd, buf, sizeof(buf));
    if ( nbytes > 0 )
        process(buf);
}
/* If flags != -1, the event will be triggered in the future to get
   our attention */

For more information, see the entry for ionotify() in the QNX Neutrino C Library Reference.

Using poll()

The poll() function examines a set of file descriptors to see if they're ready for reading or writing. To use poll() with PPS, set up an array of struct pollfd that includes the file descriptor for the PPS object. You can optionally set a time limit. For example:

readfds[0].fd = pps_fd;
readfds[0].events = POLLIN;

switch ( ret = poll(readfds, 1, timeout ) )
{
   case -1:
      /* An error occurred. */
      break;
   case  0:
      /* poll() timed out. */
      break;
   default:
      if( readfds[0].revents & POLLRDNORM )
      {
         num_bytes = read( pps_fd, buf, sizeof(buf) );
         if (num_bytes > 0)
         {
            process(buf);
         }
      }
}

For more information, see the entry for poll() in the C Library Reference.