/* COPYING ******************************************************************
For copyright and licensing terms, see the file named COPYING.
// **************************************************************************
*/

#include <cstring>
#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include "redo.h"
#include "jobserver.h"

/* The GNU make jobserver ***************************************************
// **************************************************************************
*/

namespace {

inline
int
set_non_blocking (
	int fd,
	bool v
) {
	int flags(fcntl(fd, F_GETFL));
	if (0 > flags) return flags;
	if (v) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK;
	return fcntl(fd, F_SETFL, flags);
}

unsigned implicit_jobs = 1;

bool
procure_slot_internal(
	const char * prog,
	int timeout
) {
	if (implicit_jobs) {
		--implicit_jobs;
		if (debug) {
			msg(prog, "INFO") << "Procured implicit job slot that is this process itself.\n";
		}
		return true;
	}

	if (-1 == jobserver::fds[0]) return false;
	while (true) {
		pollfd p;
		p.fd = jobserver::fds[0];
		p.events = POLLIN;
		const int r0(poll(&p, sizeof p/sizeof(pollfd), timeout));
		if ((r0 < 0) || !(p.revents & POLLIN)) return false;

		char c;
		const ssize_t r(read(jobserver::fds[0], &c, sizeof c));
		if (debug) {
			if (0 < r)
				msg(prog, "INFO") << "Procured a job slot from the jobserver.\n";
			else
				msg(prog, "INFO") << "Failed to procure a job slot from the jobserver.\n";
		}
		return 0 < r;
	}
}

}

int jobserver::fds[2] = { -1, -1 };

bool
jobserver::try_procure_slot(
	const char * prog
) {
	return procure_slot_internal(prog, 0);
}

bool
jobserver::procure_slot(
	const char * prog
) {
	return procure_slot_internal(prog, -1);
}

void
jobserver::vacate_slot(
	const char * prog
) {
	if (!implicit_jobs) {
		++implicit_jobs;
		if (debug) {
			msg(prog, "INFO") << "Vacated implicit job slot that is this process itself.\n";
		}
	} else
	if (-1 != fds[1]) {
		const char c('\0');
		write(fds[1], &c, sizeof c);
		if (debug) {
			msg(prog, "INFO") << "Vacated a job slot to the jobserver.\n";
		}
	}
}

int
jobserver::create_pipe(
) {
	const int rc(pipe(fds));
	if (0 <= rc) {
		set_non_blocking(fds[0], true);
		set_non_blocking(fds[1], true);
	}
	return rc;
}
