#define _GNU_SOURCE 1
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>
#include <errno.h>
#include <netinet/ip.h>

#define USE_DOUBLE_POLL 1
#define USE_TRY_NO_POLL 0


#define DIE() die(__LINE__)

static void die(int line)
{
 char *buf = NULL;

 asprintf(&buf, "%d:", line);
 if (buf)
   perror(buf);
 exit (EXIT_FAILURE);
}

#define EV_WRITE 1
#define EV_READ 2
#define EV_POLL_WRITE 3
#define EV_POLL_READ 4

#if USE_TRY_NO_POLL
# define EV_AFTER_READ EV_WRITE
# define EV_AFTER_WRITE EV_READ
#else
# define EV_AFTER_READ EV_POLL_WRITE
# define EV_AFTER_WRITE EV_POLL_READ
#endif

#define EV_IS_WRITE(x) ((x) & 1)

static int do_event(int fd, int event)
{
 char buf[1];
 int ret = 0;
 
 switch (event)
 {
  case EV_WRITE:
    if ((ret = write(fd, "a", 1)) == -1)
    {
     if (errno != EAGAIN)
       DIE();
     event = EV_POLL_WRITE;
    }
    else
      event = EV_AFTER_WRITE;
    break;
    
  case EV_READ:
    if ((ret = read(fd, buf, 1)) == -1)
    {
     if (errno == EPIPE)
       exit (EXIT_SUCCESS);
     if (errno != EAGAIN)
       DIE();
     event = EV_POLL_READ;
    }
    else
      event = EV_AFTER_READ;
    break;
    
  case EV_POLL_WRITE:
  {
   struct pollfd p;
   p.fd = fd;
   p.events = POLLOUT;
   p.revents = 0;
   
   if (!USE_DOUBLE_POLL || !(ret = poll(&p, 1, 0)))
     ret = poll(&p, 1, -1);
   if (ret != 1)
     DIE();
   event = EV_WRITE;
  }
  break;
  
  case EV_POLL_READ:
  {
   struct pollfd p;

   p.fd = fd;
   p.events = POLLIN;
   p.revents = 0;
   
   if (!USE_DOUBLE_POLL || !(ret = poll(&p, 1, 0)))
     ret = poll(&p, 1, -1);
   if (ret != 1)
     DIE();
   event = EV_READ;
  }
  break;
 }

 return (event);
}

static void go_parent(int fd)
{
 int event = EV_WRITE;
 unsigned int count = 100000;

 while (count)
 {
  int write_event = EV_IS_WRITE(event);
  
  event = do_event(fd, event);

  if (!!write_event != !!EV_IS_WRITE(event))
    --count;
 } 
}

static int go_chld(int fd)
{
 int event = EV_WRITE;

 while (1)  
   event = do_event(fd, event);
}

int main(void)
{
 int fds[2];
 pid_t chld = 0;
 
 if (socketpair(PF_LOCAL, SOCK_STREAM, IPPROTO_IP, fds) == -1)
   DIE();
 
 if ((chld = fork()) == -1)
   DIE();
 
 if (chld)
   go_parent(fds[0]);
 else
   go_chld(fds[1]);

 exit (EXIT_SUCCESS);
}
