--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/nkernsa/testdfc.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1909 @@
+// 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\testdfc.cpp
+//
+//
+
+#include <nktest/nkutils.h>
+
+#ifndef __SMP__
+#define iNThreadBaseSpare7 iSpare7
+class NSchedulable;
+#endif
+
+extern "C" TUint32 set_bit0_if_nonnull(TUint32&);
+extern "C" void flip_bit0(TUint32&);
+extern "C" TUint32 swap_out_if_bit0_clear(TUint32&);
+
+#ifdef __SMP__
+class TAddDfc : public TGenericIPI
+#else
+class TAddDfc : public NTimer
+#endif
+ {
+public:
+ TAddDfc();
+ TDfc* Add(TDfc* aDfc, TUint32 aCpuMask);
+ static TAddDfc* New();
+#ifdef __SMP__
+ static void Isr(TGenericIPI*);
+#else
+ static void TimerCallBack(TAny*);
+ void WaitCompletion();
+#endif
+public:
+ TDfc* iDfc;
+ };
+
+TAddDfc::TAddDfc()
+#ifdef __SMP__
+ : iDfc(0)
+#else
+ : NTimer(&TimerCallBack, this),
+ iDfc(0)
+#endif
+ {
+ }
+
+TAddDfc* TAddDfc::New()
+ {
+ TAddDfc* p = new TAddDfc;
+ TEST_OOM(p);
+ return p;
+ }
+
+#ifdef __SMP__
+void TAddDfc::Isr(TGenericIPI* a)
+#else
+void TAddDfc::TimerCallBack(TAny* a)
+#endif
+ {
+ TAddDfc& adder = *(TAddDfc*)a;
+ TDfc* dfc = (TDfc*)__e32_atomic_swp_ord_ptr(&adder.iDfc, 0);
+ if (dfc)
+ dfc->Add();
+ }
+
+TDfc* TAddDfc::Add(TDfc* aDfc, TUint32 aCpuMask)
+ {
+ TDfc* old = (TDfc*)__e32_atomic_swp_ord_ptr(&iDfc, aDfc);
+#ifdef __SMP__
+ Queue(&Isr, aCpuMask);
+#else
+ (void)aCpuMask;
+ OneShot(1);
+#endif
+ return old;
+ }
+
+#ifndef __SMP__
+void TAddDfc::WaitCompletion()
+ {
+ while (iDfc)
+ {}
+ }
+#endif
+
+class TTestDfc : public TDfc
+ {
+public:
+ TTestDfc(TUint aId);
+ TTestDfc(TUint aId, TDfcQue* aQ, TInt aPri);
+ static void Run(TAny* aPtr);
+
+ static void CheckEmpty(TInt aLine);
+ static void CheckFirstEntry(TInt aLine, TUint32 aCpu, TUint32 aContext, TDfcQue* aQ, TUint32 aId);
+
+ static CircBuf* Buffer;
+ static volatile TBool Full;
+ static volatile TUint32 Last;
+
+ enum {EBufferSlots=1024};
+ };
+
+#define CHECK_EMPTY() TTestDfc::CheckEmpty(__LINE__)
+#define CHECK_FIRST_ENTRY(cpu, ctx, q, id) TTestDfc::CheckFirstEntry(__LINE__, cpu, ctx, q, id)
+
+CircBuf* TTestDfc::Buffer;
+volatile TBool TTestDfc::Full = FALSE;
+volatile TUint32 TTestDfc::Last;
+
+TTestDfc::TTestDfc(TUint aId)
+ : TDfc(&Run, (TAny*)aId)
+ {
+ }
+
+TTestDfc::TTestDfc(TUint aId, TDfcQue* aQ, TInt aPri)
+ : TDfc(&Run, (TAny*)aId, aQ, aPri)
+ {
+ }
+
+void TTestDfc::Run(TAny* aPtr)
+ {
+ TUint32 id = (TUint32)aPtr;
+ TUint32 tid = 0;
+ TUint32 ctx = NKern::CurrentContext();
+ TUint32 cpu = NKern::CurrentCpu();
+ if (ctx == NKern::EThread)
+ {
+ NThread* t = NKern::CurrentThread();
+ tid = t->iNThreadBaseSpare7;
+ }
+ TUint32 x = (cpu<<24) | (ctx<<16) | (tid<<8) | id;
+ TInt r = Buffer->TryPut(x);
+ if (r != KErrNone)
+ Full = TRUE;
+ Last = id;
+ }
+
+void TTestDfc::CheckEmpty(TInt aLine)
+ {
+ TInt c = Buffer->Count();
+ TUint32 x;
+ Buffer->TryGet(x);
+ if (c!=0)
+ {
+ TEST_PRINT3("Line %d Buffer not empty C:%d X:%08x", aLine, c, x);
+ }
+ }
+
+void TTestDfc::CheckFirstEntry(TInt aLine, TUint32 aCpu, TUint32 aContext, TDfcQue* aQ, TUint32 aId)
+ {
+ TUint32 tid = aQ ? aQ->iThread->iNThreadBaseSpare7 : 0;
+ TUint32 expected = (aCpu<<24) | (aContext<<16) | (tid << 8) | aId;
+ TUint32 x;
+ TInt r = Buffer->TryGet(x);
+ if (r!=KErrNone)
+ {
+ TEST_PRINT2("Line %d Buffer empty, Expected %08x", aLine, expected);
+ }
+ else if (x != expected)
+ {
+ TEST_PRINT3("Line %d Got %08x Expected %08x", aLine, x, expected);
+ }
+ }
+
+class TPauseIDFC : public TDfc
+ {
+public:
+ TPauseIDFC();
+ void Pause(TInt aCpu);
+ void Release();
+ static void Run(TAny*);
+public:
+ volatile TInt iFlag;
+ };
+
+TPauseIDFC::TPauseIDFC()
+ : TDfc(&Run, this),
+ iFlag(-1)
+ {
+ }
+
+void TPauseIDFC::Pause(TInt aCpu)
+ {
+ TAddDfc adder;
+ iFlag = -1;
+ __e32_memory_barrier();
+ adder.Add(this, 1u<<aCpu);
+ adder.WaitCompletion();
+ while (iFlag == -1)
+ {}
+ }
+
+void TPauseIDFC::Release()
+ {
+ __e32_atomic_store_ord32(&iFlag, 1);
+ }
+
+void TPauseIDFC::Run(TAny* aPtr)
+ {
+ TPauseIDFC* p = (TPauseIDFC*)aPtr;
+ __e32_atomic_store_ord32(&p->iFlag, 0);
+ while (__e32_atomic_load_acq32(&p->iFlag) == 0)
+ {}
+ }
+
+class TPauseDFC : public TDfc
+ {
+public:
+ TPauseDFC(TDfcQue* aQ);
+ void Pause(TInt aWait=0);
+ void BusyPause();
+ void Release();
+ static void Run(TAny*);
+public:
+ NFastSemaphore* volatile iSem;
+ volatile TInt iWait;
+ };
+
+TPauseDFC::TPauseDFC(TDfcQue* aQ)
+ : TDfc(&Run, this, aQ, 0),
+ iSem(0)
+ {
+ }
+
+void TPauseDFC::Pause(TInt aWait)
+ {
+ iWait = aWait;
+ NFastSemaphore entrySem(0);
+ iSem = &entrySem;
+ Enque();
+ NKern::FSWait(&entrySem);
+ }
+
+void TPauseDFC::BusyPause()
+ {
+ volatile TInt& flag = (volatile TInt&)iSem;
+ __e32_atomic_store_ord32(&flag, 0xfffffffe);
+ Enque();
+ while (__e32_atomic_load_acq32(&flag) == 0xfffffffe)
+ {}
+ }
+
+void TPauseDFC::Release()
+ {
+ NFastSemaphore* s = (NFastSemaphore*)__e32_atomic_swp_ord_ptr(&iSem, 0);
+ if (((TInt)s)==-1)
+ {
+ volatile TInt& flag = (volatile TInt&)iSem;
+ __e32_atomic_store_ord32(&flag, 0);
+ }
+ else
+ NKern::FSSignal(s);
+ }
+
+void TPauseDFC::Run(TAny* aPtr)
+ {
+ TPauseDFC* p = (TPauseDFC*)aPtr;
+ volatile TInt& flag = (volatile TInt&)p->iSem;
+ if (flag == -2)
+ {
+ flag = -1;
+ __e32_memory_barrier();
+ while (flag == -1)
+ {}
+ }
+ else
+ {
+ NFastSemaphore exitSem(0);
+ NFastSemaphore* s = (NFastSemaphore*)__e32_atomic_swp_ord_ptr(&p->iSem, &exitSem);
+ if (p->iWait)
+ {
+ nfcfspin(__microseconds_to_norm_fast_counter(10000));
+ NKern::Sleep(p->iWait);
+ }
+ NKern::FSSignal(s);
+ NKern::FSWait(&exitSem);
+ }
+ }
+
+void DoDFCTest1()
+ {
+ TEST_PRINT("DFCTest1");
+ TInt cpu;
+ for_each_cpu(cpu)
+ {
+ TDfcQue* q = CreateDfcQ("DfcQ0", 1, cpu);
+ DestroyDfcQ(q);
+ q = CreateDfcQ("DfcQ1", 32, cpu);
+ DestroyDfcQ(q);
+ }
+ }
+
+TBool QueueDfc(TDfc* aDfc, TInt aMode, TBool aExRet)
+ {
+ if (aMode==0)
+ return !aExRet == !aDfc->Enque();
+ else if (aMode>0)
+ {
+ TAddDfc adder;
+ TInt cpu = aMode - 1;
+ adder.Add(aDfc, 1u<<cpu);
+ adder.WaitCompletion();
+ nfcfspin(__microseconds_to_norm_fast_counter(10000));
+ return TRUE;
+ }
+ else if (aMode==-1)
+ {
+ NKern::Lock();
+ TBool ret = aDfc->Add();
+ NKern::Unlock();
+ return !aExRet == !ret;
+ }
+ return FALSE;
+ }
+
+#define QUEUE_DFC(dfc, mode, exret) TEST_RESULT(QueueDfc(dfc,mode,exret),"")
+
+void DoDFCTest2()
+ {
+ TEST_PRINT("DFCTest2");
+ TInt num_cpus = NKern::NumberOfCpus();
+ TInt this_cpu = NKern::CurrentCpu();
+ TDfcQue* q;
+ q = CreateDfcQ("DfcQ2", 1, this_cpu);
+ TEST_OOM(q);
+ q->iThread->iNThreadBaseSpare7 = 1;
+
+ TTestDfc* d1 = new TTestDfc(1, q, 1);
+ TEST_OOM(d1);
+ TEST_RESULT(!d1->IsIDFC(), "");
+ TTestDfc* d2 = new TTestDfc(2, q, 2);
+ TEST_OOM(d2);
+ TEST_RESULT(!d2->IsIDFC(), "");
+ TTestDfc* d3 = new TTestDfc(3, q, 2);
+ TEST_OOM(d3);
+ TEST_RESULT(!d3->IsIDFC(), "");
+ TTestDfc* d4 = new TTestDfc(4, q, 3);
+ TEST_OOM(d4);
+ TEST_RESULT(!d4->IsIDFC(), "");
+
+ TInt mode;
+ for (mode=-1; mode<=num_cpus; ++mode)
+ {
+ TEST_PRINT1("Mode %d", mode);
+ CHECK_EMPTY();
+ TEST_RESULT(!d1->Queued(), "");
+ QUEUE_DFC(d1, mode, TRUE);
+ TEST_RESULT(d1->Queued(), "");
+ QUEUE_DFC(d1, mode, FALSE);
+ TEST_RESULT(d1->Queued(), "");
+ QUEUE_DFC(d2, mode, TRUE);
+ QUEUE_DFC(d3, mode, TRUE);
+ QUEUE_DFC(d4, mode, TRUE);
+ CHECK_EMPTY();
+ NKern::Sleep(30);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 4);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 2);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 3);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 1);
+ CHECK_EMPTY();
+ QUEUE_DFC(d4, mode, TRUE);
+ QUEUE_DFC(d3, mode, TRUE);
+ QUEUE_DFC(d2, mode, TRUE);
+ QUEUE_DFC(d1, mode, TRUE);
+ CHECK_EMPTY();
+ NKern::Sleep(30);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 4);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 3);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 2);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 1);
+ CHECK_EMPTY();
+ QUEUE_DFC(d4, mode, TRUE);
+ QUEUE_DFC(d3, mode, TRUE);
+ QUEUE_DFC(d2, mode, TRUE);
+ QUEUE_DFC(d1, mode, TRUE);
+ TEST_RESULT(d4->Queued(), "");
+ TEST_RESULT(d4->Cancel(), "");
+ TEST_RESULT(!d4->Cancel(), "");
+ TEST_RESULT(!d4->Queued(), "");
+ CHECK_EMPTY();
+ NKern::Sleep(30);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 3);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 2);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 1);
+ CHECK_EMPTY();
+ QUEUE_DFC(d4, mode, TRUE);
+ QUEUE_DFC(d3, mode, TRUE);
+ QUEUE_DFC(d2, mode, TRUE);
+ QUEUE_DFC(d1, mode, TRUE);
+ TEST_RESULT(d3->Queued(), "");
+ TEST_RESULT(d3->Cancel(), "");
+ TEST_RESULT(!d3->Queued(), "");
+ CHECK_EMPTY();
+ NKern::Sleep(30);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 4);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 2);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 1);
+ CHECK_EMPTY();
+ QUEUE_DFC(d4, mode, TRUE);
+ QUEUE_DFC(d3, mode, TRUE);
+ QUEUE_DFC(d2, mode, TRUE);
+ QUEUE_DFC(d1, mode, TRUE);
+ TEST_RESULT(d3->Queued(), "");
+ TEST_RESULT(d2->Queued(), "");
+ TEST_RESULT(d3->Cancel(), "");
+ TEST_RESULT(d2->Cancel(), "");
+ TEST_RESULT(!d3->Queued(), "");
+ TEST_RESULT(!d2->Queued(), "");
+ CHECK_EMPTY();
+ NKern::Sleep(30);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 4);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 1);
+ CHECK_EMPTY();
+ QUEUE_DFC(d4, mode, TRUE);
+ QUEUE_DFC(d3, mode, TRUE);
+ QUEUE_DFC(d2, mode, TRUE);
+ QUEUE_DFC(d1, mode, TRUE);
+ TEST_RESULT(d3->Cancel(), "");
+ TEST_RESULT(d2->Cancel(), "");
+ TEST_RESULT(d4->Cancel(), "");
+ TEST_RESULT(d1->Cancel(), "");
+ CHECK_EMPTY();
+ NKern::Sleep(30);
+ CHECK_EMPTY();
+ QUEUE_DFC(d4, mode, TRUE);
+ QUEUE_DFC(d3, mode, TRUE);
+ QUEUE_DFC(d2, mode, TRUE);
+ QUEUE_DFC(d1, mode, TRUE);
+ TEST_RESULT(d1->Queued(), "");
+ TEST_RESULT(d3->Cancel(), "");
+ TEST_RESULT(d2->Cancel(), "");
+ TEST_RESULT(d4->Cancel(), "");
+ TEST_RESULT(d1->Cancel(), "");
+ TEST_RESULT(!d1->Queued(), "");
+ QUEUE_DFC(d1, mode, TRUE);
+ TEST_RESULT(d1->Queued(), "");
+ QUEUE_DFC(d1, mode, FALSE);
+ TEST_RESULT(d1->Queued(), "");
+ CHECK_EMPTY();
+ NKern::Sleep(30);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EThread, q, 1);
+ CHECK_EMPTY();
+ }
+
+ delete d4;
+ delete d3;
+ delete d2;
+ delete d1;
+ DestroyDfcQ(q);
+ }
+
+void DoDFCTest3(TInt aCpu)
+ {
+ TEST_PRINT1("DFCTest3 CPU %d", aCpu);
+ TInt num_cpus = NKern::NumberOfCpus();
+ TInt this_cpu = NKern::CurrentCpu();
+ TBool same_cpu = (aCpu==this_cpu);
+ TDfcQue* q;
+ q = CreateDfcQ("DfcQ2", 32, aCpu);
+ TEST_OOM(q);
+ q->iThread->iNThreadBaseSpare7 = 1;
+ TPauseDFC pauser(q);
+
+ TTestDfc* d1 = new TTestDfc(1, q, 1);
+ TEST_OOM(d1);
+ TEST_RESULT(!d1->IsIDFC(), "");
+ TTestDfc* d2 = new TTestDfc(2, q, 2);
+ TEST_OOM(d2);
+ TEST_RESULT(!d2->IsIDFC(), "");
+ TTestDfc* d3 = new TTestDfc(3, q, 2);
+ TEST_OOM(d3);
+ TEST_RESULT(!d3->IsIDFC(), "");
+ TTestDfc* d4 = new TTestDfc(4, q, 3);
+ TEST_OOM(d4);
+ TEST_RESULT(!d4->IsIDFC(), "");
+
+ TInt mode;
+ for (mode=-1; mode<=num_cpus; ++mode)
+ {
+ TEST_PRINT1("Mode %d", mode);
+ CHECK_EMPTY();
+ TEST_RESULT(!d1->Queued(), "");
+ QUEUE_DFC(d1, mode, TRUE);
+ if (!same_cpu)
+ while (d1->Queued()) {}
+ TEST_RESULT(!d1->Queued(), "");
+ QUEUE_DFC(d1, mode, TRUE);
+ if (!same_cpu)
+ while (d1->Queued()) {}
+ TEST_RESULT(!d1->Queued(), "");
+ QUEUE_DFC(d2, mode, TRUE);
+ if (!same_cpu)
+ while (d2->Queued()) {}
+ QUEUE_DFC(d3, mode, TRUE);
+ if (!same_cpu)
+ while (d3->Queued()) {}
+ QUEUE_DFC(d4, mode, TRUE);
+ if (!same_cpu)
+ while (d4->Queued()) {}
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 1);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 1);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 2);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 3);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 4);
+ CHECK_EMPTY();
+ QUEUE_DFC(d4, mode, TRUE);
+ QUEUE_DFC(d3, mode, TRUE);
+ QUEUE_DFC(d2, mode, TRUE);
+ QUEUE_DFC(d1, mode, TRUE);
+ if (!same_cpu)
+ while (d1->Queued()) {}
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 4);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 3);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 2);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 1);
+ CHECK_EMPTY();
+ QUEUE_DFC(d4, mode, TRUE);
+ QUEUE_DFC(d3, mode, TRUE);
+ QUEUE_DFC(d2, mode, TRUE);
+ QUEUE_DFC(d1, mode, TRUE);
+ if (!same_cpu)
+ while (d1->Queued()) {}
+ TEST_RESULT(!d4->Queued(), "");
+ TEST_RESULT(!d4->Cancel(), "");
+ TEST_RESULT(!d4->Queued(), "");
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 4);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 3);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 2);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 1);
+ CHECK_EMPTY();
+ pauser.Pause();
+ CHECK_EMPTY();
+ TEST_RESULT(!d1->Queued(), "");
+ QUEUE_DFC(d1, mode, TRUE);
+ TEST_RESULT(d1->Queued(), "");
+ QUEUE_DFC(d1, mode, FALSE);
+ TEST_RESULT(d1->Queued(), "");
+ QUEUE_DFC(d2, mode, TRUE);
+ QUEUE_DFC(d3, mode, TRUE);
+ QUEUE_DFC(d4, mode, TRUE);
+ QUEUE_DFC(d4, mode, FALSE);
+ CHECK_EMPTY();
+ pauser.Release();
+ pauser.Pause();
+ pauser.Release();
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 4);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 2);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 3);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 1);
+ CHECK_EMPTY();
+ pauser.Pause();
+ CHECK_EMPTY();
+ TEST_RESULT(!d1->Queued(), "");
+ QUEUE_DFC(d1, mode, TRUE);
+ TEST_RESULT(d1->Queued(), "");
+ QUEUE_DFC(d1, mode, FALSE);
+ TEST_RESULT(d1->Queued(), "");
+ QUEUE_DFC(d4, mode, TRUE);
+ QUEUE_DFC(d3, mode, TRUE);
+ QUEUE_DFC(d2, mode, TRUE);
+ CHECK_EMPTY();
+ pauser.Release();
+ pauser.Pause();
+ pauser.Release();
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 4);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 3);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 2);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 1);
+ CHECK_EMPTY();
+ pauser.Pause();
+ CHECK_EMPTY();
+ TEST_RESULT(!d1->Queued(), "");
+ QUEUE_DFC(d1, mode, TRUE);
+ TEST_RESULT(d1->Queued(), "");
+ QUEUE_DFC(d1, mode, FALSE);
+ TEST_RESULT(d1->Queued(), "");
+ QUEUE_DFC(d2, mode, TRUE);
+ QUEUE_DFC(d3, mode, TRUE);
+ QUEUE_DFC(d4, mode, TRUE);
+ CHECK_EMPTY();
+ TEST_RESULT(d1->Cancel(), "");
+ TEST_RESULT(!d1->Queued(), "");
+ pauser.Release();
+ pauser.Pause();
+ pauser.Release();
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 4);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 2);
+ CHECK_FIRST_ENTRY(aCpu, NKern::EThread, q, 3);
+ CHECK_EMPTY();
+ }
+
+ delete d4;
+ delete d3;
+ delete d2;
+ delete d1;
+ DestroyDfcQ(q);
+ }
+
+TBool QueueIDfc(TDfc* aDfc, TInt aMode, TBool aExRet)
+ {
+ if (aMode==0)
+ return !aExRet == !aDfc->RawAdd();
+ else if (aMode>0)
+ {
+ TTestDfc::Last = 0xffffffffu;
+ TAddDfc adder;
+ TInt cpu = (aMode&0xff) - 1;
+ adder.Add(aDfc, 1u<<cpu);
+ adder.WaitCompletion();
+ if (!(aMode&0x100))
+ {
+ while (TTestDfc::Last != (TUint32)aDfc->iPtr)
+ {}
+ }
+ return TRUE;
+ }
+ else if (aMode==-1)
+ {
+ NKern::Lock();
+ TBool ret = aDfc->Add();
+ NKern::Unlock();
+ return !aExRet == !ret;
+ }
+ return FALSE;
+ }
+
+#define QUEUE_IDFC(dfc, mode, exret) TEST_RESULT(QueueIDfc(dfc,mode,exret),"")
+
+void DoIDFCTest1()
+ {
+ TEST_PRINT("IDFCTest1");
+
+ TInt num_cpus = NKern::NumberOfCpus();
+ TInt this_cpu = NKern::CurrentCpu();
+
+ TTestDfc* d1 = new TTestDfc(1);
+ TEST_OOM(d1);
+ TEST_RESULT(d1->IsIDFC(), "");
+ TTestDfc* d2 = new TTestDfc(2);
+ TEST_OOM(d2);
+ TEST_RESULT(d2->IsIDFC(), "");
+ TTestDfc* d3 = new TTestDfc(3);
+ TEST_OOM(d3);
+ TEST_RESULT(d3->IsIDFC(), "");
+ TTestDfc* d4 = new TTestDfc(4);
+ TEST_OOM(d4);
+ TEST_RESULT(d4->IsIDFC(), "");
+
+ TInt mode;
+ for (mode=-1; mode<=num_cpus; ++mode)
+ {
+ TInt xcpu = (mode>0) ? (mode-1) : this_cpu;
+ TEST_PRINT1("Mode %d", mode);
+ CHECK_EMPTY();
+ TEST_RESULT(!d1->Queued(), "");
+ QUEUE_IDFC(d1, mode, TRUE);
+ TEST_RESULT(!d1->Queued(), "");
+ QUEUE_IDFC(d1, mode, TRUE);
+ TEST_RESULT(!d1->Queued(), "");
+ QUEUE_IDFC(d2, mode, TRUE);
+ QUEUE_IDFC(d3, mode, TRUE);
+ QUEUE_IDFC(d4, mode, TRUE);
+ CHECK_FIRST_ENTRY(xcpu, NKern::EIDFC, 0, 1);
+ CHECK_FIRST_ENTRY(xcpu, NKern::EIDFC, 0, 1);
+ CHECK_FIRST_ENTRY(xcpu, NKern::EIDFC, 0, 2);
+ CHECK_FIRST_ENTRY(xcpu, NKern::EIDFC, 0, 3);
+ CHECK_FIRST_ENTRY(xcpu, NKern::EIDFC, 0, 4);
+ CHECK_EMPTY();
+ QUEUE_IDFC(d4, mode, TRUE);
+ QUEUE_IDFC(d3, mode, TRUE);
+ QUEUE_IDFC(d2, mode, TRUE);
+ QUEUE_IDFC(d1, mode, TRUE);
+ CHECK_FIRST_ENTRY(xcpu, NKern::EIDFC, 0, 4);
+ CHECK_FIRST_ENTRY(xcpu, NKern::EIDFC, 0, 3);
+ CHECK_FIRST_ENTRY(xcpu, NKern::EIDFC, 0, 2);
+ CHECK_FIRST_ENTRY(xcpu, NKern::EIDFC, 0, 1);
+ CHECK_EMPTY();
+ }
+ TInt irq = NKern::DisableAllInterrupts();
+ TEST_RESULT(d1->RawAdd(), "");
+ TEST_RESULT(d1->Queued(), "");
+ CHECK_EMPTY();
+ NKern::RestoreInterrupts(irq);
+ TEST_RESULT(!d1->Queued(), "");
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EIDFC, 0, 1);
+
+ NKern::Lock();
+ TEST_RESULT(d1->Add(), "");
+ TEST_RESULT(d3->Add(), "");
+ TEST_RESULT(d2->Add(), "");
+ TEST_RESULT(d4->Add(), "");
+ TEST_RESULT(!d1->Add(), "");
+ TEST_RESULT(d1->Queued(), "");
+ TEST_RESULT(d2->Queued(), "");
+ TEST_RESULT(d3->Queued(), "");
+ TEST_RESULT(d4->Queued(), "");
+ CHECK_EMPTY();
+ NKern::Unlock();
+ TEST_RESULT(!d1->Queued(), "");
+ TEST_RESULT(!d2->Queued(), "");
+ TEST_RESULT(!d3->Queued(), "");
+ TEST_RESULT(!d4->Queued(), "");
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EIDFC, 0, 1);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EIDFC, 0, 3);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EIDFC, 0, 2);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EIDFC, 0, 4);
+ CHECK_EMPTY();
+
+ NKern::Lock();
+ TEST_RESULT(d1->Add(), "");
+ TEST_RESULT(d3->Add(), "");
+ TEST_RESULT(d2->Add(), "");
+ TEST_RESULT(d4->Add(), "");
+ TEST_RESULT(d1->Queued(), "");
+ TEST_RESULT(d2->Queued(), "");
+ TEST_RESULT(d3->Queued(), "");
+ TEST_RESULT(d4->Queued(), "");
+ TEST_RESULT(d3->Cancel(), "");
+ TEST_RESULT(!d3->Queued(), "");
+ TEST_RESULT(!d3->Cancel(), "");
+ CHECK_EMPTY();
+ NKern::Unlock();
+ TEST_RESULT(!d1->Queued(), "");
+ TEST_RESULT(!d2->Queued(), "");
+ TEST_RESULT(!d4->Queued(), "");
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EIDFC, 0, 1);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EIDFC, 0, 2);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EIDFC, 0, 4);
+ CHECK_EMPTY();
+
+ NKern::Lock();
+ TEST_RESULT(d1->Add(), "");
+ TEST_RESULT(d3->Add(), "");
+ TEST_RESULT(d2->Add(), "");
+ TEST_RESULT(d4->Add(), "");
+ TEST_RESULT(d1->Queued(), "");
+ TEST_RESULT(d2->Queued(), "");
+ TEST_RESULT(d3->Queued(), "");
+ TEST_RESULT(d4->Queued(), "");
+ TEST_RESULT(d3->Cancel(), "");
+ TEST_RESULT(d1->Cancel(), "");
+ TEST_RESULT(d2->Cancel(), "");
+ TEST_RESULT(d4->Cancel(), "");
+ TEST_RESULT(!d1->Queued(), "");
+ TEST_RESULT(!d2->Queued(), "");
+ TEST_RESULT(!d3->Queued(), "");
+ TEST_RESULT(!d4->Queued(), "");
+ CHECK_EMPTY();
+ NKern::Unlock();
+ CHECK_EMPTY();
+
+ TPauseIDFC pauser;
+ TInt cpu;
+ for_each_cpu(cpu)
+ {
+ if (cpu == this_cpu)
+ continue;
+ mode = cpu + 0x101;
+ TEST_PRINT1("CPU %d", cpu);
+ pauser.Pause(cpu);
+ CHECK_EMPTY();
+ TEST_RESULT(!d1->Queued(), "");
+ QUEUE_IDFC(d1, mode, TRUE);
+ TEST_RESULT(d1->Queued(), "");
+ QUEUE_IDFC(d1, mode, FALSE);
+ TEST_RESULT(d1->Queued(), "");
+ QUEUE_IDFC(d2, mode, TRUE);
+ QUEUE_IDFC(d3, mode, TRUE);
+ QUEUE_IDFC(d4, mode, TRUE);
+ CHECK_EMPTY();
+ pauser.Release();
+ pauser.Pause(cpu);
+ pauser.Release();
+ CHECK_FIRST_ENTRY(cpu, NKern::EIDFC, 0, 1);
+ CHECK_FIRST_ENTRY(cpu, NKern::EIDFC, 0, 2);
+ CHECK_FIRST_ENTRY(cpu, NKern::EIDFC, 0, 3);
+ CHECK_FIRST_ENTRY(cpu, NKern::EIDFC, 0, 4);
+ CHECK_EMPTY();
+ pauser.Pause(cpu);
+ CHECK_EMPTY();
+ TEST_RESULT(!d1->Queued(), "");
+ QUEUE_IDFC(d1, mode, TRUE);
+ TEST_RESULT(d1->Queued(), "");
+ QUEUE_IDFC(d2, mode, TRUE);
+ QUEUE_IDFC(d3, mode, TRUE);
+ QUEUE_IDFC(d4, mode, TRUE);
+ TEST_RESULT(d1->Cancel(), "");
+ TEST_RESULT(!d1->Queued(), "");
+ TEST_RESULT(!d1->Cancel(), "");
+ CHECK_EMPTY();
+ pauser.Release();
+ pauser.Pause(cpu);
+ pauser.Release();
+ CHECK_FIRST_ENTRY(cpu, NKern::EIDFC, 0, 2);
+ CHECK_FIRST_ENTRY(cpu, NKern::EIDFC, 0, 3);
+ CHECK_FIRST_ENTRY(cpu, NKern::EIDFC, 0, 4);
+ CHECK_EMPTY();
+ pauser.Pause(cpu);
+ CHECK_EMPTY();
+ TEST_RESULT(!d1->Queued(), "");
+ QUEUE_IDFC(d1, mode, TRUE);
+ TEST_RESULT(d1->Queued(), "");
+ QUEUE_IDFC(d2, mode, TRUE);
+ QUEUE_IDFC(d3, mode, TRUE);
+ QUEUE_IDFC(d4, mode, TRUE);
+ TEST_RESULT(d1->Cancel(), "");
+ TEST_RESULT(!d1->Queued(), "");
+ TEST_RESULT(d4->Cancel(), "");
+ TEST_RESULT(d2->Cancel(), "");
+ TEST_RESULT(d3->Cancel(), "");
+ CHECK_EMPTY();
+ pauser.Release();
+ pauser.Pause(cpu);
+ pauser.Release();
+ CHECK_EMPTY();
+ }
+
+ delete d4;
+ delete d3;
+ delete d2;
+ delete d1;
+ }
+
+void DoIdleDFCTest1(TInt aCpu)
+ {
+#ifdef __SMP__
+ TEST_PRINT2("IdleDFCTest1 CPU %d (%08x)", aCpu, TheScheduler.iCpusNotIdle);
+#else
+ TEST_PRINT1("IdleDFCTest1 CPU %d (%08x)", aCpu);
+#endif
+// TInt num_cpus = NKern::NumberOfCpus();
+ TInt this_cpu = NKern::CurrentCpu();
+ TBool same_cpu = (aCpu==this_cpu);
+ TDfcQue* q = 0;
+ TPauseDFC* pauser = 0;
+ if (!same_cpu)
+ {
+ q = CreateDfcQ("DfcQ3", 1, aCpu);
+ TEST_OOM(q);
+ pauser = new TPauseDFC(q);
+ TEST_OOM(pauser);
+ }
+
+ TTestDfc* d1 = new TTestDfc(1);
+ TEST_OOM(d1);
+ TEST_RESULT(d1->IsIDFC(), "");
+ TTestDfc* d2 = new TTestDfc(2);
+ TEST_OOM(d2);
+ TEST_RESULT(d2->IsIDFC(), "");
+ TTestDfc* d3 = new TTestDfc(3);
+ TEST_OOM(d3);
+ TEST_RESULT(d3->IsIDFC(), "");
+ TTestDfc* d4 = new TTestDfc(4);
+ TEST_OOM(d4);
+ TEST_RESULT(d4->IsIDFC(), "");
+
+ TEST_RESULT(!d1->Queued(), "");
+ TEST_RESULT(d1->QueueOnIdle(), "");
+ TEST_RESULT(d1->Queued(), "");
+ TEST_RESULT(!d1->QueueOnIdle(), "");
+ CHECK_EMPTY();
+ if (pauser)
+ pauser->BusyPause();
+ NKern::Sleep(1);
+ if (pauser)
+ TEST_RESULT(d1->Queued(), "");
+ else
+ {
+ TEST_RESULT(!d1->Queued(), "");
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EIDFC, 0, 1);
+ }
+ CHECK_EMPTY();
+ TBool ret = d1->QueueOnIdle();
+ TEST_RESULT(pauser?!ret:ret, "");
+ TEST_RESULT(d1->Queued(), "");
+ TEST_RESULT(d1->Cancel(), "");
+ TEST_RESULT(!d1->Queued(), "");
+ CHECK_EMPTY();
+ NKern::Sleep(1);
+ CHECK_EMPTY();
+ if (pauser)
+ pauser->Release();
+ TEST_RESULT(d4->QueueOnIdle(), "");
+ TEST_RESULT(d3->QueueOnIdle(), "");
+ TEST_RESULT(d1->QueueOnIdle(), "");
+ TEST_RESULT(d2->QueueOnIdle(), "");
+ TEST_RESULT(d3->Cancel(), "");
+ CHECK_EMPTY();
+ TInt xcpu = this_cpu;
+ if (pauser)
+ {
+ xcpu = aCpu;
+ pauser->Pause(1);
+ pauser->Release();
+ }
+ else
+ NKern::Sleep(1);
+ CHECK_FIRST_ENTRY(xcpu, NKern::EIDFC, 0, 4);
+ CHECK_FIRST_ENTRY(xcpu, NKern::EIDFC, 0, 1);
+ CHECK_FIRST_ENTRY(xcpu, NKern::EIDFC, 0, 2);
+ CHECK_EMPTY();
+
+ delete d4;
+ delete d3;
+ delete d2;
+ delete d1;
+ delete pauser;
+ if (q)
+ DestroyDfcQ(q);
+ }
+
+TDfc* Dfcs[6];
+NFastSemaphore* IdleDFCTest2Fs;
+
+void IdleDFCTest2Fn(TAny* a)
+ {
+ TUint32 id = (TUint32)a;
+ if (id==1)
+ {
+ TEST_RESULT(Dfcs[1]->Cancel(), "");
+ TEST_RESULT(Dfcs[3]->QueueOnIdle(), "");
+ TEST_RESULT(Dfcs[4]->QueueOnIdle(), "");
+ TEST_RESULT(Dfcs[5]->QueueOnIdle(), "");
+ }
+ if (id==1 || id==4)
+ IdleDFCTest2Fs->Signal();
+ if (id==3)
+ {
+ TEST_RESULT(Dfcs[5]->Cancel(), "");
+ }
+ TTestDfc::Run(a);
+ }
+
+void DoIdleDFCTest2()
+ {
+ TEST_PRINT("IdleDFCTest2");
+ NFastSemaphore sem(0);
+ TInt this_cpu = NKern::CurrentCpu();
+ TInt i;
+ for (i=0; i<6; ++i)
+ {
+ Dfcs[i] = new TDfc(&IdleDFCTest2Fn, (TAny*)(i+1));
+ TEST_OOM(Dfcs[i]);
+ }
+ TEST_RESULT(Dfcs[0]->QueueOnIdle(), "");
+ TEST_RESULT(Dfcs[1]->QueueOnIdle(), "");
+ TEST_RESULT(Dfcs[2]->QueueOnIdle(), "");
+ IdleDFCTest2Fs = &sem;
+ CHECK_EMPTY();
+ NKern::FSWait(&sem);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EIDFC, 0, 1);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EIDFC, 0, 3);
+ CHECK_EMPTY();
+ NKern::FSWait(&sem);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EIDFC, 0, 4);
+ CHECK_FIRST_ENTRY(this_cpu, NKern::EIDFC, 0, 5);
+ CHECK_EMPTY();
+ for (i=0; i<6; ++i)
+ delete Dfcs[i];
+ }
+
+#ifdef __SMP__
+
+class TDfcStress;
+class TDfcX
+ {
+public:
+ enum
+ {
+ EFlag_IdleDFC=1,
+ EFlag_IDFC=2,
+ EFlag_DFC=4,
+ EFlag_Timer=8,
+ EFlag_Tied=16
+ };
+public:
+ TDfcX();
+ ~TDfcX();
+ static void IDfcFn(TAny*);
+ static void DfcFn(TAny*);
+ static void TimerIsrFn(TAny*);
+ static void TimerDfcFn(TAny*);
+ void Update();
+ TBool Add(TAny* a=0);
+ TBool Cancel(TAny* a=0);
+ TBool Enque(TAny* a=0);
+ TBool QueueOnIdle(TAny* a=0);
+ TBool SafeAdd();
+ TBool SafeCancel();
+ TBool SafeEnque();
+ TBool SafeQueueOnIdle();
+ void CreateDfcOrTimer();
+ void GetDesc(char* aDesc);
+ inline TBool IsIDFC()
+ {return (iFlags & (EFlag_IDFC|EFlag_Timer)) == EFlag_IDFC;}
+ inline TBool IsIdler()
+ {return iFlags & EFlag_IdleDFC;}
+ void ThreadActivity();
+public:
+ union
+ {
+ TDfc* volatile iDfc;
+ NTimer* volatile iTimer;
+ };
+ TDfcQue* iDfcQ;
+ TUint32 iQueueCount;
+ TUint32 iRunCount[KMaxCpus];
+ TUint32 iCancelCount;
+ TUint32 iExclusionFailCount;
+ TUint32 iId;
+ TUint32 iFlags;
+ TDfcStress* iS;
+ TUint64 iTimeQueued;
+ TUint64 iMaxTime;
+ TUint64 iSumTime;
+ TSpinLock iSpinLock;
+ NSchedulable* iXTied;
+ volatile TUint32* iExclusionCheck;
+ };
+
+TDfcX::TDfcX()
+ : iSpinLock(TSpinLock::EOrderGenericIrqLow1)
+ {
+ memclr(this,sizeof(TDfcX));
+ new (&iSpinLock) TSpinLock(TSpinLock::EOrderGenericIrqLow1);
+ }
+
+TDfcX::~TDfcX()
+ {
+ TAny* p = __e32_atomic_swp_ord_ptr(&iDfc, 0);
+ if (p)
+ {
+ if (iFlags & EFlag_Timer)
+ delete ((NTimer*)p);
+ else
+ delete ((TDfc*)p);
+ }
+ }
+
+class TDfcStress
+ {
+public:
+ enum
+ {
+ EMode_Wait =0x00000001u,
+ EMode_AllowCancel =0x00000002u,
+ EMode_AllowIdle =0x00000004u,
+ EMode_AllowEnque =0x00000008u,
+ EMode_Recycle =0x00000010u,
+ EMode_UseTied =0x00000020u,
+ EMode_Migrate =0x00000040u,
+ EMode_SelfMigrate =0x00000080u,
+ EMode_Exit =0x80000000u
+ };
+public:
+ enum {EMaxDfc=48, EMaxDfcQ=8};
+
+ TDfcStress();
+ static TDfcStress* New(TInt aNumDfc, TInt aNumDfcQ, TBool aTimerTest);
+ void Create();
+ static void DoThreadFn(TAny*);
+ void ThreadFn();
+ static void BackStopFn(TAny*);
+ void Run();
+ void Close();
+ void DoTestPhase(TInt aMode, TInt aTime, TInt aCount);
+ void GetModeText(char* aName);
+public:
+ TDfcX* NewDfc(TUint32 aId, TUint32 aFlags, TInt aDfcQ);
+ TDfcX* NewIDfc(TUint32 aId, TUint32 aFlags, NSchedulable* aTied=0);
+ TDfcX* NewTimer(TUint32 aId, TUint32 aFlags, TInt aDfcQ, NSchedulable* aTied);
+ void CreateDfc(TUint32 aId);
+public:
+ TInt iNumDfc;
+ TDfcX* iDfcX[EMaxDfc];
+ TInt iNumDfcQ;
+ TBool iTimerTest;
+ TDfcQue* iDfcQ[EMaxDfcQ];
+ NThread* iThread[KMaxCpus];
+ volatile TBool iStop;
+ volatile TInt iRunning;
+ volatile TInt iMode;
+ NFastSemaphore* iExitSem;
+ TDfcX* volatile iGarbage;
+ TUint32 iRandomTimeLimit;
+ TDfc* iBackStopIdleDfc;
+ TDfcQue* iBackStopIdleDfcQ;
+ };
+
+void TDfcX::Update()
+ {
+ TUint32 exc0 = 0;
+ TUint32 exc1 = 0;
+ if (iExclusionCheck)
+ exc0 = *iExclusionCheck;
+ TInt cpu = NKern::CurrentCpu();
+ __e32_atomic_add_ord32(&iRunCount[cpu], 1);
+ TInt ctx = NKern::CurrentContext();
+ TBool is_idfc = IsIDFC();
+ TBool is_timer = iFlags & EFlag_Timer;
+ TBool is_tied = iFlags & EFlag_Tied;
+ if ((is_idfc||is_timer) && is_tied && !(iS->iMode & (TDfcStress::EMode_Migrate|TDfcStress::EMode_SelfMigrate)))
+ {
+ TInt cpu = NKern::CurrentCpu();
+ TInt xcpu = iXTied->iCpuAffinity;
+ if (cpu != xcpu)
+ {
+ __crash();
+ }
+ }
+ TInt irq=0;
+ if ((ctx!=NKern::EThread) && (iS->iMode & TDfcStress::EMode_AllowCancel))
+ irq = iSpinLock.LockIrqSave();
+ TUint64 now = fast_counter();
+ TUint64 delta = now - iTimeQueued;
+ if (TInt64(delta)>=0)
+ {
+ if (delta > iMaxTime)
+ iMaxTime = delta;
+ iSumTime += delta;
+ }
+ if ((ctx!=NKern::EThread) && (iS->iMode & TDfcStress::EMode_AllowCancel))
+ iSpinLock.UnlockIrqRestore(irq);
+ if (IsIdler())
+ {
+ TInt i;
+ NKern::Lock();
+ for (i=0; i<KMaxCpus; ++i)
+ {
+ NThread* t = iS->iThread[i];
+ if (t)
+ t->iRequestSemaphore.Reset();
+ }
+ NKern::Unlock();
+ iS->iBackStopIdleDfc->Cancel();
+ }
+ if (iExclusionCheck)
+ exc1 = *iExclusionCheck;
+ if (exc0!=exc1)
+ __e32_atomic_add_ord32(&iExclusionFailCount, 1);
+ }
+
+void TDfcStress::BackStopFn(TAny* a)
+ {
+ TDfcStress* s = (TDfcStress*)a;
+ TInt i;
+ NKern::Lock();
+ for (i=0; i<KMaxCpus; ++i)
+ {
+ NThread* t = s->iThread[i];
+ if (t)
+ t->iRequestSemaphore.Reset();
+ }
+ NKern::Unlock();
+ }
+
+void TDfcX::IDfcFn(TAny* a)
+ {
+ TDfcX* d = (TDfcX*)a;
+ d->Update();
+ }
+
+void TDfcX::DfcFn(TAny* a)
+ {
+ TDfcX* d = (TDfcX*)a;
+ d->ThreadActivity();
+ d->Update();
+ d->ThreadActivity();
+ }
+
+void TDfcX::TimerDfcFn(TAny* a)
+ {
+ TDfcX* d = (TDfcX*)a;
+ d->ThreadActivity();
+ d->Update();
+ d->ThreadActivity();
+ }
+
+void TDfcX::TimerIsrFn(TAny* a)
+ {
+ TDfcX* d = (TDfcX*)a;
+ d->Update();
+ }
+
+void TDfcX::ThreadActivity()
+ {
+ TInt ncpus = NKern::NumberOfCpus();
+ TInt ocpu = NKern::CurrentCpu();
+ NThread* pC = NKern::CurrentThread();
+ volatile TUint32* pX = (volatile TUint32*)&pC->iRunCount32[1]; // HACK!
+ TInt cpu = ocpu;
+ TInt i;
+ if ((iS->iMode & TDfcStress::EMode_SelfMigrate) && !pC->iEvents.IsEmpty())
+ {
+ for (i=0; i<ncpus; ++i)
+ {
+ ++*pX;
+ if (++cpu == ncpus)
+ cpu = 0;
+ NKern::ThreadSetCpuAffinity(pC, cpu);
+ }
+ }
+ else
+ {
+ ++*pX;
+ ++*pX;
+ ++*pX;
+ ++*pX;
+ ++*pX;
+ ++*pX;
+ ++*pX;
+ ++*pX;
+ }
+ ++*pX;
+ ++*pX;
+ ++*pX;
+ }
+
+TBool TDfcX::Add(TAny* a)
+ {
+ TBool is_timer = iFlags & EFlag_Timer;
+ if (!a)
+ a = iDfc;
+ TUint64 time = fast_counter();
+ TBool ok;
+ if (is_timer)
+ ok = ((NTimer*)a)->OneShot(1) == KErrNone;
+ else
+ ok = ((TDfc*)a)->Add();
+ if (ok)
+ {
+ iTimeQueued = time;
+ __e32_atomic_add_ord32(&iQueueCount, 1);
+ }
+ return ok;
+ }
+
+TBool TDfcX::Cancel(TAny* a)
+ {
+ TBool is_timer = iFlags & EFlag_Timer;
+ if (!a)
+ a = iDfc;
+ TBool ok;
+ if (is_timer)
+ ok = ((NTimer*)a)->Cancel();
+ else
+ ok = ((TDfc*)a)->Cancel();
+ if (ok)
+ __e32_atomic_add_ord32(&iCancelCount, 1);
+ return ok;
+ }
+
+TBool TDfcX::Enque(TAny* a)
+ {
+ TBool is_timer = iFlags & EFlag_Timer;
+ if (!a)
+ a = iDfc;
+ TUint64 time = fast_counter();
+ TBool ok;
+ if (is_timer)
+ ok = ((NTimer*)a)->Again(2) == KErrNone;
+ else
+ ok = ((TDfc*)a)->Enque();
+ if (ok)
+ {
+ iTimeQueued = time;
+ __e32_atomic_add_ord32(&iQueueCount, 1);
+ }
+ return ok;
+ }
+
+TBool TDfcX::QueueOnIdle(TAny* a)
+ {
+ TBool is_timer = iFlags & EFlag_Timer;
+ if (is_timer)
+ return FALSE;
+ if (!a)
+ a = iDfc;
+ TUint64 time = fast_counter();
+ TBool ok = ((TDfc*)a)->QueueOnIdle();
+ if (ok)
+ {
+ iTimeQueued = time;
+ __e32_atomic_add_ord32(&iQueueCount, 1);
+ }
+ return ok;
+ }
+
+TBool TDfcX::SafeAdd()
+ {
+ TBool ret = FALSE;
+ TUint32 x = set_bit0_if_nonnull((TUint32&)iDfc);
+ if (x && !(x&1))
+ {
+ TDfc* p = (TDfc*)x;
+ ret = Add(p);
+ flip_bit0((TUint32&)iDfc);
+ }
+ return ret;
+ }
+
+TBool TDfcX::SafeEnque()
+ {
+ TBool ret = FALSE;
+ TUint32 x = set_bit0_if_nonnull((TUint32&)iDfc);
+ if (x && !(x&1))
+ {
+ TDfc* p = (TDfc*)x;
+ ret = Enque(p);
+ flip_bit0((TUint32&)iDfc);
+ }
+ return ret;
+ }
+
+TBool TDfcX::SafeQueueOnIdle()
+ {
+ TBool ret = FALSE;
+ TUint32 x = set_bit0_if_nonnull((TUint32&)iDfc);
+ if (x && !(x&1))
+ {
+ TDfc* p = (TDfc*)x;
+ ret = QueueOnIdle(p);
+ flip_bit0((TUint32&)iDfc);
+ }
+ return ret;
+ }
+
+TBool TDfcX::SafeCancel()
+ {
+ TBool ret = FALSE;
+ TUint32 x = swap_out_if_bit0_clear((TUint32&)iDfc);
+ if (x && !(x&1))
+ {
+ if (iFlags & EFlag_Timer)
+ {
+ NTimer* p = (NTimer*)x;
+ ret = Cancel(p);
+ p->~NTimer();
+ memset(p, 0xbb, sizeof(NTimer));
+ free(p);
+ }
+ else
+ {
+ TDfc* p = (TDfc*)x;
+ ret = Cancel(p);
+ p->~TDfc();
+ memset(p, 0xbb, sizeof(TDfc));
+ free(p);
+ }
+ CreateDfcOrTimer();
+ }
+ return ret;
+ }
+
+void TDfcX::GetDesc(char* a)
+ {
+ memset(a, 0x20, 8);
+ if (iFlags & EFlag_Timer)
+ *a++ = 'T';
+ if (iFlags & EFlag_IDFC)
+ *a++ = 'I';
+ if (iFlags & EFlag_DFC)
+ *a++ = 'D';
+ if (iFlags & EFlag_IdleDFC)
+ *a++ = 'i';
+ if (iFlags & EFlag_Tied)
+ *a++ = 't';
+ }
+
+TDfcStress::TDfcStress()
+ {
+ memclr(this, sizeof(*this));
+ }
+
+TDfcX* TDfcStress::NewDfc(TUint32 aId, TUint32 aFlags, TInt aDfcQ)
+ {
+ TDfcX* d = new TDfcX;
+ TEST_OOM(d);
+ d->iId = aId;
+ d->iFlags = aFlags;
+ d->iS = this;
+
+ d->iDfcQ = iDfcQ[aDfcQ];
+ d->CreateDfcOrTimer();
+ return d;
+ }
+
+TDfcX* TDfcStress::NewIDfc(TUint32 aId, TUint32 aFlags, NSchedulable* aTied)
+ {
+ TDfcX* d = new TDfcX;
+ TEST_OOM(d);
+ d->iId = aId;
+ d->iFlags = aFlags;
+ d->iS = this;
+
+ d->iXTied = aTied;
+ d->CreateDfcOrTimer();
+ return d;
+ }
+
+TDfcX* TDfcStress::NewTimer(TUint32 aId, TUint32 aFlags, TInt aDfcQ, NSchedulable* aTied)
+ {
+ TDfcX* d = new TDfcX;
+ TEST_OOM(d);
+ d->iId = aId;
+ d->iFlags = aFlags;
+ d->iS = this;
+
+ d->iDfcQ = (aFlags & TDfcX::EFlag_DFC) ? iDfcQ[aDfcQ] : 0;
+ d->iXTied = (aFlags & TDfcX::EFlag_Tied) ? aTied : 0;
+ d->CreateDfcOrTimer();
+ return d;
+ }
+
+
+void TDfcX::CreateDfcOrTimer()
+ {
+// volatile TUint32* xc = 0;
+ NThreadBase* t = iS->iDfcQ[0]->iThread;
+ volatile TUint32* xc = &t->iRunCount32[1]; // HACK!
+ if (!(iFlags & EFlag_Timer))
+ {
+ TDfc* d = 0;
+ if (!(iFlags & EFlag_IDFC))
+ {
+ d = new TDfc(&TDfcX::DfcFn, this, iDfcQ, 1);
+ xc = (volatile TUint32*)&iDfcQ->iThread->iRunCount32[1];
+ }
+ else if (iFlags & EFlag_Tied)
+ {
+ d = new TDfc(iXTied, &TDfcX::IDfcFn, this);
+ xc = (volatile TUint32*)&iXTied->iRunCount32[1];
+ }
+ else
+ d = new TDfc(&TDfcX::IDfcFn, this);
+ __NK_ASSERT_ALWAYS(d!=0);
+ __e32_atomic_store_rel_ptr(&iDfc, d);
+ }
+ else
+ {
+ NTimer* tmr = 0;
+ if (iFlags & EFlag_DFC)
+ {
+ tmr = new NTimer(&TDfcX::TimerDfcFn, this, iDfcQ, 1);
+ xc = (volatile TUint32*)&iDfcQ->iThread->iRunCount32[1];
+ }
+ else if (iFlags & EFlag_Tied)
+ {
+ tmr = new NTimer(iXTied, &TDfcX::TimerIsrFn, this);
+ xc = (volatile TUint32*)&iXTied->iRunCount32[1];
+ }
+ else
+ tmr = new NTimer(&TDfcX::TimerIsrFn, this);
+ __NK_ASSERT_ALWAYS(tmr!=0);
+ __e32_atomic_store_rel_ptr(&iTimer, tmr);
+ }
+ iExclusionCheck = xc;
+ }
+
+TDfcStress* TDfcStress::New(TInt aNumDfc, TInt aNumDfcQ, TBool aTimerTest)
+ {
+ TDfcStress* p = new TDfcStress;
+ TEST_OOM(p);
+ p->iTimerTest = aTimerTest;
+ p->iNumDfc = aNumDfc;
+ p->iNumDfcQ = aNumDfcQ;
+ p->Create();
+ return p;
+ }
+
+void TDfcStress::Create()
+ {
+DEBUGPRINT("TDfcStress @ %08x", this);
+ TInt i;
+ TInt num_cpus = NKern::NumberOfCpus();
+ TInt cpu = 0;
+ iExitSem = new NFastSemaphore(0);
+ TEST_OOM(iExitSem);
+ for (i=0; i<iNumDfcQ; ++i)
+ {
+ char c[8] = "DFCQ*";
+ c[4] = (char)('0'+i);
+ TDfcQue* q = CreateDfcQ(c, 32, cpu);
+ TEST_OOM(q);
+ iDfcQ[i] = q;
+ if (++cpu == num_cpus)
+ cpu = 0;
+ NThreadBase* t = q->iThread;
+DEBUGPRINT("DfcQ %2d @ %08x Thread @%08x Stack %08x+%08x", i, iDfcQ[i], t, t->iStackBase, t->iStackSize);
+ }
+ iBackStopIdleDfcQ = CreateDfcQ("BackStop", 1, 0);
+ TEST_OOM(iBackStopIdleDfcQ);
+ iBackStopIdleDfc = new TDfc(&BackStopFn, this, iBackStopIdleDfcQ, 1);
+ TEST_OOM(iBackStopIdleDfc);
+ for (i=0; i<num_cpus; ++i)
+ {
+ char c[8] = "THRD*";
+ c[4] = (char)('0'+i);
+ NThread* t = CreateUnresumedThreadSignalOnExit(c, &DoThreadFn, 11, this, 0, -1, iExitSem, i);
+ TEST_OOM(t);
+ iThread[i] = t;
+DEBUGPRINT("Thread %2d @ %08x (Stack %08x+%08x)", i, iThread[i], t->iStackBase, t->iStackSize);
+ }
+ for (i=0; i<iNumDfc; ++i)
+ {
+ CreateDfc(i);
+DEBUGPRINT("DfcX %2d @ %08x (DFC @ %08x)", i, iDfcX[i], iDfcX[i]->iDfc);
+ }
+ }
+
+void TDfcStress::CreateDfc(TUint32 aId)
+ {
+ TUint32 type = aId & 7;
+ TUint32 q = aId % iNumDfcQ;
+ TDfcX* d = 0;
+ switch (type)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ if (iTimerTest)
+ d = NewTimer(aId, TDfcX::EFlag_Timer|TDfcX::EFlag_DFC, q, 0);
+ else
+ d = NewDfc(aId, TDfcX::EFlag_DFC, q);
+ break;
+ case 4:
+ case 5:
+ if (iTimerTest)
+ d = NewTimer(aId, TDfcX::EFlag_Timer|TDfcX::EFlag_Tied, 0, iDfcQ[iNumDfcQ-1-(type&1)]->iThread);
+ else
+ {
+ if (aId>=16 && aId<32 && iNumDfcQ>2)
+ d = NewIDfc(aId, TDfcX::EFlag_IDFC|TDfcX::EFlag_Tied, iDfcQ[2]->iThread);
+ else
+ d = NewIDfc(aId, TDfcX::EFlag_IDFC);
+ }
+ break;
+ case 6:
+ case 7:
+ if (iTimerTest)
+ d = NewTimer(aId, TDfcX::EFlag_Timer, 0, 0);
+ else
+ d = NewDfc(aId, TDfcX::EFlag_DFC|TDfcX::EFlag_IdleDFC, q);
+ break;
+ };
+ __e32_atomic_store_rel_ptr(&iDfcX[aId], d);
+ }
+
+void TDfcStress::Close()
+ {
+ TInt i;
+
+ // delete DFCs before the DFC queues they might be on
+ for (i=0; i<iNumDfc; ++i)
+ {
+ TDfcX* d = iDfcX[i];
+ delete d;
+ }
+ delete iBackStopIdleDfc;
+
+ for (i=0; i<iNumDfcQ; ++i)
+ DestroyDfcQ(iDfcQ[i]);
+ DestroyDfcQ(iBackStopIdleDfcQ);
+
+ delete iExitSem;
+ delete this;
+ }
+
+void TDfcStress::DoThreadFn(TAny* a)
+ {
+ ((TDfcStress*)a)->ThreadFn();
+ }
+
+void append(char*& a, const char* s)
+ {
+ while(*s)
+ *a++ = *s++;
+ *a=0;
+ }
+
+void TDfcStress::GetModeText(char* a)
+ {
+ memclr(a,128);
+ if (iMode==0)
+ {
+ append(a, "Add only");
+ return;
+ }
+ if (iMode & EMode_Wait)
+ append(a, "Wait ");
+ if (iMode & EMode_AllowCancel)
+ append(a, "Cancel ");
+ if (iMode & EMode_AllowIdle)
+ append(a, "Idle ");
+ if (iMode & EMode_AllowEnque)
+ append(a, "Enque ");
+ if (iMode & EMode_Recycle)
+ append(a, "Recycle ");
+ if (iMode & EMode_Migrate)
+ append(a, "Migrate ");
+ if (iMode & EMode_SelfMigrate)
+ append(a, "SelfMigrate ");
+ }
+
+/*
+Test Mode:
+
+Bit 31 If set causes thread to exit
+Bit 0 If set does random wait after each operation
+Bit 1 Allows Cancel operations if set
+Bit 2 Allows idle operations if set
+Bit 3 Test Enque() as well as Add()
+Bit 4 Use SafeXXX operations
+Bit 5 Use tied IDFCs
+Bit 6 Migrate threads with things tied to them
+Bit 7 Threads with things tied to them migrate themselves during execution
+
+*/
+void TDfcStress::ThreadFn()
+ {
+ TBool finish = FALSE;
+ TUint32 seed[2];
+ seed[0] = NKern::CurrentCpu() ^ 0xddb3d743;
+ seed[1] = 0;
+ FOREVER
+ {
+ if (iStop)
+ {
+ __e32_atomic_add_ord32(&iRunning, (TUint32)(-1));
+ while (iStop)
+ {
+ if (iMode<0)
+ {
+ finish = TRUE;
+ break;
+ }
+ }
+ if (finish)
+ break;
+ else
+ __e32_atomic_add_ord32(&iRunning, 1);
+ }
+ if (iMode & EMode_Wait)
+ {
+ TUint32 wait = random(seed);
+ wait %= iRandomTimeLimit;
+ wait += 1;
+ fcfspin(wait);
+ }
+ TUint32 action = random(seed);
+ TUint32 action2 = random(seed);
+ if (action & 0xff000000)
+ {
+ // queue or cancel a DFC or timer
+ TBool cancel = action2 & 2;
+ TUint32 id = action % iNumDfc;
+ TDfcX* d = iDfcX[id];
+ if (iMode & EMode_Recycle)
+ {
+ TBool isIDFC = d->IsIDFC();
+ TBool isIdler = d->IsIdler();
+ if (cancel)
+ d->SafeCancel();
+ else if (isIdler)
+ d->SafeQueueOnIdle();
+ else if ((iMode & EMode_AllowEnque) && (action2 & 1) && !isIDFC)
+ d->SafeEnque();
+ else
+ {
+ NKern::Lock();
+ d->SafeAdd();
+ NKern::Unlock();
+ }
+ }
+ else
+ {
+ if (cancel && (iMode & EMode_AllowCancel))
+ {
+ d->Cancel();
+ }
+ else if (!d->IsIdler())
+ {
+ if ((iMode & EMode_AllowEnque) && (action2 & 1) && !d->IsIDFC())
+ {
+ d->Enque();
+ }
+ else
+ {
+ NKern::Lock();
+ d->Add();
+ NKern::Unlock();
+ }
+ }
+ else
+ {
+ d->QueueOnIdle();
+ }
+ }
+ continue;
+ }
+ if (iMode & EMode_AllowIdle)
+ {
+ iBackStopIdleDfc->QueueOnIdle();
+ NKern::WaitForAnyRequest();
+ }
+ }
+ }
+
+void StopTimeout(TAny*)
+ {
+ __crash();
+ }
+
+void TDfcStress::DoTestPhase(TInt aMode, TInt aTime, TInt aCount)
+ {
+ char mode_text[128];
+ TInt i;
+ TUint32 maxavg = 0;
+ TInt n;
+ iMode = aMode;
+ iStop = FALSE;
+ GetModeText(mode_text);
+ TEST_PRINT1("Testing with: %s", mode_text);
+ for (i=0; i<aCount; ++i)
+ {
+ NKern::Sleep(aTime);
+ DebugPrint(".",1);
+ }
+ DebugPrint("\r\n",2);
+ TEST_PRINT("Stopping ...");
+ iStop = TRUE;
+ NTimer timer(&StopTimeout, 0);
+ timer.OneShot(2000);
+ BackStopFn(this);
+ n = 0;
+ while (iRunning && ++n<=100)
+ NKern::Sleep(10);
+ if (iRunning)
+ {
+ __crash();
+ }
+ iBackStopIdleDfc->Cancel();
+ timer.Cancel();
+ TEST_PRINT("Threads stopped");
+ for (i=0; i<iNumDfcQ; ++i)
+ {
+ TUint32 ev = iDfcQ[i]->iThread->iEventState;
+ DEBUGPRINT("DfcThread %d EventState = %08x", i, ev);
+ TEST_RESULT(!(ev & NSchedulable::EEventCountMask), "");
+ }
+ for (i=0; i<NKern::NumberOfCpus(); ++i)
+ {
+ TUint32 ev = iThread[i]->iEventState;
+ DEBUGPRINT("Thread %d EventState = %08x", i, ev);
+ TEST_RESULT(!(ev & NSchedulable::EEventCountMask), "");
+ }
+ NKern::Sleep(10);
+ for (i=0; i<iNumDfc; ++i)
+ {
+ TDfcX* d = iDfcX[i];
+ d->Cancel();
+ TUint32 qc = d->iQueueCount;
+ TUint32* rc = d->iRunCount;
+ TUint32 totrc = rc[0] + rc[1] + rc[2] + rc[3] + rc[4] + rc[5] + rc[6] + rc[7];
+ TUint32 cc = d->iCancelCount;
+ TUint32 f = d->iFlags;
+// TUint32 imm = d->IsIDFC()?1:0;
+ TUint32 max = d->iMaxTime;
+ TUint32 avg = 0;
+ TUint32 xfc = d->iExclusionFailCount;
+ if (totrc)
+ avg = TUint32(d->iSumTime / TUint64(totrc));
+ if (avg > maxavg)
+ maxavg = avg;
+ char desc[16];
+ memclr(desc,16);
+ d->GetDesc(desc);
+ DEBUGPRINT("%2d: %s QC %9d RC %9d CC %9d MAX %9d AVG %9d XFC %9d RC %9d %9d %9d %9d %9d %9d %9d %9d", i, desc, qc, totrc, cc, max, avg, xfc, rc[0], rc[1], rc[2], rc[3], rc[4], rc[5], rc[6], rc[7]);
+ TInt diff = (TInt)(qc - (totrc+cc));
+ TEST_RESULT1(diff==0, "Counts mismatched, diff=%d", diff);
+ TEST_RESULT(!(f&TDfcX::EFlag_Tied) || xfc==0, "Exclusion Failure!");
+ d->iQueueCount = 0;
+ memclr(d->iRunCount, sizeof(d->iRunCount));
+ d->iCancelCount = 0;
+ d->iMaxTime = 0;
+ d->iSumTime = 0;
+ }
+ if (!iRandomTimeLimit)
+ iRandomTimeLimit = maxavg + (maxavg>>1);
+ }
+
+void TDfcStress::Run()
+ {
+ TInt i;
+ NThread* t;
+ iStop = FALSE;
+ iMode = 0;
+ TInt num_cpus = NKern::NumberOfCpus();
+ iRunning = num_cpus;
+ for (i=0; i<KMaxCpus; ++i)
+ {
+ t = iThread[i];
+ if (t)
+ NKern::ThreadResume(t);
+ }
+ TEST_PRINT("Threads resumed");
+
+
+ DoTestPhase(0x00, 10000, 1);
+ if (iTimerTest)
+ {
+ const TInt N = 20;
+ DoTestPhase(EMode_AllowCancel|EMode_AllowEnque|EMode_Recycle, 10000, N);
+ DoTestPhase(EMode_AllowCancel|EMode_AllowEnque|EMode_Wait|EMode_Recycle, 10000, N);
+ DoTestPhase(EMode_AllowCancel|EMode_AllowEnque|EMode_Recycle|EMode_SelfMigrate, 10000, N);
+ DoTestPhase(EMode_AllowCancel|EMode_AllowEnque|EMode_Wait|EMode_Recycle|EMode_SelfMigrate, 10000, N);
+ DoTestPhase(EMode_AllowCancel|EMode_AllowEnque, 10000, N);
+ DoTestPhase(EMode_AllowCancel|EMode_AllowEnque|EMode_Wait, 10000, N);
+ DoTestPhase(EMode_AllowCancel|EMode_AllowEnque|EMode_SelfMigrate, 10000, N);
+ DoTestPhase(EMode_AllowCancel|EMode_AllowEnque|EMode_Wait|EMode_SelfMigrate, 10000, N);
+ }
+ else
+ {
+ DoTestPhase(EMode_AllowCancel|EMode_AllowEnque, 10000, 20);
+ DoTestPhase(EMode_AllowCancel|EMode_AllowIdle|EMode_AllowEnque, 10000, 20);
+ DoTestPhase(EMode_AllowIdle|EMode_AllowEnque, 10000, 20);
+ DoTestPhase(EMode_AllowCancel|EMode_AllowIdle, 10000, 20);
+ DoTestPhase(EMode_AllowCancel|EMode_Wait, 10000, 20);
+ DoTestPhase(EMode_AllowCancel, 10000, 20);
+ DoTestPhase(EMode_AllowCancel|EMode_AllowIdle|EMode_AllowEnque|EMode_Recycle, 10000, 20);
+ }
+
+ iMode = EMode_Exit;
+ TEST_PRINT("Terminating threads");
+ for (i=0; i<num_cpus; ++i)
+ NKern::FSWait(iExitSem);
+ TEST_PRINT("Done");
+ }
+
+void DoStressTest(TBool aTimerTest)
+ {
+ TEST_PRINT("Stress test...");
+ TInt ndfcs=0;
+ TInt ndfcq=0;
+ switch (NKern::NumberOfCpus())
+ {
+ case 1: ndfcs=16; ndfcq=2; break;
+ case 2: ndfcs=16; ndfcq=2; break;
+ case 3: ndfcs=24; ndfcq=2; break;
+ case 4: ndfcs=32; ndfcq=3; break;
+ case 5: ndfcs=32; ndfcq=3; break;
+ case 6: ndfcs=48; ndfcq=4; break;
+ case 7: ndfcs=48; ndfcq=4; break;
+ case 8: ndfcs=48; ndfcq=4; break;
+ default:
+ __NK_ASSERT_ALWAYS(0);
+ break;
+ }
+ TDfcStress* ds = TDfcStress::New(ndfcs, ndfcq, aTimerTest);
+ ds->Run();
+ ds->Close();
+ }
+
+#endif
+
+void TestDFCs()
+ {
+ TEST_PRINT("Testing DFCs...");
+
+ TTestDfc::Buffer = CircBuf::New(TTestDfc::EBufferSlots);
+ TInt cpu;
+ (void)cpu;
+#ifdef __SMP__
+ DoStressTest(TRUE);
+#endif
+
+ DoDFCTest1();
+ DoDFCTest2();
+ for_each_cpu(cpu)
+ {
+ DoDFCTest3(cpu);
+ }
+ DoIDFCTest1();
+ for_each_cpu(cpu)
+ {
+ DoIdleDFCTest1(cpu);
+ }
+ DoIdleDFCTest2();
+
+#ifdef __SMP__
+ DoStressTest(FALSE);
+#endif
+
+ delete TTestDfc::Buffer;
+ TTestDfc::Buffer = 0;
+ }