/***************************************************************************
              threads-mutex.cpp  -  Test CMutex (and CThread)
                             -------------------
    begin                : Mon Sep 15 2008
    copyright            : (C) 2008 by Edward Sheldrake
    email                : ejs1920@yahoo.co.uk
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

/*
 * This is probably the most useless test ever.
 * It either has to take a longer time and/or very high load average
 * or you could have the threads spend most of their time sleeping
 * instead of waiting for the mutex.
 * 
 * You need to increase the amount/length of threads until the
 * test fails when the counter does not lock the mutex.
 */

#include <dclib/core/cmutex.h>
#include <dclib/core/cthread.h>

#include <stdio.h>

class Counter
{
public:
	Counter() : m_count( 0 ), m_high( 0 ), m_low( 0 ) {};
	~Counter() {};
	
	long GetCount()
	{
		long c;
		m_Mutex.Lock();
		c = m_count;
		m_Mutex.UnLock();
		return c;
	}
	
	long GetHigh()
	{
		long h;
		m_Mutex.Lock();
		h = m_high;
		m_Mutex.UnLock();
		return h;
	}
	
	long GetLow()
	{
		long l;
		m_Mutex.Lock();
		l = m_low;
		m_Mutex.UnLock();
		return l;
	}
	
	void Increment()
	{
		m_Mutex.Lock();
		
		++m_count;
		if ( m_count > m_high )
		{
			m_high = m_count;
		}
		
		m_Mutex.UnLock();
	}
	
	void Decrement()
	{
		m_Mutex.Lock();
		
		--m_count;
		if ( m_count < m_low )
		{
			m_low = m_count;
		}
		
		m_Mutex.UnLock();
	}

private:
	CMutex m_Mutex;
	long m_count;
	long m_high;
	long m_low;
};

class AddSub : public CThread
{
public:
	AddSub( int m, int n, Counter * target ) : CThread(), m_mode( m ), m_times( 0 ), m_max( n ), m_counter( target ) {};
	~AddSub() {};
	
	virtual void Thread()
	{
		if ( m_mode == 0 )
		{
			m_counter->Increment();
		}
		else if ( m_mode == 1 )
		{
			m_counter->Decrement();
		}
		
		++m_times;
		
		if ( m_times == m_max )
		{
			Stop();
		}
	}
	
private:
	int m_mode;
	int m_times;
	int m_max;
	Counter * m_counter;
};

/* avoid THREADS * OPS == overflow */
#define THREADS 9
#define OPS (10000000 ## L)

int main( int /* argc */, char*[] /* argv */ )
{
	Counter TestCounter;
	
	AddSub * threads[THREADS];
	
	for ( int i = 0; i < THREADS; ++i )
	{
		threads[i] = new AddSub( i%2, OPS, &TestCounter );
	}
	
	for ( int i = 0; i < THREADS; ++i )
	{
		threads[i]->Start();
	}
	
	int checked = 0;
	while ( true )
	{
		for ( checked = 0; checked < THREADS; ++checked )
		{
			if ( threads[checked]->Stopped() == false )
			{
				checked = -10;
				break;
			}
		}
		
		if ( checked == THREADS )
		{
			break;
		}
		
		CThread::NanoSleep(100);
	}

#if 1
	printf("high=%ld low=%ld\n",TestCounter.GetHigh(),TestCounter.GetLow());
	printf("count=%ld expected=%ld\n",TestCounter.GetCount(), OPS * (THREADS%2));
#endif
	
	for ( int i = 0; i < THREADS; ++i )
	{
		delete threads[i];
		threads[i] = 0;
	}
	
	if ( TestCounter.GetCount() == OPS * (THREADS%2) )
	{
		
		return 0;
	}
	else
	{
		return 100;
	}
}
