private: No


#define _GNU_SOURCE
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// An "arena" for experimenting with MAP_FIXED mappings with overcommit
// disabled
struct arena {
	void *base;
	size_t reserved;
	size_t committed;
};

struct arena a_new(size_t reserve_len) {
	struct arena a = {0, reserve_len, 0};
	a.base = mmap(NULL, reserve_len, PROT_NONE, MAP_PRIVATE
			| MAP_ANONYMOUS, -1, 0);
	if (a.base == MAP_FAILED) {
		perror("vm reserve");
		exit(1);
	}
	return a;
}

void *a_alloc(struct arena *a, size_t size) {
	if (a->committed + size >= a->reserved
			|| a->committed + size < a->committed) {
		return NULL;
	}
	void *res = mmap(a->base + a->committed, size, PROT_READ | PROT_WRITE,
			MAP_POPULATE | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
			-1, 0);
	if (res == MAP_FAILED) {
		perror("expand map");
		return NULL;
	}
	if (res != a->base + a->committed) {
		fprintf(stderr, "new map is at the wrong location\n");
		exit(1);
	}
	a->committed += size;
	return res;
}

void a_dealloc(struct arena *a, size_t size) {
	// Place a new PROT_NONE mapping over the end of the committed range
	void *res = mmap(a->base + a->committed - size, size, PROT_NONE,
			MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
	if (res == MAP_FAILED) {
		perror("decommit");
		exit(1);
	}
	if (res != a->base + a->committed - size) {
		fprintf(stderr, "new map is in wrong place\n");
		exit(1);
	}
	a->committed -= size;
}

void a_del(struct arena a) {
	munmap(a.base, a.reserved);
}



int main(int argc, char *argv[]) {
	const size_t gig = 1024 * 1024 * 1024;
	puts("reserving virtual memory range");
	struct arena a = a_new((size_t)128 * gig);

	sleep(10);

	puts("allocating memory");
	a_alloc(&a, gig);
	a_alloc(&a, gig);
	a_alloc(&a, gig);
	a_alloc(&a, gig);
	a_alloc(&a, gig);
	a_alloc(&a, gig);
	a_alloc(&a, gig);
	a_alloc(&a, gig);

	puts("done");
	sleep(10);

	puts("deallocating");
	a_dealloc(&a, 4 * gig);
	sleep(5);

	puts("reallocating");
	a_alloc(&a, 4 * gig);
	sleep(5);

	a_del(a);
	return 0;
}