// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2020 Florian Fischer
#include <urcu.h>						 // for rcu_read_lock, rcu_read_unlock
#include <urcu/rculfhash.h>	 // for RCU lock-free hash table

#include <algorithm>	 // for find
#include <cstdlib>		 // for exit, EXIT_FAILURE, EXIT_SUC...
#include <functional>	 // for hash
#include <iostream>		 // for hash
#include <vector>			 // for vector

#include "Common.hpp"										 // for DIE
#include "CountingPrivateSemaphore.hpp"	 // for CPS
#include "Fiber.hpp"										 // for Fiber
#include "Runtime.hpp"									 // for Runtime
#include "emper.hpp"										 // for spawn

struct node {
	int value;
	struct cds_lfht_node node; /* Chaining in hash table */
};

using node_t = struct node;

auto main() -> int {
	Runtime runtime;
	struct cds_lfht* ht;
	std::vector<int> values = {
			-5, 42, 42, 36, 24,
	}; /* 42 is duplicated */

	// Allocate new hash table.
	ht = cds_lfht_new(1, 1, 0, CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, nullptr);
	if (!ht) {
		DIE_MSG("Error allocating hash table");
	}

	Fiber* verifier = Fiber::from([&]() {
		CPS cps;
		for (auto& value : values) {
			// add each value to the hash table
			spawn(
					[&ht, value] {
						auto* node = reinterpret_cast<node_t*>(malloc(sizeof(node_t)));
						if (!node) {
							DIE_MSG_ERRNO("allocating node failed");
						}

						cds_lfht_node_init(&node->node);
						node->value = value;
						size_t hash = std::hash<int>{}(value);

						rcu_read_lock();
						cds_lfht_add(ht, hash, &node->node);
						rcu_read_unlock();
					},
					cps);
		}

		// Wait for the adders to finish
		cps.wait();

		// Verify the content of the hash table.
		// Iterate over each hash table node.
		// Iteration needs to be performed within RCU read-side critical section.
		struct cds_lfht_iter iter;
		node_t* node;
		rcu_read_lock();
		size_t i = 0;
		cds_lfht_for_each_entry(ht, &iter, node, node) {
			++i;
			auto it = std::find(values.begin(), values.end(), node->value);
			if (it == values.end()) {
				std::cerr << "value: " << node->value << " not found in cds_lfht" << std::endl;
				exit(EXIT_FAILURE);
			}
		}
		rcu_read_unlock();

		if (i != values.size()) {
			std::cerr << "number of values in cds_lfht: " << i
								<< " differ number of added ones: " << values.size() << std::endl;
			exit(EXIT_FAILURE);
		}

		exit(EXIT_SUCCESS);
	});

	runtime.scheduleFromAnywhere(*verifier);
	runtime.waitUntilFinished();

	return EXIT_FAILURE;
}