#include <stdbool.h>
#include <stdatomic.h>
#include <pthread.h>
#include <time.h>		// timespec, nanosleep
#include <unistd.h>		// sysconf
#include <stdio.h>		// printf
#include <stdlib.h>		// exit
#include <fcntl.h>		// O_READ
#include <sys/mman.h>	// mmap
#include <string.h>		// strlen
#include <errno.h>		// errno

#define eprintf(args...) fprintf(stderr, ##args)

const char *DEVICE_PATH = "/dev/my_shmem";
const struct timespec WAIT_DURATION = {
	.tv_nsec = 100000000, // 0.1s
	.tv_sec = 0,
};

static atomic_bool timeout;

void *pthread_fn(void *);

int main(int argc, char *argv[])
{
	const long PAGE_SIZE = sysconf(_SC_PAGESIZE);
	if (PAGE_SIZE == -1) {
		eprintf("Error retrieving page size: %d.\n", errno);
		exit(EXIT_FAILURE);
	}

	// open device file w/ RW perms
	FILE *fp = fopen(DEVICE_PATH, "r+");
	if (!fp) {
		eprintf("Error opening device %s: %d.\n", DEVICE_PATH, errno);
		exit(EXIT_FAILURE);
	}
	int fd = fileno(fp);
	if (fd == -1) {
		eprintf("Error retrieving file descriptor: %d.\n", errno);
		exit(EXIT_FAILURE);
	}

	// mmap device
	void *buf = mmap(NULL, PAGE_SIZE * 2, PROT_READ,
		MAP_SHARED, fd, 0);
	if (!buf) {
		eprintf("Error mmap-ing: %d.\n", errno);
		exit(EXIT_FAILURE);
	}

	// Read from mmap-ed device
	pthread_t thread_handle;
	int ret_pth_create = pthread_create(
		&thread_handle, NULL, &pthread_fn, buf);
	if (ret_pth_create) {
		eprintf("Error pthread_create: %d.\n", ret_pth_create);
	}

	// Sleep for some time, then inform exit.
	nanosleep(&WAIT_DURATION, NULL);
	atomic_store(&timeout, true);

	// Join thread
	pthread_join(thread_handle, NULL);

	// Unmap device
	munmap(buf, PAGE_SIZE * 2);

	// Close device
	fclose(fp);

	printf("Finished!\n");
	exit(EXIT_SUCCESS);
}

void *pthread_fn(void *mmap_buf) {
	bool time_to_exit;
	char readbuf[5];

	for (;;) {
		time_to_exit = atomic_load(&timeout);
		if (time_to_exit) break;

		memcpy(readbuf, mmap_buf, 4);
		printf("Read 0x%x 0x%x 0x%x 0x%x.\n",
			readbuf[0], readbuf[1], readbuf[2], readbuf[3]);
	}

	return NULL;
}