// Copyright (c) 2008-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\demandpaging\t_threadcreate.cpp
// 
//

#define __E32TEST_EXTENSION__
#include <e32test.h>
#include <dptest.h>
#include <e32hal.h>
#include <u32exec.h>
#include <e32svr.h>
#include <e32panic.h>
#include "u32std.h"

#include "t_dpcmn.h"

enum
	{
	EUnspecified, 
	EPaged, 
	EUnpaged,
	};

_LIT(KGlobalThreadName, "gThreadGlobal");

RSemaphore gSem1;
RSemaphore gSem2;
TBool gStackPaged;
TUint8* gStackPtr = NULL;

struct SThreadPagedInfo
	{
	TBool iHeapPaged;
	TBool iStackPaged;
	};

TUint8 ReadByte(volatile TUint8* aPtr)
	{
	return *aPtr;
	}

TInt TestThreadFunction(TAny* aPtr)
	{
	for (TInt i = 0; i<2; i++)
		{
		if (i == 1)
			{
			User::SetRealtimeState(User::ERealtimeStateOn);
			RDebug::Printf("aPtr %x",aPtr);
			ReadByte((TUint8*)aPtr);
			}
		}
	return KErrNone;
	}

//
// IsStackPaged
//
// Determine whether the stack is paged by flushing the cache and attempting
// to read a byte that has been paged out
//
//
TInt IsStackPaged(const TUint8* aPtr)
	{
	RThread thread;
	TInt r;
	r = thread.Create(KNullDesC, TestThreadFunction, 0x1000, NULL, (TAny*)aPtr);
	if (r != KErrNone)
		{
		return r;
		}

	TRequestStatus status;
	thread.Logon(status);
	if(status.Int() != KRequestPending)
		{
		return KErrGeneral;
		}
	thread.Resume();
	User::WaitForRequest(status);
	if (thread.ExitType() == EExitPanic &&
		thread.ExitCategory() == _L("KERN-EXEC") &&
		thread.ExitReason() == EIllegalFunctionForRealtimeThread)
		{
		gStackPaged = ETrue;
		}
	else
		{ 
		r = thread.ExitReason();
		if(r != KErrNone)
			return r;

		if (EExitKill != thread.ExitType())
			return KErrGeneral;
		gStackPaged = EFalse;
		}	
	thread.Close();
	if (!gStackPaged)
		{
		RDebug::Printf("    %08x present", aPtr);
		}
	else
		{
		RDebug::Printf("    %08x not present", aPtr);
		}
	return r;
	}

/**
Thread that just returns the data paging attributes of the thread.
*/
TInt ThreadFunc(TAny* aThreadInfo)
	{
	SThreadPagedInfo& info = *(SThreadPagedInfo*)aThreadInfo;
	RHeap& heap = User::Heap();
	RChunk chunk;
	chunk.SetHandle(heap.ChunkHandle());
	info.iHeapPaged = chunk.IsPaged();
	gStackPtr = (TUint8*)&chunk;
	RDebug::Printf("&chunk %x",&chunk);
	gSem1.Signal();
	gSem2.Wait();
	info.iStackPaged = gStackPaged;
	return KErrNone;
	}

TInt DummyFunction(TAny*)
	{
	return KErrNone;
	}

TInt PanicThreadCreate(TAny* aCreateInfo)
	{
	RThread thread;
	TThreadCreateInfo createInfo((*(TThreadCreateInfo*) aCreateInfo));
	thread.Create(createInfo);
	return KErrGeneral; // Should never reach here
	}

//
// CheckHeapStackPaged
//
// Using the TThreadCreateInfo used to create the cheap, determine 
// whether the stack and the heap are paged or not
//
//
TInt CheckHeapStackPaged(TThreadCreateInfo& aCreateInfo, TInt aPaged, SThreadPagedInfo& aPagedInfo, TBool aUseProcessHeap = EFalse)
	{
	RThread thread;
	TBool paged;
	switch (aPaged)
		{
		case EUnspecified:
			test.Printf(_L("Testing gProcessPaged\n"));
			paged = gProcessPaged;
			break;

		case EPaged:
			test.Printf(_L("Testing Paged\n"));
			aCreateInfo.SetPaging(TThreadCreateInfo::EPaged);
			paged = ETrue;
			break;

		case EUnpaged:
			test.Printf(_L("Testing Unpaged\n"));
			aCreateInfo.SetPaging(TThreadCreateInfo::EUnpaged);
			paged = EFalse;
			break;
		}


	test_KErrNone(thread.Create(aCreateInfo));
	
	// Disable JIT debugging.
	TBool justInTime=User::JustInTime();
	User::SetJustInTime(EFalse);

	TRequestStatus status;
	thread.Logon(status); 
	
	thread.Resume();
	
	gSem1.Wait();		
	DPTest::FlushCache();
	TInt r = IsStackPaged(gStackPtr);
	test_KErrNone(r);
	gSem2.Signal();

	User::WaitForRequest(status);
	test (EExitKill == thread.ExitType());
	test(KErrNone == status.Int());
	
	test(KErrNone == thread.ExitReason());

	if (thread.ExitType() == EExitPanic)
		{
		test(thread.ExitCategory()==_L("USER"));
		}
			
	CLOSE_AND_WAIT(thread);

	// Put JIT debugging back to previous status.
	User::SetJustInTime(justInTime);

	UpdatePaged(paged);
	if (aUseProcessHeap)
		{// If using existing thread heap, heap will take the process paging status
		test_Equal(gProcessPaged, aPagedInfo.iHeapPaged);
		}
	else
		{
		test_Equal(paged, aPagedInfo.iHeapPaged);
		}
	test_Equal(paged, aPagedInfo.iStackPaged);
	return KErrNone;
	}

//
// TestThreadCreate
//
//----------------------------------------------------------------------------------------------
//! @SYMTestCaseID			KBASE-T_THREADHEAPCREATE-xxxx
//! @SYMTestType			UT
//! @SYMPREQ				PREQ1954
//! @SYMTestCaseDesc		TThreadCreateInfo tests
//!							Verify the thread heap creation implementation
//! @SYMTestActions	
//! 1.	Call TThreadCreateInfo::TThreadCreateInfo() with valid parameters. 
//! 	Following this call RThread::Create()
//! 2.	Call TThreadCreateInfo::TThreadCreateInfo() with an invalid stack size. 
//!		Following this call RThread::Create()
//! 3.	Call TThreadCreateInfo::SetCreateHeap() with an invalid min heap size. 
//!		Following this call RThread::Create()
//! 4.	Call TThreadCreateInfo::SetCreateHeap() with an invalid max heap size. 
//!		Following this call RThread::Create()
//! 5.	Call TThreadCreateInfo::SetCreateHeap() with minHeapSize. > maxHeapSize 
//!		Following this call RThread::Create()
//! 6.	Call TThreadCreateInfo::SetUseHeap() specifying NULL. Following this call RThread::Create()
//! 7.	Call TThreadCreateInfo::SetOwner() with aOwner set to EOwnerProcess. 
//!		Following this call RThread::Create()
//! 8.	Call TThreadCreateInfo::SetOwner() with aOwner set to EOwnerThread. 
//!		Following this call RThread::Create()
//! 9.	Call TThreadCreateInfo::SetPaging() with aPaging set to unspecified. 
//!		Following this call RThread::Create() and check the paging status of the thread
//! 10.	Call TThreadCreateInfo::SetPaging() with aPaging set to EPaged. 
//!		Following this call RThread::Create() and check the paging status of the thread
//! 11.	Call TThreadCreateInfo::SetPaging() with aPaging set to EUnpaged. 
//!		Following this call RThread::Create() and check the paging status of the thread
//!
//! @SYMTestExpectedResults All tests should pass.
//! @SYMTestPriority        High
//! @SYMTestStatus          Implemented
//----------------------------------------------------------------------------------------------
void TestThreadCreate()
	{
	TInt r;
	test.Start(_L("Test RThread::Create() (New Heap)"));
		{
		RThread thread;
		TThreadCreateInfo createInfo(KGlobalThreadName, DummyFunction, KDefaultStackSize, NULL);
		createInfo.SetCreateHeap(KMinHeapSize, KMinHeapSize);
		r = thread.Create(createInfo);
		test_KErrNone(r);
		test_KErrNone(TestThreadExit(thread, EExitKill, KErrNone));
		thread.Close();
		}

	test.Next(_L("Test RThread::Create() - invalid stack size"));
		{
		TThreadCreateInfo createInfo(KGlobalThreadName, DummyFunction, -1 , NULL);
		createInfo.SetCreateHeap(KMinHeapSize, KMinHeapSize);
		
		RThread threadPanic;
		test_KErrNone(threadPanic.Create(_L("Panic UserHeap"), PanicThreadCreate, KDefaultStackSize, KMinHeapSize, 
															KMinHeapSize,  (TAny*) &createInfo));
		test_KErrNone(TestThreadExit(threadPanic, EExitPanic, EThrdStackSizeNegative));
		}

	test.Next(_L("Test RThread::Create() - invalid min heap size"));
		{
		TThreadCreateInfo createInfo(KGlobalThreadName, DummyFunction, KDefaultStackSize , NULL);
		createInfo.SetCreateHeap(-1, KMinHeapSize);
		
		RThread threadPanic;
		test_KErrNone(threadPanic.Create(_L("Panic UserHeap"), PanicThreadCreate, KDefaultStackSize, KMinHeapSize, 
															KMinHeapSize,  (TAny*) &createInfo));

		test_KErrNone(TestThreadExit(threadPanic, EExitPanic, EThrdHeapMinTooSmall));
		}

	test.Next(_L("Test RThread::Create() - invalid max heap size"));
		{
		TThreadCreateInfo createInfo(KGlobalThreadName, DummyFunction, KDefaultStackSize , NULL);
		createInfo.SetCreateHeap(KMinHeapSize, -1);
		
		RThread threadPanic;
		test_KErrNone(threadPanic.Create(_L("Panic UserHeap"), PanicThreadCreate, KDefaultStackSize, KMinHeapSize, 
															KMinHeapSize,  (TAny*) &createInfo));

		test_KErrNone(TestThreadExit(threadPanic, EExitPanic, EThrdHeapMaxLessThanMin));
		}

	test.Next(_L("Test RThread::Create() - min heap size > max heap size"));
		{
		TThreadCreateInfo createInfo(KGlobalThreadName, DummyFunction, KDefaultStackSize , NULL);
		createInfo.SetCreateHeap(KMinHeapSize << 1, KMinHeapSize);
		
		RThread threadPanic;
		test_KErrNone(threadPanic.Create(_L("Panic UserHeap"), PanicThreadCreate, KDefaultStackSize, KMinHeapSize, 
															KMinHeapSize,  (TAny*) &createInfo));

		test_KErrNone(TestThreadExit(threadPanic, EExitPanic, EThrdHeapMaxLessThanMin));
		}

	test.Next(_L("Test TThreadCreateInfo::SetUseHeap() "));
		{
		RThread thread;
		TThreadCreateInfo createInfo(KGlobalThreadName, DummyFunction, KDefaultStackSize, NULL);
		createInfo.SetUseHeap(NULL);
		r = thread.Create(createInfo);
		test_KErrNone(r);
		test_KErrNone(TestThreadExit(thread, EExitKill, KErrNone));
		thread.Close();
		}

	test.Next(_L("Test TThreadCreateInfo::SetOwner(EOwnerProcess) "));
		{
		RThread thread;
		TThreadCreateInfo createInfo(KGlobalThreadName, DummyFunction, KDefaultStackSize, NULL);
		createInfo.SetCreateHeap(KMinHeapSize, KMinHeapSize);
		createInfo.SetOwner(EOwnerProcess);
		r = thread.Create(createInfo);
		test_KErrNone(r);
		test_KErrNone(TestThreadExit(thread, EExitKill, KErrNone));
		thread.Close();
		}


	test.Next(_L("Test TThreadCreateInfo::SetOwner(EOwnerThread) "));
		{
		RThread thread;
		TThreadCreateInfo createInfo(KGlobalThreadName, DummyFunction, KDefaultStackSize, NULL);
		createInfo.SetCreateHeap(KMinHeapSize, KMinHeapSize);
		createInfo.SetOwner(EOwnerThread);
		r = thread.Create(createInfo);
		test_KErrNone(r);
		test_KErrNone(TestThreadExit(thread, EExitKill, KErrNone));
		thread.Close();
		}



	gSem1.CreateLocal(0);
	gSem2.CreateLocal(0);
	test.Next(_L("Test Thread paging (New Heap)"));
		{		
		TBool aPaged = gProcessPaged;
		SThreadPagedInfo pagedInfo;
		test.Printf(_L("Testing gProcessPaged: aPaged = %x\n"), aPaged);
		TThreadCreateInfo createInfo(	KGlobalThreadName, ThreadFunc, KDefaultStackSize,
										(TAny*)&pagedInfo);
		createInfo.SetCreateHeap(KMinHeapSize, KMinHeapSize);
		
		test_KErrNone(CheckHeapStackPaged(createInfo, EUnspecified, pagedInfo));
		test_KErrNone(CheckHeapStackPaged(createInfo, EPaged, pagedInfo));
		test_KErrNone(CheckHeapStackPaged(createInfo, EUnpaged, pagedInfo));
		}


	test.Next(_L("Test RThread::Create() (Existing Heap)"));
		{
		SThreadPagedInfo pagedInfo;
		TThreadCreateInfo createInfo(	KGlobalThreadName, ThreadFunc, KDefaultStackSize, 
										(TAny*)&pagedInfo);
		createInfo.SetUseHeap(NULL);
		
		test_KErrNone(CheckHeapStackPaged(createInfo, EUnspecified, pagedInfo, ETrue));
		test_KErrNone(CheckHeapStackPaged(createInfo, EPaged, pagedInfo, ETrue));
		test_KErrNone(CheckHeapStackPaged(createInfo, EUnpaged, pagedInfo, ETrue));
		}
	test.End();
	}



TInt TestingTThreadCreate()
	{
	test.Printf(_L("Test TThreadCreateInfo\n"));
	TestThreadCreate();

	return 0;
	}
