//
// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// kernel --
//
// Author           : Thierry Delisle
// Created On       : Tue Jan 17 12:27:26 2017
// Last Modified By : Peter A. Buhr
// Last Modified On : Tue Apr 10 14:46:49 2018
// Update Count     : 10
//

#pragma once

#include <stdbool.h>

#include "invoke.h"
#include "time_t.h"

extern "C" {
#include <pthread.h>
#include <semaphore.h>
}

//-----------------------------------------------------------------------------
// Locks
struct semaphore {
	__spinlock_t lock;
	int count;
	__queue_t(thread_desc) waiting;
};

void  ?{}(semaphore & this, int count = 1);
void ^?{}(semaphore & this);
void   P (semaphore & this);
void   V (semaphore & this);


//-----------------------------------------------------------------------------
// Processor
extern struct cluster * mainCluster;

enum FinishOpCode { No_Action, Release, Schedule, Release_Schedule, Release_Multi, Release_Multi_Schedule, Callback };

typedef void (*__finish_callback_fptr_t)(void);

//TODO use union, many of these fields are mutually exclusive (i.e. MULTI vs NOMULTI)
struct FinishAction {
	FinishOpCode action_code;
	/*
	// Union of possible actions
	union {
		// Option 1 : locks and threads
		struct {
			// 1 thread or N thread
			union {
				thread_desc * thrd;
				struct {
					thread_desc ** thrds;
					unsigned short thrd_count;
				};
			};
			// 1 lock or N lock
			union {
				__spinlock_t * lock;
				struct {
					__spinlock_t ** locks;
					unsigned short lock_count;
				};
			};
		};
		// Option 2 : action pointer
		__finish_callback_fptr_t callback;
	};
	/*/
	thread_desc * thrd;
	thread_desc ** thrds;
	unsigned short thrd_count;
	__spinlock_t * lock;
	__spinlock_t ** locks;
	unsigned short lock_count;
	__finish_callback_fptr_t callback;
	//*/
};
static inline void ?{}(FinishAction & this) {
	this.action_code = No_Action;
	this.thrd = NULL;
	this.lock = NULL;
}
static inline void ^?{}(FinishAction & this) {}

// Processor
coroutine processorCtx_t {
	struct processor * proc;
};

// Wrapper around kernel threads
struct processor {
	// Main state
	// Coroutine ctx who does keeps the state of the processor
	struct processorCtx_t runner;

	// Cluster from which to get threads
	struct cluster * cltr;

	// Name of the processor
	const char * name;

	// Handle to pthreads
	pthread_t kernel_thread;

	// Termination
	// Set to true to notify the processor should terminate
	volatile bool do_terminate;

	// Termination synchronisation
	semaphore terminated;

	// RunThread data
	// Action to do after a thread is ran
	struct FinishAction finish;

	// Preemption data
	// Node which is added in the discrete event simulaiton
	struct alarm_node_t * preemption_alarm;

	// If true, a preemption was triggered in an unsafe region, the processor must preempt as soon as possible
	bool pending_preemption;

	// Idle lock
	sem_t idleLock;

	// Link lists fields
	struct {
		struct processor * next;
		struct processor * prev;
	} node;

#ifdef __CFA_DEBUG__
	// Last function to enable preemption on this processor
	const char * last_enable;
#endif
};

void  ?{}(processor & this, const char * name, struct cluster & cltr);
void ^?{}(processor & this);

static inline void  ?{}(processor & this)                    { this{ "Anonymous Processor", *mainCluster}; }
static inline void  ?{}(processor & this, struct cluster & cltr)    { this{ "Anonymous Processor", cltr}; }
static inline void  ?{}(processor & this, const char * name) { this{name, *mainCluster }; }

static inline [processor *&, processor *& ] __get( processor & this ) {
	return this.node.[next, prev];
}

//-----------------------------------------------------------------------------
// Cluster
struct cluster {
	// Ready queue locks
	__spinlock_t ready_queue_lock;

	// Ready queue for threads
	__queue_t(thread_desc) ready_queue;

	// Name of the cluster
	const char * name;

	// Preemption rate on this cluster
	Duration preemption_rate;

	// List of processors
	__spinlock_t proc_list_lock;
	__dllist_t(struct processor) procs;
	__dllist_t(struct processor) idles;

	// List of processors
	__spinlock_t thread_list_lock;
	__dllist_t(struct thread_desc) threads;

	// Link lists fields
	struct {
		cluster * next;
		cluster * prev;
	} node;
};
extern Duration default_preemption();

void ?{} (cluster & this, const char * name, Duration preemption_rate);
void ^?{}(cluster & this);

static inline void ?{} (cluster & this)                           { this{"Anonymous Cluster", default_preemption()}; }
static inline void ?{} (cluster & this, Duration preemption_rate) { this{"Anonymous Cluster", preemption_rate}; }
static inline void ?{} (cluster & this, const char * name)        { this{name, default_preemption()}; }

static inline [cluster *&, cluster *& ] __get( cluster & this ) {
	return this.node.[next, prev];
}

// Local Variables: //
// mode: c //
// tab-width: 4 //
// End: //
