packages feed

select-0.4: src/System/Posix/IO/Select/cbits.c

#define _POSIX_C_SOURCE 200112L
#include <stdlib.h>
#include <sys/select.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include "cbits.h"
#ifndef NDEBUG
#include <stdio.h>
#endif


int xselect(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout)
{
  struct timespec remaining;
  int ret;
  struct timespec start;
  struct timespec current;
  struct timespec stop;
  if (timeout != NULL)
  {
    remaining.tv_sec = timeout->tv_sec;
    remaining.tv_nsec = timeout->tv_usec*1000;
    clock_gettime(CLOCK_MONOTONIC, &start);
    stop.tv_sec = start.tv_sec + timeout->tv_sec;
    stop.tv_nsec = start.tv_nsec + timeout->tv_usec*1000;
  }
  fd_set readfds_orig = *readfds;
  fd_set writefds_orig = *writefds;
  fd_set exceptfds_orig = *exceptfds;

  for (;;)
  {
    #ifndef NDEBUG
    if (timeout != NULL)
    {
      printf("C: Remaining time: %ld s, %ld ns\n", remaining.tv_sec, remaining.tv_nsec);
      printf("C: Will select for %ld s, %ld us\n", timeout->tv_sec, timeout->tv_usec);
    }
    else
      printf("C: Will select forever.\n");
    #endif

    ret = select(nfds, readfds, writefds, exceptfds, timeout);

    if (ret == -1 && errno == EINTR)
    {
      #ifndef NDEBUG
      printf("C: Was interrupted.\n");
      #endif
      if (timeout != NULL) // Case of finite timeouts.
      {
	clock_gettime(CLOCK_MONOTONIC, &current);
	remaining.tv_sec = stop.tv_sec - current.tv_sec;
	remaining.tv_nsec = stop.tv_nsec - current.tv_nsec;

	while (remaining.tv_nsec > 1000000000)
	{
	  remaining.tv_sec++;
	  remaining.tv_nsec -= 1000000000;
	}
	while (remaining.tv_nsec < 0)
	{
	  remaining.tv_sec--;
	  remaining.tv_nsec += 1000000000;
	}
	if (remaining.tv_sec < 0)
	{
	  remaining.tv_sec = 0;
	  remaining.tv_nsec = 0;
	}

        #ifndef NDEBUG
	printf("C: Was interrupted with %ld s, %ld ns remaining\n", remaining.tv_sec, remaining.tv_nsec);
        #endif

	// Set up for retry.
	timeout->tv_sec = remaining.tv_sec;
	timeout->tv_usec = remaining.tv_nsec/1000;
      }
      // Set up for retry (also in infinite case).
      *readfds = readfds_orig;
      *writefds = writefds_orig;
      *exceptfds = exceptfds_orig;
      #ifndef NDEBUG
      printf("C: Retrying select.\n");
      #endif
    }
    else
      return ret;
  }

  return -2;   // Cannot happen.
}