/*
	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License version 2 
	as published by the Free Software Foundation.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA


	Copyright (C) 2006  Thierry Berger-Perrin <tbptbp@gmail.com>
*/
#ifndef SYS_CPU_H
#define SYS_CPU_H


#include "specifics.h"

namespace sys {
	namespace cpu {
		/*
			Ok, we're making a few bogus assumptions like all cpu running at the same frequency
			or that said freq doesn't change. That's wrong, but good enough.
			We also assume all cpu/cores share the same feature set.

			However, we don't assume tsc are synchronised over cores/cpu (that doesn't hold on windows).
			And generally we make sure threads are binded.

			Nomenclature: 1 cpu = 1 core / 1 cpu (whatever makes sense in the execution context).
		*/

		enum {
			max_cpu = 16,
			preferred_cpu = 0
		};

		enum priority_t { 
			priority_normal = 0, priority_above = 1, priority_high = 2, priority_max = 3,

			// nvidia driver's threads are playing tricks behind my back on windoze
			// it's really really annoying.
			// a) i have to bump all priorities b) it's more stable if the main thread is binded to cpu #1 (nope)
			process_priority = priority_high,

			thread_priority_main = priority_max,
			thread_priority_pump = priority_normal,
			thread_priority_grunts = priority_high
		};


		extern int_t		num_cpu;
		extern float64_t	cpu_frequency;
		
		NOINLINE bool_t bind_process(const int cpu);
		NOINLINE bool_t bind_thread(const int cpu);
		
		NOINLINE bool_t set_process_priority(const priority_t prio);
		NOINLINE bool_t set_thread_priority(const priority_t prio);

		NOINLINE bool_t bootstrap();
	}


	#ifdef __ICC__
		// shut the fuck up
		#pragma warning(push)
		#pragma warning(disable: 1011 869)	// missing return statement at end of non-void function "sys::atomic::xadd32 | parameter "addr" was never referenced
	#endif
	namespace atomic {
		/*
			More than just atomic those are also coherent, hence somewhat expensive.
		*/

		/*
		   synopsys:
			old = (addr);
			(addr) += delta;
			return old;
		*/
		static FINLINE int32_t xadd32(int32_t &addr, const int32_t delta) {
			#if defined __MSVC__ || defined __ICC_MSVC__
				__asm {
					mov eax, delta;
					mov ecx, addr;
					lock xadd [ecx], eax;
				}
			#else
				int32_t old = delta;
				__asm__ __volatile__(
					"lock; xaddl %0, %1;"

					: "=r"(old)
					: "m"(addr), "0"(old)
					: "memory" );
				return old;
			#endif
		}

		static FINLINE void inc32(int32_t &addr) {
			#if defined __MSVC__ || defined __ICC_MSVC__
				// of course there's no way to tell msvc addr has been modified.
				__asm { lock inc [addr] }
			#else
				__asm__ __volatile__(
					"lock; incl %0;"
					: : "m"(addr) : "memory" );
			
			#endif
		}
		static FINLINE void dec32(int32_t &addr) {
			#if defined __MSVC__ || defined __ICC_MSVC__
				// of course there's no way to tell msvc addr has been modified.
				__asm { lock dec [addr] };
			#else
				__asm__ __volatile__(
					"lock; decl %0;"
					: : "m"(addr) : "memory" );			
			#endif
		}
	}
	#ifdef __ICC__
		#pragma warning(pop)
	#endif

}

#endif
