/* Check cf5-opt.vim defs.
VIM: let g:lcppflags="-std=c++11 -O2 -pthread -D_GLIBCXX_USE_SCHED_YIELD"
VIM: let g:cf5output=0
*/

/*
 *	Conclusion:
 *		Linux RH 5.7 scheduler affiliates threads to a core perhaps. As a result
 *		if 20 threads are working on 16 core machine then 12 of them work
 *		100% of the time and the 8 are working only 50% of the time.
 *		34 thread case also was tested and the result is 28 threads work 50%
 *		of the time and 6 threads work 33% of the time.
 *		This same effect is visible in Intel TBB.
 *		Looks RH 5.7 uses linux kernel 2.6.18 which has O(1) scheduler.
 *
 *		Linux RH 6.0 has linux kernel 2.6.32 which has Ingo Molnár's the 
 *		"Completely Fair Scheduler". On that system the above mentioned is not
 *		observed.
 */

#include <thread>
#include <vector>
#include <atomic>
#include <condition_variable>
#include <sstream>
#include <iomanip>
#include <iostream>
#include <exception>

void busy_wait()
{
	for ( volatile int j = 0; j < 10000; ++j );
   	// Actually this doesn't matter. Commented or uncommented the result
	// is the same.
	std::this_thread::yield();
}

int main ( void )
{try{

	const int n = 34;
	std::atomic<int> last_id(0);
	
	typedef long long work_type;
	const work_type wamount = 1024L*1024L*40;
	const work_type wslot = 1;
	std::atomic<work_type>  last_work(0);
	
	std::vector<std::thread> threads;
	threads.reserve(n);

//	std::condition_variable cv;
	std::mutex m;
	m.lock();

	for ( int i=0; i < n; ++i )
	{
		threads.push_back( std::thread( [&]()
		{
			int id = last_id++;
			{
				std::unique_lock<std::mutex> ul(m);
			//	cv.wait(ul);
			}
			work_type c = 0;
			while( true )
			{
				work_type w = atomic_fetch_add( &last_work, wslot );
				if ( w >= wamount )
					break;
				c += wslot;
				busy_wait();
			}
			std::stringstream ss;
			ss << "Thread: " << std::setw(2) << id 
			   << " counted " << std::setw(15) << c 
			   << std::endl;
			std::cout << ss.str();
		}));
	}

	m.unlock();
//	cv.notify_all();

	for ( std::thread& t : threads )
		t.join();

	return 0;
}
catch ( const std::exception& e )
{
	std::cerr << std::endl
			<< "std::exception(\"" << e.what() << "\")." << std::endl;
	return 2;
}
catch ( ... )
{
	std::cerr  << std::endl
			<< "unknown exception." << std::endl;
	return 1;
}}

