os/graphics/egl/egltest/endpointtestsuite/automated/tsrc/egltest_threadedstress_remote.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 // Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 
    13 
    14 /**
    15  @file
    16  @test
    17  @internalComponent - Internal Symbian test code
    18 */
    19 
    20 
    21 #include "egltest_threadedstress.h"
    22 #include "eglendpointwrap.h"
    23 #include "egltest_endpoint_images.h"
    24 #include "egltest_threadmonitor.h"
    25 #include <e32atomics.h>
    26 #include "egltest_endpoint_images.h"
    27 #include <e32math.h>
    28 
    29 
    30 //Private Helper Class Declarations-----------------------------------------------
    31 
    32 class CTightLoopThread : public CBase, public MLog
    33     {
    34 public:
    35     ~CTightLoopThread();
    36     
    37     //Control the loop from the controlling thread. 
    38     //Calling Start() more than once causes panic.
    39     void Start();
    40     TRemoteTestVerdict Stop();
    41     TThreadId ThreadId() const;
    42     
    43 protected:
    44     CTightLoopThread();
    45     void ConstructL(TBool aSharedHeap);
    46     MLog& Logger() const;
    47     void Log(const TText8* aFile, TInt aLine, TInt aSeverity, TRefByValue<const TDesC> aFmt, ...);
    48     
    49     //To be implemented by derived class.
    50     virtual void SetupInThreadContextL() = 0;
    51     virtual void TeardownInThreadContextL() = 0;
    52     virtual TBool ExecuteInnerLoopBody() = 0;
    53     
    54 private:
    55     static TInt ThreadEntryPoint(TAny* aSelf);
    56     void EnterThreadLoopL();
    57 
    58 private:
    59     RThread iThread;
    60     TRequestStatus iNotifyStart;
    61     volatile TBool iNotifyStop;
    62     TBool iHasBeenStarted;
    63     TBool iHasBeenStopped;
    64     };
    65 
    66 
    67 class CEndpointExercise : public CTightLoopThread
    68     {
    69 public:
    70     static CEndpointExercise* NewL(TBool aSharedHeap);
    71     ~CEndpointExercise();
    72     
    73     void SetupInThreadContextL();
    74     void TeardownInThreadContextL();
    75     TBool ExecuteInnerLoopBody();
    76     
    77 private:
    78     CEndpointExercise();
    79     void ConstructL(TBool aSharedHeap);
    80     void ExecuteInnerLoopBodyL();
    81     TInt CheckImage(EGLImageKHR aEglImage);
    82     
    83     //Logging helpers.
    84     void PanicIfError(TInt aError, const TText8* aFile, TInt aLine) const;
    85     void PanicIfFalse(TBool aBool, const TText8* aFile, TInt aLine) const;
    86     void LogAndLeaveIfErrorL(TInt aError, const TText8* aFile, TInt aLine) const;
    87     void LogAndLeaveIfFalseL(TBool aBool, const TText8* aFile, TInt aLine) const;
    88     #define PANIC_IF_ERROR(ERROR)           PanicIfError((ERROR), (TText8*)__FILE__, __LINE__)
    89     #define PANIC_IF_FALSE(BOOL)            PanicIfFalse((BOOL), (TText8*)__FILE__, __LINE__)
    90     #define LOG_AND_LEAVE_IF_ERROR_L(ERROR) LogAndLeaveIfErrorL((ERROR), (TText8*)__FILE__, __LINE__)
    91     #define LOG_AND_LEAVE_IF_FALSE_L(BOOL)  LogAndLeaveIfFalseL((BOOL), (TText8*)__FILE__, __LINE__)
    92 
    93 private:
    94     TInt iIteration;
    95     TInt iCurrentColour;
    96     RSurfaceManager iSurfaceManager;
    97     RSurfaceUpdateSession iSurfaceUpdate;
    98     RSurfaceManager::TSurfaceCreationAttributesBuf iSurfaceAttribs;
    99     EGLDisplay iDisplay;
   100     TEglEndpointWrap iEglEp;
   101     CEglWindowSurface* iDummyWindowSurface;
   102     };
   103 
   104 //--------------------------------------------------------------------------------
   105 
   106 
   107 //Cleanup Items used through out tests--------------------------------------------
   108 
   109 struct TCleanupSurface
   110     {
   111     RSurfaceManager* iSurfaceManager;
   112     TSurfaceId iSurfaceId;
   113     };
   114 static void CleanupSurface(TAny* aCleanupSurface)
   115     {
   116     TCleanupSurface* surface = static_cast<TCleanupSurface*>(aCleanupSurface);
   117     TInt err = surface->iSurfaceManager->CloseSurface(surface->iSurfaceId);
   118     ASSERT(err == KErrNone);
   119     }
   120 
   121 
   122 struct TCleanupEndpoint
   123     {
   124     EGLDisplay iDisplay;
   125     EGLEndpointNOK iEndpoint;
   126     };
   127 static void CleanupEndpoint(TAny* aCleanupEndpoint)
   128     {
   129     TCleanupEndpoint* endpoint = static_cast<TCleanupEndpoint*>(aCleanupEndpoint);
   130     TEglEndpointWrap ep;
   131     ASSERT(ep.Error() == KErrNone);
   132     EGLBoolean err = ep.DestroyEndpoint(endpoint->iDisplay, endpoint->iEndpoint);
   133     ASSERT(err);
   134     }
   135 
   136 
   137 struct TCleanupImage
   138     {
   139     EGLDisplay iDisplay;
   140     EGLEndpointNOK iEndpoint;
   141     EGLImageKHR iImage;
   142     };
   143 static void CleanupImage(TAny* aCleanupImage)
   144     {
   145     TCleanupImage* image = static_cast<TCleanupImage*>(aCleanupImage);
   146     TEglEndpointWrap ep;
   147     ASSERT(ep.Error() == KErrNone);
   148     EGLBoolean err = ep.ReleaseImage(image->iDisplay, image->iEndpoint, image->iImage, EGL_NONE);
   149     ASSERT(err);
   150     }
   151 
   152 
   153 static void CleanupPointerArray(TAny* aPointerArray)
   154     {
   155     RPointerArray<CEndpointExercise>* array = static_cast<RPointerArray<CEndpointExercise>*>(aPointerArray);
   156     array->ResetAndDestroy();
   157     }
   158 
   159 //--------------------------------------------------------------------------------
   160 
   161 
   162 //Utility Functions---------------------------------------------------------------
   163 
   164 inline TInt RandomNumberInRange(TInt aMin, TInt aMax)
   165     {
   166     if(aMin > aMax)
   167         {
   168         TInt temp = aMin;
   169         aMin = aMax;
   170         aMax = temp;
   171         }
   172     
   173     //Scale and offset to put random into the range inclusively.
   174     TUint range = aMax - aMin;
   175     TUint random  = Math::Random() % (range + 1);
   176     return (TInt)random + aMin;
   177     }
   178 
   179 
   180 inline TReal Square(TReal aNumber)
   181     {
   182     return aNumber * aNumber;
   183     }
   184 
   185 
   186 static TBool SamplesAreIncreasing(TInt* aSampledData, TInt aNumSamples)
   187     {
   188     //Naive linear least squares to get gradient of fit line and correlation coefficient.
   189     //Using TReal to avoid worrying about wrap.
   190     
   191     TReal n = aNumSamples;
   192     TReal sumX = 0.0;
   193     TReal sumXSq = 0.0;
   194     TReal sumY = 0.0;
   195     TReal sumYSq = 0.0;
   196     TReal sumXTimesY = 0.0;
   197     
   198     for(TInt i=0; i < aNumSamples; i++)
   199         {
   200         TReal x = (TReal)(i + 1);
   201         TReal y = (TReal)aSampledData[i];
   202         sumX += x;
   203         sumXSq += Square(x);
   204         sumY += y;
   205         sumYSq += Square(y);
   206         sumXTimesY += x * y;
   207         }
   208     
   209     TReal xBar = sumX / n;
   210     TReal yBar = sumY / n;
   211     
   212     TReal gradient = (sumXTimesY - (n * xBar * yBar)) / (sumXSq - (n * Square(xBar)));
   213     TReal correlation = Square(sumXTimesY - (n * xBar * yBar)) / ((sumXSq - (n * Square(xBar))) * (sumYSq - (n * Square(yBar))));
   214     
   215     //If the gradient is positive and the correlation coefficient is high, the samples are increasing.
   216     return (correlation > 0.8) && (gradient > 0.0);
   217     }
   218 
   219 //--------------------------------------------------------------------------------
   220 
   221 
   222 //CTightLoopThread----------------------------------------------------------------
   223 
   224 CTightLoopThread::CTightLoopThread() :
   225     iNotifyStop(EFalse),
   226     iHasBeenStarted(EFalse),
   227     iHasBeenStopped(EFalse)
   228     {
   229     }
   230 
   231 
   232 void CTightLoopThread::ConstructL(TBool aSharedHeap)
   233     {
   234     //Stack and heap sizes.
   235     static const TInt KStackSize =   0x2000;      //  8KB
   236     static const TInt KHeapMinSize = 0x1000;      //  4KB
   237     static const TInt KHeapMaxSize = 0x1000000;   // 16MB
   238     
   239     //The new thread either has its own heap or shares ours.
   240     if(aSharedHeap)
   241         {
   242         User::LeaveIfError(iThread.Create(KNullDesC, ThreadEntryPoint, KStackSize, NULL, this, EOwnerThread));
   243         }
   244     else
   245         {
   246         User::LeaveIfError(iThread.Create(KNullDesC, ThreadEntryPoint, KStackSize, KHeapMinSize, KHeapMaxSize, this, EOwnerThread));
   247         }
   248     
   249     //Resume and rendezvous.
   250     iThread.Resume();
   251     TRequestStatus rendezvous;
   252     iThread.Rendezvous(rendezvous);
   253     User::WaitForRequest(rendezvous);
   254     User::LeaveIfError(rendezvous.Int());
   255     }
   256 
   257 
   258 MLog& CTightLoopThread::Logger() const
   259     {
   260     return *const_cast<CTightLoopThread*>(this);
   261     }
   262 
   263 
   264 class TOverflowTruncate : public TDesOverflow
   265     {
   266 public:
   267     virtual void Overflow(TDes& /*aDes*/)
   268         {
   269         //Do nothing - just let it truncate.
   270         }
   271     };
   272 
   273 
   274 void CTightLoopThread::Log(const TText8* aFile, TInt aLine, TInt aSeverity, TRefByValue<const TDesC> aFmt, ...)
   275     {
   276     TOverflowTruncate overflow;
   277     VA_LIST list;
   278     VA_START(list, aFmt);
   279     TBuf<0x100> buf;
   280     buf.AppendFormatList(aFmt, list, &overflow);
   281     TPtrC8 file8(aFile);
   282     TBuf<0x100> file16;
   283     file16.Copy(file8);
   284     //Lots of effort is required to pump this into the TEF log file, so for now we just print to debug.
   285     RDebug::Print(_L("CTightLoopThread: %S:%d, Severity=%d, Message=\"%S\""), &file16, aLine, aSeverity, &buf);
   286     }
   287 
   288 
   289 CTightLoopThread::~CTightLoopThread()
   290     {
   291     //Shutdown the thread according to the state it is in.
   292     if(!iHasBeenStarted)
   293         {
   294         TRequestStatus* notifyStart = &iNotifyStart;
   295         iThread.RequestComplete(notifyStart, KErrAbort);
   296         }
   297     if(iHasBeenStarted && !iHasBeenStopped)
   298         {
   299         Stop();
   300         }
   301     iThread.Close();
   302     }
   303 
   304 
   305 void CTightLoopThread::Start()
   306     {
   307     ASSERT(!iHasBeenStarted);
   308     TRequestStatus* notifyStart = &iNotifyStart;
   309     iThread.RequestComplete(notifyStart, KErrNone);
   310     iHasBeenStarted = ETrue;
   311     }
   312 
   313 
   314 TRemoteTestVerdict CTightLoopThread::Stop()
   315     {
   316     ASSERT(iHasBeenStarted);
   317     ASSERT(!iHasBeenStopped);
   318     
   319     TRequestStatus logon;
   320     iThread.Logon(logon);
   321     __e32_atomic_store_rel32(&iNotifyStop, ETrue);
   322     User::WaitForRequest(logon);
   323     
   324     TExitType exitType = iThread.ExitType();
   325     iThread.Close();
   326     iHasBeenStopped = ETrue;
   327     
   328     switch(exitType)
   329         {
   330         case EExitKill:
   331             //Terminated normally (since we never call kill).
   332             return ERtvPass; 
   333         
   334         case EExitPanic:
   335             //Thread panicked.
   336             return ERtvPanic;
   337             
   338         default:
   339             //Any other option should be impossible in our use case.
   340             ASSERT(0);
   341         }
   342     return ERtvAbort;
   343     }
   344 
   345 
   346 TThreadId CTightLoopThread::ThreadId() const
   347     {
   348     return iThread.Id();
   349     }
   350 
   351 
   352 TInt CTightLoopThread::ThreadEntryPoint(TAny* aSelf)
   353     {
   354     CTightLoopThread* self = static_cast<CTightLoopThread*>(aSelf);
   355     CTrapCleanup* cleanup = CTrapCleanup::New();
   356     
   357     TRAPD(err,
   358         //Create active scheduler.
   359         CActiveScheduler* scheduler = new (ELeave) CActiveScheduler();
   360         CleanupStack::PushL(scheduler);
   361         CActiveScheduler::Install(scheduler);
   362 
   363         //Setup the draw loop.
   364         self->EnterThreadLoopL();
   365 
   366         //Clean up.
   367         CleanupStack::PopAndDestroy(scheduler);
   368         );
   369     
   370     __ASSERT_ALWAYS(err == KErrNone, User::PanicUnexpectedLeave());
   371     delete cleanup;
   372     
   373     return KErrNone;
   374     }
   375 
   376 
   377 void CTightLoopThread::EnterThreadLoopL()
   378     {
   379     //Setup the derived class in this thread context.
   380     TRAPD(err, SetupInThreadContextL());
   381     
   382     //Set the request to pending, rendezvous with parent and wait for start signal.
   383     iNotifyStart = KRequestPending;
   384     RThread().Rendezvous(err);
   385     User::WaitForRequest(iNotifyStart);
   386     
   387     //Exit immediately if the KErrAbort signal was received.
   388     TBool keepGoing = ETrue;
   389     if(iNotifyStart == KErrAbort)
   390         {
   391         keepGoing = EFalse;
   392         }
   393     else
   394         {
   395         ASSERT(iNotifyStart == KErrNone);
   396         }
   397     
   398     //Loop until we are told to stop.
   399     while(!__e32_atomic_load_acq32(&iNotifyStop) && keepGoing)
   400         {
   401         keepGoing = ExecuteInnerLoopBody();
   402         }
   403     
   404     //Teardown the derived class in this thread context.
   405     TeardownInThreadContextL();
   406     }
   407 
   408 //--------------------------------------------------------------------------------
   409 
   410 
   411 //CEndpointExercise---------------------------------------------------------------
   412 
   413 CEndpointExercise* CEndpointExercise::NewL(TBool aSharedHeap)
   414     {
   415     CEndpointExercise* self = new (ELeave) CEndpointExercise();
   416     CleanupStack::PushL(self);
   417     self->ConstructL(aSharedHeap);
   418     CleanupStack::Pop(self);
   419     return self;
   420     }
   421 
   422 
   423 CEndpointExercise::CEndpointExercise()
   424     {
   425     }
   426 
   427 
   428 void CEndpointExercise::ConstructL(TBool aSharedHeap)
   429     {
   430     CTightLoopThread::ConstructL(aSharedHeap);
   431     User::LeaveIfError(iEglEp.Error());
   432     }
   433 
   434 
   435 CEndpointExercise::~CEndpointExercise()
   436     {
   437     }
   438 
   439 
   440 void CEndpointExercise::PanicIfError(TInt aError, const TText8* aFile, TInt aLine) const
   441     {
   442     if(aError != KErrNone)
   443         {
   444         Logger().Log(aFile, aLine, ESevrErr, _L("Panicking due to error %d"), aError);
   445         User::Panic(_L("EPTHREADEDSTRESS"), aLine);
   446         }
   447     }
   448 
   449 
   450 void CEndpointExercise::PanicIfFalse(TBool aBool, const TText8* aFile, TInt aLine) const
   451     {
   452     if(!aBool)
   453         {
   454         Logger().Log(aFile, aLine, ESevrErr, _L("Panicking due to failing invariant test"));
   455         User::Panic(_L("EPTHREADEDSTRESS"), aLine);
   456         }
   457     }
   458 
   459 
   460 void CEndpointExercise::LogAndLeaveIfErrorL(TInt aError, const TText8* aFile, TInt aLine) const
   461     {
   462     if(aError != KErrNone)
   463         {
   464         Logger().Log(aFile, aLine, ESevrWarn, _L("Abandoning iteration due to error %d"), aError);
   465         User::Leave(aError);
   466         }
   467     }
   468 
   469 
   470 void CEndpointExercise::LogAndLeaveIfFalseL(TBool aBool, const TText8* aFile, TInt aLine) const
   471     {
   472     if(!aBool)
   473         {
   474         Logger().Log(aFile, aLine, ESevrWarn, _L("Abandoning iteration due to failing invariant test"));
   475         User::Leave(KErrUnknown);
   476         }
   477     }
   478 
   479 
   480 TInt CEndpointExercise::CheckImage(EGLImageKHR aEglImage)
   481     {
   482     TRAPD
   483         (err,
   484         //Convert the image to a CTestVgEglImage
   485         CTestVgEglImage* vgEglImage = CTestVgEglImage::NewL(aEglImage);
   486         CleanupStack::PushL(vgEglImage);
   487         
   488         //Check the corners and center pixel are the same colour.
   489         //Since this test is focussed on correct OOM behaviour, 
   490         //we panic if the functionality is incorrect.
   491         PANIC_IF_FALSE(vgEglImage->IsSolidColourL());
   492         
   493         CleanupStack::PopAndDestroy(vgEglImage);
   494         );
   495     return err;
   496     }
   497 
   498 
   499 void CEndpointExercise::SetupInThreadContextL()
   500     {
   501     //Colour to fill surface with (this is incremented every frame).
   502     iCurrentColour = 0x88CC44;
   503     
   504     //Connections to SUS and surface manager.
   505     User::LeaveIfError(iSurfaceManager.Open());
   506     User::LeaveIfError(iSurfaceUpdate.Connect(5));
   507     
   508     //Surface attribs to create surface with.
   509     iSurfaceAttribs().iSize = TSize(100, 100);
   510     iSurfaceAttribs().iBuffers = 2;
   511     iSurfaceAttribs().iPixelFormat = EUidPixelFormatARGB_8888_PRE;
   512     iSurfaceAttribs().iStride = 100 * 4;
   513     iSurfaceAttribs().iOffsetToFirstBuffer = 0;
   514     iSurfaceAttribs().iAlignment = 32;
   515     iSurfaceAttribs().iContiguous = EFalse;
   516     iSurfaceAttribs().iCacheAttrib = RSurfaceManager::ECached;
   517     iSurfaceAttribs().iOffsetBetweenBuffers = 0;
   518     iSurfaceAttribs().iSurfaceHints = NULL;
   519     iSurfaceAttribs().iHintCount = 0;
   520     iSurfaceAttribs().iMappable = ETrue;
   521     
   522     iDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
   523     
   524     //Create an EglWindowSurface so we have a current context for vg operations.
   525     iDummyWindowSurface = CEglWindowSurface::NewL();
   526     iDummyWindowSurface->CreateL(EStandardSurface, TPoint(0, 0));
   527     iDummyWindowSurface->ActivateL();
   528     }
   529 
   530 
   531 void CEndpointExercise::TeardownInThreadContextL()
   532     {
   533     delete iDummyWindowSurface;
   534     iSurfaceUpdate.Close();
   535     iSurfaceManager.Close();
   536     }
   537 
   538 
   539 TBool CEndpointExercise::ExecuteInnerLoopBody()
   540     {
   541     TRAPD(err, ExecuteInnerLoopBodyL());
   542     if(err != KErrNone)
   543         {
   544         Logger().Log((TText8*)__FILE__, __LINE__, ESevrWarn, _L("Iteration %d did not run to completion, due to an acceptable error in low memory conditions"), iIteration);
   545         }
   546     iIteration++;
   547     return ETrue;
   548     }
   549 
   550 
   551 void CEndpointExercise::ExecuteInnerLoopBodyL()
   552     {
   553     //Create a surface.
   554     TCleanupSurface surface = {&iSurfaceManager, TSurfaceId::CreateNullId()};
   555     LOG_AND_LEAVE_IF_ERROR_L(iSurfaceManager.CreateSurface(iSurfaceAttribs, surface.iSurfaceId));
   556     CleanupStack::PushL(TCleanupItem(CleanupSurface, &surface));
   557     
   558     //Map surface and get pointer to buffer 0.
   559     RChunk surfaceChunk;
   560     TInt offset;
   561     PANIC_IF_ERROR(iSurfaceManager.MapSurface(surface.iSurfaceId, surfaceChunk));
   562     CleanupClosePushL(surfaceChunk);
   563     PANIC_IF_ERROR(iSurfaceManager.GetBufferOffset(surface.iSurfaceId, 0, offset));
   564     TUint32* buffer = (TUint32*)(surfaceChunk.Base() + offset);
   565     
   566     //Fill surface with current colour. This could
   567     //be much faster but its good enough for testing.
   568     TUint32 fillColour = TRgb(iCurrentColour, 255)._Color16MAP();
   569     for(TInt y=0; y < iSurfaceAttribs().iSize.iHeight; ++y)
   570         {
   571         for(TInt x=0; x < iSurfaceAttribs().iSize.iWidth; ++x)
   572             {
   573             buffer[x] = fillColour;
   574             }
   575         buffer += iSurfaceAttribs().iStride >> 2;
   576         }
   577     
   578     //Create an endpoint for the surface.
   579     TCleanupEndpoint endpoint = {iDisplay, EGL_NO_ENDPOINT_NOK};
   580     endpoint.iEndpoint = iEglEp.CreateEndpoint(iDisplay, EGL_ENDPOINT_TYPE_CONSUMER_NOK, EGL_TSURFACEID_NOK, &surface.iSurfaceId, NULL);
   581     LOG_AND_LEAVE_IF_FALSE_L(endpoint.iEndpoint != EGL_NO_ENDPOINT_NOK);
   582     CleanupStack::PushL(TCleanupItem(CleanupEndpoint, &endpoint));
   583     
   584     //Submit buffer 0 to surface update server.
   585     TRequestStatus displayed;
   586     iSurfaceUpdate.NotifyWhenDisplayedXTimes(1, displayed);
   587     LOG_AND_LEAVE_IF_ERROR_L(iSurfaceUpdate.SubmitUpdate(KAllScreens, surface.iSurfaceId, 0, NULL));
   588     User::WaitForRequest(displayed);
   589     
   590     //Begin streaming. Should not fail since we have submitted a buffer since creating ep.
   591     LOG_AND_LEAVE_IF_FALSE_L(iEglEp.EndpointBeginStreaming(iDisplay, endpoint.iEndpoint));
   592     
   593     //Acquire an image from the endpoint.
   594     TCleanupImage image = {iDisplay, endpoint.iEndpoint, EGL_NO_IMAGE_KHR};
   595     image.iImage = iEglEp.AcquireImage(iDisplay, endpoint.iEndpoint);
   596     LOG_AND_LEAVE_IF_FALSE_L(image.iImage != EGL_NO_IMAGE_KHR);
   597     CleanupStack::PushL(TCleanupItem(CleanupImage, &image));
   598     
   599     //Check that the image we acquired is coherrent.
   600     LOG_AND_LEAVE_IF_ERROR_L(CheckImage(image.iImage));
   601     
   602     //Release image, destroy endpoint, close chunk and close surface.
   603     CleanupStack::PopAndDestroy(4);
   604     
   605     //Modify the colour that we draw.
   606     iCurrentColour += 16;
   607     }
   608 
   609 //--------------------------------------------------------------------------------
   610 
   611 
   612 //Remote test step----------------------------------------------------------------
   613 
   614 CEglTest_RemoteTestStep_EndpointThreadStress::CEglTest_RemoteTestStep_EndpointThreadStress() :
   615     CRemoteTestStepBase(ETestUidEndpointThreadStress)
   616     {
   617     }
   618 
   619 
   620 CEglTest_RemoteTestStep_EndpointThreadStress::~CEglTest_RemoteTestStep_EndpointThreadStress()
   621     {
   622     }
   623 
   624 
   625 TRemoteTestVerdict CEglTest_RemoteTestStep_EndpointThreadStress::DoStartRemoteTestStepL(const TRemoteTestParams& /*aMessageIn*/)
   626     {
   627     REMOTE_INFO_PRINTF1(_L("Starting Remote Test Step."));
   628     EglStartL();
   629     return ERtvPass;
   630     }
   631 
   632 
   633 TRemoteTestVerdict CEglTest_RemoteTestStep_EndpointThreadStress::DoEndRemoteTestStepL(const TRemoteTestParams& /*aMessageIn*/)
   634     {
   635     REMOTE_INFO_PRINTF1(_L("Ending Remote Test Step."));
   636     EglEndL();
   637     return ERtvPass;
   638     }
   639 
   640 
   641 TInt CEglTest_RemoteTestStep_EndpointThreadStress::Timeout() const
   642     {
   643     return 120 * 1000000; //2 min.
   644     }
   645 
   646 
   647 TRemoteTestVerdict CEglTest_RemoteTestStep_EndpointThreadStress::DoRunRemoteTestCaseL(TInt aTestCase, const TRemoteTestParams& aParams)
   648     {
   649     switch(aTestCase)
   650         {
   651         case 0:     return CrazyThreadingTestCaseL(aParams);
   652         case 1:     return OutOfHeapMemoryTestCaseL(aParams);
   653         default:    return ERtvAbort;
   654         }
   655     }
   656 
   657 
   658 //For a detailed description of this test case (GRAPHICS-EGL-594), see the local side cpp file.
   659 TRemoteTestVerdict CEglTest_RemoteTestStep_EndpointThreadStress::CrazyThreadingTestCaseL(const TRemoteTestParams& /*aParams*/)
   660     {
   661     //Create the exercises. These run an endpoint exercise in a tight loop in a private thread.
   662     CEndpointExercise* exercise1 = CEndpointExercise::NewL(EFalse);
   663     CleanupStack::PushL(exercise1);
   664     CEndpointExercise* exercise2 = CEndpointExercise::NewL(EFalse);
   665     CleanupStack::PushL(exercise2);
   666     
   667     //Create a monitor to cleanup if any of the threads panic. The controller thread 
   668     //must be at index zero in the array. This will even work if a deadlock occurs 
   669     //between the  exercise threads, since the call to stop the exercise will never 
   670     //return and the framework will eventually time us out. The monitor will notice
   671     //that the controller thread has panicked and will forward the panic to the exercises.
   672     RArray<TThreadId> threads;
   673     CleanupClosePushL(threads);
   674     threads.AppendL(RThread().Id());
   675     threads.AppendL(exercise1->ThreadId());
   676     threads.AppendL(exercise2->ThreadId());
   677     CThreadMonitor* monitor = CThreadMonitor::NewL(threads);
   678     CleanupStack::PushL(monitor);
   679     
   680     //Start the exercises.
   681     exercise1->Start();
   682     exercise2->Start();
   683     
   684     //Let the exercises run for 20 seconds.
   685     User::After(20 * 1000000);
   686     
   687     //Stop the exercises and record the results.
   688     TRemoteTestVerdict result1 = exercise1->Stop();
   689     TRemoteTestVerdict result2 = exercise2->Stop();
   690     
   691     CleanupStack::PopAndDestroy(4, exercise1);
   692     return (result1 != ERtvPass) ? result1 : result2;
   693     }
   694 
   695 
   696 class THeapGobbler
   697     {
   698 public:
   699     static THeapGobbler* New(TInt aSize)
   700         {
   701         THeapGobbler* self = (THeapGobbler*)new TUint8[sizeof(THeapGobbler) - sizeof(TUint8) + aSize];
   702         if(!self)
   703             {
   704             return NULL;
   705             }
   706         self->iSize = aSize;
   707         self->iNext = NULL;
   708         return self;
   709         }
   710     
   711 public:
   712     THeapGobbler* iNext;
   713     TInt iSize;
   714     TUint8 iMemory[1];
   715     };
   716 
   717 
   718 //For a detailed description of this test case (GRAPHICS-EGL-601), see the local side cpp file.
   719 TRemoteTestVerdict CEglTest_RemoteTestStep_EndpointThreadStress::OutOfHeapMemoryTestCaseL(const TRemoteTestParams& aParams)
   720     {
   721     const TInt KHeapSizeMin = 0x100000;   //1MB.
   722     const TInt KHeapSizeMax = 0x10000000; //256MB.
   723 
   724     RHeap* testHeap = User::ChunkHeap(NULL, KHeapSizeMin, KHeapSizeMax, KMinHeapGrowBy, 4);
   725     if(!testHeap)
   726         {
   727         REMOTE_ERR_PRINTF1(_L("Failed to create chunk heap. Aborting."));
   728         return ERtvAbort;
   729         }
   730     RHeap* oldHeap = User::SwitchHeap(testHeap);
   731     
   732     CTrapCleanup *cleanUpStack = CTrapCleanup::New();
   733     if (!cleanUpStack)
   734         {
   735         User::SwitchHeap(oldHeap);
   736         testHeap->Close();
   737         User::Leave(KErrNoMemory);
   738         }
   739 
   740     TRemoteTestVerdict verdict = ERtvPass;
   741     TRAPD(err, verdict = DoOutOfHeapMemoryTestCaseL(aParams));
   742     
   743     delete cleanUpStack;
   744     User::SwitchHeap(oldHeap);
   745     testHeap->Close();
   746     
   747     User::LeaveIfError(err);
   748     return verdict;
   749     }
   750 
   751  
   752 TRemoteTestVerdict CEglTest_RemoteTestStep_EndpointThreadStress::DoOutOfHeapMemoryTestCaseL(const TRemoteTestParams& aParams)
   753     {
   754     const TInt numExercises = aParams.iEndpointThreadStress.iNumThreads;
   755     
   756     const TInt KMinCellSize = 500;
   757     const TInt KMaxCellSize = 2000;
   758     const TInt KNumIterations = 20;
   759     TInt heapAllocSize[KNumIterations];
   760     TRemoteTestVerdict exerciseResult = ERtvPass;
   761     
   762     //One iteration of the outer loop results in one data point for deciding if the heap is leaking or not. 
   763     for(TInt x=0; x < KNumIterations; x++)
   764         {
   765         //Reserving space in these arrays ahead of time to 
   766         //make cleanup stack manipulation more staightforward.
   767         RPointerArray<CEndpointExercise> exercises;
   768         CleanupStack::PushL(TCleanupItem(CleanupPointerArray, &exercises));
   769         exercises.ReserveL(numExercises);
   770         RArray<TThreadId> threads;
   771         CleanupClosePushL(threads);
   772         threads.ReserveL(numExercises + 1);
   773         
   774         //Save the controller thread id for the monitor.
   775         threads.Append(RThread().Id());
   776         
   777         //Create endpoint exercise threads and save the thread Ids for the monitor.
   778         for(TInt j=0; j < numExercises; j++)
   779             {
   780             //Appends can't fail since we have already reserved space.
   781             //Note that the exercises all share the same heap as this thread.
   782             exercises.Append(CEndpointExercise::NewL(ETrue));
   783             threads.Append(exercises[j]->ThreadId());
   784             }
   785 
   786         //Create a monitor to handle thread cleanup if something panics or deadlocks.
   787         CThreadMonitor* monitor = CThreadMonitor::NewL(threads);
   788         
   789         //Nothing can leave after this.
   790         CleanupStack::Pop(2);
   791         
   792         //Start the exercises.
   793         for(TInt j=0; j < numExercises; j++)
   794             {
   795             exercises[j]->Start();
   796             }
   797         
   798         THeapGobbler* firstCell = NULL;
   799         THeapGobbler* lastCell = NULL;
   800         TInt numberOfCells = 0;
   801         
   802         for(TInt i=0; i < 2; i++)
   803             {
   804             //Allocate random sizes until full.
   805             THeapGobbler* newCell = THeapGobbler::New(RandomNumberInRange(KMinCellSize, KMaxCellSize));
   806             while(newCell)
   807                 {
   808                 if(lastCell)
   809                     lastCell->iNext = newCell;
   810                 if(!firstCell)
   811                     firstCell = newCell;
   812                 lastCell = newCell;
   813                 numberOfCells++;
   814                 newCell = THeapGobbler::New(RandomNumberInRange(KMinCellSize, KMaxCellSize));
   815                 }
   816             
   817             //Let exercise run while heap is full.
   818             User::After(1 * 1000);
   819             
   820             //Deallocate n/4 cells.
   821             for(TInt n = numberOfCells / 4; n >= 1; --n)
   822                 {
   823                 THeapGobbler* oldCell = firstCell;
   824                 firstCell = oldCell->iNext;
   825                 delete oldCell;
   826                 numberOfCells--;
   827                 if(!firstCell)
   828                     {
   829                     lastCell = NULL;
   830                     ASSERT(numberOfCells == 0);
   831                     break;
   832                     }
   833                 }
   834             
   835             //Let exercise run while heap is not full.
   836             User::After(1 * 1000);
   837             }
   838         
   839         //Deallocate all cells.
   840         while(firstCell)
   841             {
   842             THeapGobbler* oldCell = firstCell;
   843             firstCell = oldCell->iNext;
   844             delete oldCell;
   845             }
   846         lastCell = NULL;
   847         numberOfCells = 0;
   848         
   849         //Stop the exercises and save the result.
   850         for(TInt j=0; j < numExercises; j++)
   851             {
   852             TRemoteTestVerdict ret = exercises[j]->Stop();
   853             exerciseResult = (exerciseResult == ERtvPass) ? ret : exerciseResult;
   854             }
   855 
   856         delete monitor;
   857         threads.Close();
   858         exercises.ResetAndDestroy();
   859         
   860         if(exerciseResult != ERtvPass)
   861             {
   862             REMOTE_ERR_PRINTF2(_L("Aborting because the endpoint exercise failed for iteration %d"), x);
   863             return exerciseResult;
   864             }
   865         
   866         //Save the heap size.
   867         User::Heap().AllocSize(heapAllocSize[x]);
   868         }
   869     
   870     //Work out if any memory has leaked and return a verdict.
   871     TBool memoryIsLeaking = SamplesAreIncreasing(heapAllocSize, KNumIterations);
   872     if(memoryIsLeaking)
   873         {
   874         REMOTE_ERR_PRINTF1(_L("Heap memory is increasing over time with high certainty, there is probably a memory leak."));
   875         }
   876     else
   877         {
   878         REMOTE_INFO_PRINTF1(_L("No heap memory leak detected."));
   879         }
   880     return memoryIsLeaking ? ERtvFail : ERtvPass;
   881     }
   882 
   883 //--------------------------------------------------------------------------------