/*
 *	ex_async - an example program showing how asynchronous I/O
 *		   can be performed from a user-level task.
 * 	J. M. Barton 2/25/89
 *
 * This program is herewith released to the public domain.  You may do
 * as you wish with this code.  The author (J. M. Barton) and his
 * employer (Silicon Graphics, Incorporated) assume no liability of any
 * kind in connection with the use or non-use of this code or any fragment
 * of it.  Use at your own risk.  Please retain this statement with all
 * copies or fragments of code taken from this program.
 */

# include	<sys/types.h>
# include	<sys/prctl.h>
# include	<sys/schedctl.h>
# include	<termio.h>
# include	<signal.h>
# include	<stdio.h>

# define	COMSIG		SIGUSR1
# define	RTPRI		NDPHIMAX

/*
 * Example strategy is to start a slave reading from the keyboard, and it
 * will signal the parent whenever a character is read, and the parent grabs
 * the character and echo's it, and then starts the child waiting again.
 * Program will work passing any file through on it's standard input, though.
 */

struct {		/* structure we communicate through */
	int	ppid;		/* parent process ID */
	int	cpid;		/* child process ID */
	int	fd;		/* file descriptor to read */
	char	byte;		/* acquired byte */
} comarea;
struct termio	tb;	/* original TTY state */

main()
{
   int			asyncslave();
   int			asyncintr();
   int			ttyclean();
   struct termio	ta;

	/*
	 * Just for fun, start the slave first.  He will block himself.
	 */
	comarea.ppid = getpid();
	comarea.cpid = sproc(asyncslave, PR_SALL, 0);

	/*
	 * Set up the terminal to return a single byte at a time.
	 */
	if (ioctl(0, TCGETA, &tb) != -1) {
		signal(SIGINT, ttyclean);
		ta = tb;
		ta.c_line = 0;
		ta.c_iflag = BRKINT|ICRNL;
		ta.c_lflag &= ~(ICANON|ECHO);
		ta.c_cc[VMIN] = 1;
		ta.c_cc[VTIME] = 0;
		ioctl(0, TCSETA, &ta);
	}

	/*
	 * Note that we can change the file descriptor at any time, such
	 * as opening a new file, and pass that to the child.  The sproc()
	 * facility insures that the child has exactly the same file
	 * descriptors as the parent.
	 */
	comarea.fd = 0;

	/*
	 * Set up the interrupt handler and let the slave go.
	 */
	sigset(COMSIG, asyncintr);
	sigrelse(COMSIG);
	unblockproc(comarea.cpid);

	/*
	 * Now just wait for the slave to read a character and then output
	 * it.
	 */
	sieve();
	/*NOTREACHED*/
}

/*
 * On interrupt, output the character that was read.
 */
asyncintr()
{
	putchar(comarea.byte);
	unblockproc(comarea.cpid);
}

/*
 * Loop forever reading characters from the input port and passing them
 * to the master.
 */
asyncslave()
{
	/*
	 * Put us at the highest possible real-time priority so
	 * we respond to events very quickly.
	 */
	schedctl(NDPRI, 0, RTPRI);

	for (;;) {
		/*
		 * Wait for command to read and do the read.  This simple
		 * protocol insures that we never trash the communications
		 * buffer.
		 */
		blockproc(0);
		if (read(comarea.fd, &comarea.byte, sizeof(char)) != 1) {
			/*
			 * Error reading from the descriptor.  Sink
			 * the parent.
			 */
			kill(comarea.ppid, SIGINT);
			exit(1);
		}

		/*
		 * Signal the parent that we got a valid byte.
		 */
		kill(comarea.ppid, COMSIG);
	}
	/*NOTREACHED*/
}

/*
 * Function to burn up time - sieve.
 */
# define true		1
# define false		0
# define size 		(256*1024)
# define sizep1		(size+1)

sieve()
{
	register int i,prime,k,count,iter;
	char flags[sizep1];

	for (iter = 0;; iter++) {
		count=0;
		for(i = 0; i <= size;i ++)
			flags[i] = true;
		for(i = 0;i <= size;i ++) {
			if(flags[i]) {
				prime = i + i + 3;
				k = i + prime;
				while(k <= size) {
					flags[k] = false;
					k += prime;
				}
				count = count + 1;
			}
		}

		/*
		 * Gaurantee that characters read from the keyboard don't
		 * mess up our output.
		 */
		sighold(COMSIG);
		printf(".");
		fflush(stdout);
		sigrelse(COMSIG);
	}
	/*NOTREACHED*/
}

/*
 * Clean up if we were attached to a tty.
 */

ttyclean()
{
	ioctl(0, TCSETA, &tb);
	exit(1);
}