// Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// e32test\system\t_atomicu.cpp
// Overview:
// Simple test for class User atomic operations
// API Information:
// User::SafeInc(), User::SafeDec(), User::LockedInc(),
// User::LockedDec()
// Details:
// - Tests SafeInc, SafeDec, LockedInc and LockedDec
// functions in single thread and determines that counts
// match after finished
// - Tests SafeInc, SafeDec, LockedInc and LockedDec
// functions in multithreaded configuration and determines
// that counts match after finished
// Platforms/Drives/Compatibility:
// All.
// Assumptions/Requirement/Pre-requisites:
// Failures and causes:
// Base Port information:
// 
//

#define __E32TEST_EXTENSION__
#include <e32test.h>
#include <e32svr.h>
#include <e32def.h>

LOCAL_D RTest test(_L("T_ATOMICU"));

const TInt KMaxOps=20000;
#define KNumThreads 20
TInt gValue=0;

void TestSafeIncAndSafeDec()
	{
	gValue=0;
	// increasing when 0, should return 0
	test(User::SafeInc(gValue)==0);
	// value also should be 0
	test(gValue==0);
	
	gValue=1;
	TInt expected=0;
	// gValue should vary only between 1 and 2
	for (TInt i=0; i<KMaxOps; i++)
		{
		expected=User::SafeInc(gValue)+1;
		test(expected==User::SafeDec(gValue));
		}

	// after running these, it should be 1
	test(gValue==1);
	
	// should stay zero after decreasing from 1 multiple times
	test(User::SafeDec(gValue)==1);
	test(User::SafeDec(gValue)==0);
	test(User::SafeDec(gValue)==0);
	test(gValue==0);
	}

void TestLockedIncAndLockedDec()
	{
	gValue=0;
	// increasing when 0, should return 0 as old value
	test(User::LockedInc(gValue)==0);
	// new value should be 1
	test(gValue==1);

	gValue=-1;

	// gValue should vary only between 1 and 2
	for (TInt i=0; i<KMaxOps; i++)
		{
		test((User::LockedInc(gValue)+1)==User::LockedDec(gValue));
		}

	// after running these, it should be back in -1
	test(gValue==-1);
	}

TInt MultiThreadSafeIncAndSafeDec_FUNC(TAny*)
	{
	for (TInt i=0; i<KMaxOps; i++)
		{
		User::SafeInc(gValue);
		User::SafeDec(gValue);
		}
	return KErrNone;
	}

TInt MultiThreadLockedIncAndLockedDec_FUNC(TAny*)
	{
	for (TInt i=0; i<KMaxOps; i++)
		{
		User::LockedInc(gValue);
		User::LockedDec(gValue);
		}
	return KErrNone;
	}

void MultiThreadSafeIncAndSafeDec()
	{
	gValue=1; // start value 1
	RThread threads[KNumThreads];
	TRequestStatus stats[KNumThreads];
	TInt i;

	for (i=0;i<KNumThreads;i++)
		{
		test_KErrNone(threads[i].Create(KNullDesC,MultiThreadSafeIncAndSafeDec_FUNC,KDefaultStackSize,NULL,NULL));
		threads[i].Logon(stats[i]);
		}
	
	//lets increase our priority first, so that all the threads start
	RThread().SetPriority(EPriorityMore);

	test.Printf(_L("Resuming threads...\n"));
	for(i=0; i<KNumThreads; i++)
		threads[i].Resume();

	for(i=0; i<KNumThreads; i++)
		{
		User::WaitForRequest(stats[i]);
		test_KErrNone(stats[i].Int());
		CLOSE_AND_WAIT(threads[i]);
		}

	test.Printf(_L("...Threads finished\n"));
	
	// back to normal
	RThread().SetPriority(EPriorityNormal);

	// test that we returned to the startvalue
	test(gValue==1);
	}

void MultiThreadLockedIncAndLockedDec()
	{
	gValue=-1; // set start value to -1
	RThread threads[KNumThreads];
	TRequestStatus stats[KNumThreads];
	TInt i;

	for (i=0;i<KNumThreads;i++)
		{
		test_KErrNone(threads[i].Create(KNullDesC,MultiThreadLockedIncAndLockedDec_FUNC,KDefaultStackSize,NULL,NULL));
		threads[i].Logon(stats[i]);
		}
	
	//lets increase our priority first, so that all the threads start
	RThread().SetPriority(EPriorityMore);

	test.Printf(_L("Resuming threads...\n"));
	for(i=0; i<KNumThreads; i++)
		threads[i].Resume();

	for(i=0; i<KNumThreads; i++)
		{
		User::WaitForRequest(stats[i]);
		test_KErrNone(stats[i].Int());
		CLOSE_AND_WAIT(threads[i]);
		}

	test.Printf(_L("...Threads finished\n"));
	
	// back to normal
	RThread().SetPriority(EPriorityNormal);

	// test that we returned to the startvalue
	test(gValue==-1);
	}


TInt E32Main()
	{
	test.Title();
	
	test.Start(_L("Test single thread User::SafeInc and User::SafeDec"));
	TestSafeIncAndSafeDec();
	
	test.Next(_L("Test single thread User::LockedInc and User::LockedDec"));
	TestLockedIncAndLockedDec();

	test.Next(_L("Test multiple thread User::SafeInc and User::SafeDec"));
	MultiThreadSafeIncAndSafeDec();
	
	test.Next(_L("Test multiple thread User::LockedInc and User::LockedDec"));
	MultiThreadLockedIncAndLockedDec();

	test.End();

	return KErrNone;
	}
