#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define LOOPS 100000000
#define BYTES 8192

static void
writer(int fd)
{
	char buffer1[BYTES];
	char buffer2[BYTES];

	memset(buffer1, '1', sizeof(buffer1));
	memset(buffer2, '2', sizeof(buffer2));

	for (int i = 0; i < LOOPS; ++i) {
		if (pwrite(fd, i % 2 == 0 ? buffer1 : buffer2, BYTES, 0) != BYTES) {
			perror("pwrite failed");
			break;
		}
		if (ftruncate(fd, 0) != 0)
		{
			perror("ftruncate failed");
			break;
		}
	}

	close(fd);
}

static void
reader(int readerno, int fd)
{
	char buffer[BYTES];
	char buffer1[BYTES];
	char buffer2[BYTES];

	memset(buffer1, '1', sizeof(buffer1));
	memset(buffer2, '2', sizeof(buffer2));

	for (int i = 0; i < LOOPS; ++i) {
		ssize_t r;

		r = pread(fd, buffer, BYTES, 0);
		if (r == 0 || r == 4096)
			continue;
		else if (r != BYTES) {
			fprintf(stderr, "[%d] perror: returned %zd: %m\n",
					readerno, r);
		}
		if (memcmp(buffer, buffer1, BYTES) != 0 &&
			memcmp(buffer, buffer2, BYTES) != 0) {
			char b0 = buffer[0];
			if (b0 == 0)
			{
				printf("found 0\n");
			}
			for (int i = 1; i < BYTES; ++i)
			{
				if (b0 != buffer[i])
				{
					fprintf(stderr, "[%d] difference at byte %d, byte before: %02x, byte: %02x\n",
							readerno, i, buffer[i - 1], buffer[i]);
					break;
				}
			}
		}
	}
	close(fd);

	return;
}

int main(int argc, char *argv[])
{
#define NCHILDREN 80
	int childpids[NCHILDREN];
	int fd;

	fd = open("test-file", O_RDWR | O_CREAT, 0644);
	if (fd < 0) {
		perror("open");
		exit(EXIT_FAILURE);
	}

	for (int i = 0; i < NCHILDREN; i++)
	{
		int pid = fork();

		if (pid < 0) {
			fprintf(stderr, "fork failed\n");
			return EXIT_FAILURE;
		}
		else if (pid == 0)
		{
			reader(i, fd);
			fprintf(stderr, "[%d] reader done\n", i);
			exit(0);
		}
		else
		{
			childpids[i] = pid;
		}
	}

	int status;

	writer(fd);
	fprintf(stderr, "writer done, killing readers\n");

	for (int i = 0; i < NCHILDREN; i++)
		kill(childpids[i], SIGTERM);

	for (int i = 0; i < NCHILDREN; i++)
		waitpid(childpids[i], &status, 0);

	return status;
}
