kerneltest/e32test/nkernsa/fastbuf.cpp
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/nkernsa/fastbuf.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,266 @@
+// Copyright (c) 2007-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\nkernsa\fastbuf.cpp
+// 
+//
+
+#include <nktest/nkutils.h>
+
+template <class T>
+class WaitFreePipe
+	{
+public:
+	static WaitFreePipe<T>* New(TInt aSize);
+	~WaitFreePipe();
+	void InitReader();
+	void InitWriter();
+	void Read(T& aOut);
+	TInt Write(const T& aIn);
+	inline TUint32 Waits() {return iWaits;}
+	inline void ResetWaits() {iWaits = 0;}
+private:
+	WaitFreePipe();
+private:
+	T* volatile iWrite;
+	T* volatile iRead;
+	T* iBase;
+	T* iEnd;
+	NRequestStatus* volatile iStat;
+	NThread* iReader;
+	volatile TUint32 iWaits;
+	};
+
+template <class T>
+WaitFreePipe<T>::WaitFreePipe()
+	:	iStat(0),
+		iReader(0),
+		iWaits(0)
+	{
+	}
+
+template <class T>
+WaitFreePipe<T>::~WaitFreePipe()
+	{
+	free(iBase);
+	}
+
+template <class T>
+WaitFreePipe<T>* WaitFreePipe<T>::New(TInt aSize)
+	{
+	WaitFreePipe<T>* p = new WaitFreePipe<T>;
+	if (!p)
+		return 0;
+	p->iBase = (T*)malloc(aSize * sizeof(T));
+	if (!p->iBase)
+		{
+		delete p;
+		return 0;
+		}
+	p->iEnd = p->iBase + aSize;
+	p->iWrite = p->iBase;
+	p->iRead = p->iBase;
+	return p;
+	}
+
+template <class T>
+void WaitFreePipe<T>::InitWriter()
+	{
+	}
+
+template <class T>
+void WaitFreePipe<T>::InitReader()
+	{
+	iReader = NKern::CurrentThread();
+	}
+
+template <class T>
+void WaitFreePipe<T>::Read(T& aOut)
+	{
+	while (iRead == iWrite)
+		{
+		NRequestStatus s;
+		s = KRequestPending;
+		// make sure set to KRequestPending is seen before iStat write
+		__e32_atomic_store_ord_ptr(&iStat, &s);
+		// make sure writer sees our request status before we check for buffer empty again
+		if (iRead != iWrite)
+			RequestComplete(iReader, (NRequestStatus*&)iStat, 0);
+		WaitForRequest(s);
+		++iWaits;
+		}
+	aOut = *iRead;
+	T* new_read = iRead + 1;
+	if (new_read == iEnd)
+		new_read = iBase;
+	// make sure read of data value is observed before update of read pointer
+	__e32_atomic_store_rel_ptr(&iRead, new_read);
+	}
+
+template <class T>
+TInt WaitFreePipe<T>::Write(const T& aIn)
+	{
+	T* new_write = iWrite + 1;
+	if (new_write == iEnd)
+		new_write = iBase;
+	if (new_write == iRead)
+		return KErrOverflow;	// buffer full
+	*iWrite = aIn;
+	// make sure data is seen before updated write pointer
+	__e32_atomic_store_ord_ptr(&iWrite, new_write);
+	if (iStat)
+		RequestComplete(iReader, (NRequestStatus*&)iStat, 0);
+	return KErrNone;
+	}
+
+
+struct SPipeTest
+	{
+	WaitFreePipe<TUint32>* iPipe;
+	TUint64 iTotalWrites;
+	TUint64 iTotalReads;
+	volatile TUint32 iWrites;
+	volatile TUint32 iReads;
+	TUint32 iMeasure;
+	volatile TUint32 iReadTime;
+	volatile TUint32 iWriteTime;
+	volatile TBool iStop;
+	};
+
+void PipeWriterThread(TAny* aPtr)
+	{
+	SPipeTest& a = *(SPipeTest*)aPtr;
+	a.iPipe->InitWriter();
+	TUint32 seed[2] = {1,0};
+	TUint32 seqs[2] = {3,0};
+	TInt r;
+	while (!a.iStop)
+		{
+		TUint32 x = random(seqs);
+		do	{
+			r = a.iPipe->Write(x);
+			if (r != KErrNone)
+				fcfspin(2*a.iWriteTime);
+			} while (r != KErrNone);
+		++a.iTotalWrites;
+		++a.iWrites;
+		while (a.iWrites>=a.iMeasure)
+			{}
+		TUint32 time = random(seed) % a.iWriteTime;
+		fcfspin(time);
+		}
+	}
+
+void PipeReaderThread(TAny* aPtr)
+	{
+	SPipeTest& a = *(SPipeTest*)aPtr;
+	TUint32 seed[2] = {2,0};
+	TUint32 seqs[2] = {3,0};
+	a.iPipe->InitReader();
+	a.iPipe->ResetWaits();
+	while (!a.iStop)
+		{
+		TUint32 x = random(seqs);
+		TUint32 y;
+		a.iPipe->Read(y);
+		TEST_RESULT(x==y, "Wrong value");
+		++a.iTotalReads;
+		++a.iReads;
+		if (a.iReads < a.iMeasure)
+			{
+			TUint32 time = random(seed) % a.iReadTime;
+			fcfspin(time);
+			continue;
+			}
+		TUint32 w = a.iPipe->Waits();
+		TUint32 wr = (w<<4)/a.iMeasure;
+		TEST_PRINT3("%d waits out of %d (wr=%d)", w, a.iMeasure, wr);
+		TUint32 rt = a.iReadTime;
+		TUint32 wt = a.iWriteTime;
+		switch (wr)
+			{
+			case 0:
+				a.iReadTime = rt>>1;
+				a.iWriteTime = wt<<1;
+				break;
+			case 1:
+			case 2:
+			case 3:
+				a.iReadTime = rt - (rt>>2);
+				a.iWriteTime = wt + (wt>>2);
+				break;
+			case 4:
+			case 5:
+			case 6:
+				a.iReadTime = rt - (rt>>3);
+				a.iWriteTime = wt + (wt>>3);
+				break;
+			case 7:
+			case 8:
+				// ok
+				break;
+			case 9:
+			case 10:
+			case 11:
+				a.iReadTime = rt - (rt>>3);
+				a.iWriteTime = wt + (wt>>3);
+				break;
+			case 12:
+			case 13:
+			case 14:
+				a.iReadTime = rt + (rt>>2);
+				a.iWriteTime = wt - (wt>>2);
+				break;
+			case 15:
+			case 16:
+				a.iReadTime = rt<<1;
+				a.iWriteTime = wt>>1;
+				break;
+			}
+		TEST_PRINT4("RT: %d->%d WT: %d->%d", rt, a.iReadTime, wt, a.iWriteTime);
+		a.iPipe->ResetWaits();
+		a.iReads = 0;
+		a.iWrites = 0;
+		}
+	}
+
+void DoPipeTest()
+	{
+	SPipeTest a;
+	memclr(&a, sizeof(a));
+	a.iPipe = WaitFreePipe<TUint32>::New(1024);
+	TEST_OOM(a.iPipe);
+	a.iMeasure = 131072;
+	a.iReadTime = 1024;
+	a.iWriteTime = 1024;
+
+	NFastSemaphore exitSem(0);
+	NThread* reader = CreateThreadSignalOnExit("Reader", &PipeReaderThread, 11, &a, 0, -1, &exitSem, 0);
+	TEST_OOM(reader);
+	NThread* writer = CreateThreadSignalOnExit("Writer", &PipeWriterThread, 11, &a, 0, -1, &exitSem, 1);
+	TEST_OOM(writer);
+
+	while (a.iTotalWrites < 0x01000000u)
+		NKern::Sleep(1000);
+	a.iStop = TRUE;
+
+	NKern::FSWait(&exitSem);
+	NKern::FSWait(&exitSem);
+	}
+
+void TestWaitFreePipe()
+	{
+	DoPipeTest();
+	}
+
+