Przeglądaj źródła

[N-Gage] Resolve hang on repeated app launch.

CAudio::~CAudio() waited on MaoscBufferCopied(KErrAbort) to set
EStateDone, but that callback can never fire once the active scheduler
loop has exited, deadlocking every close.

- StopThread() before iStream->Stop() in ~CAudio()
- Force iState = EStateDone instead of waiting on a dead callback
- Add 5s timeout to AudioIsReady() poll in E32Main()
- Fix CleanupStack LIFO pop order (mainApp before gRenderer)
Michael Fitzmayer 1 tydzień temu
rodzic
commit
51925aa92e

+ 44 - 75
src/audio/ngage/SDL_ngageaudio.cpp

@@ -75,13 +75,17 @@ void CAudio::ConstructL(TInt aLatency)
 CAudio::~CAudio()
 CAudio::~CAudio()
 {
 {
     if (iStream) {
     if (iStream) {
+        // Stop the processing thread before stopping the stream.
+        StopThread();
+
         iStream->Stop();
         iStream->Stop();
 
 
-        while (iState != EStateDone) {
-            User::After(100000); // 100ms.
-        }
+        // MaoscBufferCopied(KErrAbort) can't fire here because the active
+        // scheduler is no longer pumping; force the state to avoid deadlock.
+        iState = EStateDone;
 
 
         delete iStream;
         delete iStream;
+        iStream = NULL;
     }
     }
 }
 }
 
 
@@ -98,12 +102,9 @@ void CAudio::Start()
     }
     }
 }
 }
 
 
-
-
 void CAudio::RunL()
 void CAudio::RunL()
 {
 {
     iTimerActive = EFalse;
     iTimerActive = EFalse;
-
 }
 }
 
 
 void CAudio::DoCancel()
 void CAudio::DoCancel()
@@ -117,10 +118,8 @@ void CAudio::StartThread()
     TInt heapMinSize = 8192;        // 8 KB initial heap size.
     TInt heapMinSize = 8192;        // 8 KB initial heap size.
     TInt heapMaxSize = 1024 * 1024; // 1 MB maximum heap size.
     TInt heapMaxSize = 1024 * 1024; // 1 MB maximum heap size.
 
 
-
     TInt err = iProcess.Create(_L("ProcessThread"), ProcessThreadCB, KDefaultStackSize * 2, heapMinSize, heapMaxSize, this);
     TInt err = iProcess.Create(_L("ProcessThread"), ProcessThreadCB, KDefaultStackSize * 2, heapMinSize, heapMaxSize, this);
-    if (err == KErrNone)
-    {
+    if (err == KErrNone) {
         TThreadPriority prio = EPriorityLess;
         TThreadPriority prio = EPriorityLess;
 
 
         const char *prioHint = SDL_GetHint(SDL_HINT_AUDIO_NGAGE_PROCESS_PRIORITY);
         const char *prioHint = SDL_GetHint(SDL_HINT_AUDIO_NGAGE_PROCESS_PRIORITY);
@@ -130,7 +129,6 @@ void CAudio::StartThread()
             RThread().SetPriority(prio);
             RThread().SetPriority(prio);
         }
         }
 
 
-
         iProcess.SetPriority(prio);
         iProcess.SetPriority(prio);
         iProcess.Resume();
         iProcess.Resume();
     } else {
     } else {
@@ -148,42 +146,36 @@ void CAudio::StopThread()
 }
 }
 
 
 /***************************************************
 /***************************************************
-* ProcessThreadCB -
-*
-* This thread calls the SDL mixer when the buffer is ready and self->iState == EStatePlaying (basically other than initial stated, when not writing)
-*
-* It only mixes, never calls WriteL
-****************************************************/
+ * ProcessThreadCB -
+ *
+ * This thread calls the SDL mixer when the buffer is ready and self->iState == EStatePlaying (basically other than initial stated, when not writing)
+ *
+ * It only mixes, never calls WriteL
+ ****************************************************/
 
 
 TInt CAudio::ProcessThreadCB(TAny *aPtr)
 TInt CAudio::ProcessThreadCB(TAny *aPtr)
 {
 {
     CTrapCleanup *cleanup = CTrapCleanup::New();
     CTrapCleanup *cleanup = CTrapCleanup::New();
     if (!cleanup)
     if (!cleanup)
         return KErrNoMemory;
         return KErrNoMemory;
-  
+
     CAudio *self = static_cast<CAudio *>(aPtr);
     CAudio *self = static_cast<CAudio *>(aPtr);
     SDL_AudioDevice *device = NGAGE_GetAudioDeviceAddr();
     SDL_AudioDevice *device = NGAGE_GetAudioDeviceAddr();
 
 
-
     TInt processTick = 40000; // Default 40ms
     TInt processTick = 40000; // Default 40ms
     const char *tickHint = SDL_GetHint(SDL_HINT_AUDIO_NGAGE_PROCESS_TICK);
     const char *tickHint = SDL_GetHint(SDL_HINT_AUDIO_NGAGE_PROCESS_TICK);
-    if (tickHint)
-    {
+    if (tickHint) {
         processTick = SDL_atoi(tickHint) * 1000;
         processTick = SDL_atoi(tickHint) * 1000;
     }
     }
 
 
-    while (self->iStreamStarted)
-    {
-        if (self->iState == EStatePlaying && !self->iBufferReady)
-        {
-             /* Ask SDL to mix audio into buffer[fill_index]*/
+    while (self->iStreamStarted) {
+        if (self->iState == EStatePlaying && !self->iBufferReady) {
+            /* Ask SDL to mix audio into buffer[fill_index]*/
             SDL_PlaybackAudioThreadIterate(device);
             SDL_PlaybackAudioThreadIterate(device);
 
 
-             /*  Signal AudioThreadCB to write it*/
+            /*  Signal AudioThreadCB to write it*/
             self->iBufferReady = ETrue;
             self->iBufferReady = ETrue;
-        }
-        else
-        {
+        } else {
             /*if we are not ready to obtain the mix data we sleep a bit this thread*/
             /*if we are not ready to obtain the mix data we sleep a bit this thread*/
             User::After(processTick);
             User::After(processTick);
         }
         }
@@ -195,17 +187,17 @@ TInt CAudio::ProcessThreadCB(TAny *aPtr)
 
 
 static TBool gAudioRunning;
 static TBool gAudioRunning;
 /***************************************************
 /***************************************************
-* AudioThreadCB -
-*
-* This thread owns the scheduler and calls WriteL, wich queues the assigned sound buffer to be played
-****************************************************/
+ * AudioThreadCB -
+ *
+ * This thread owns the scheduler and calls WriteL, wich queues the assigned sound buffer to be played
+ ****************************************************/
 
 
 TInt AudioThreadCB(TAny *aParams)
 TInt AudioThreadCB(TAny *aParams)
 {
 {
     CTrapCleanup *cleanup = CTrapCleanup::New();
     CTrapCleanup *cleanup = CTrapCleanup::New();
     CActiveScheduler *scheduler = new CActiveScheduler();
     CActiveScheduler *scheduler = new CActiveScheduler();
     CActiveScheduler::Install(scheduler);
     CActiveScheduler::Install(scheduler);
-    
+
     TRAPD(err, {
     TRAPD(err, {
         TInt latency = *(TInt *)aParams;
         TInt latency = *(TInt *)aParams;
         CAudio *audio = CAudio::NewL(latency);
         CAudio *audio = CAudio::NewL(latency);
@@ -215,7 +207,6 @@ TInt AudioThreadCB(TAny *aParams)
 
 
         gAudioRunning = ETrue;
         gAudioRunning = ETrue;
         audio->Start();
         audio->Start();
-      
 
 
         TInt processTick = 5000; // Default 5ms
         TInt processTick = 5000; // Default 5ms
         const char *tickHint = SDL_GetHint(SDL_HINT_AUDIO_NGAGE_PROCESS_TICK);
         const char *tickHint = SDL_GetHint(SDL_HINT_AUDIO_NGAGE_PROCESS_TICK);
@@ -223,34 +214,27 @@ TInt AudioThreadCB(TAny *aParams)
             processTick = SDL_atoi(tickHint) * 1000;
             processTick = SDL_atoi(tickHint) * 1000;
         }
         }
 
 
-
-        while (gAudioRunning)
-        {
+        while (gAudioRunning) {
             TInt error;
             TInt error;
             CActiveScheduler::RunIfReady(error, CActive::EPriorityIdle);
             CActiveScheduler::RunIfReady(error, CActive::EPriorityIdle);
-           
+
             /*there is some mix data sound ready*/
             /*there is some mix data sound ready*/
-            if (audio->iBufferReady)
-            {
+            if (audio->iBufferReady) {
                 audio->iBufferReady = EFalse;
                 audio->iBufferReady = EFalse;
 
 
                 SDL_AudioDevice *device = NGAGE_GetAudioDeviceAddr();
                 SDL_AudioDevice *device = NGAGE_GetAudioDeviceAddr();
 
 
-                if (device && device->hidden)
-                {
+                if (device && device->hidden) {
                     SDL_PrivateAudioData *phdata = (SDL_PrivateAudioData *)device->hidden;
                     SDL_PrivateAudioData *phdata = (SDL_PrivateAudioData *)device->hidden;
                     audio->iState = EStateWriting;
                     audio->iState = EStateWriting;
                     /*sends the chuck mixed to the queue*/
                     /*sends the chuck mixed to the queue*/
                     audio->iBufDes.Set(phdata->buffer[phdata->fill_index], device->buffer_size, device->buffer_size);
                     audio->iBufDes.Set(phdata->buffer[phdata->fill_index], device->buffer_size, device->buffer_size);
                     TRAPD(werr, audio->iStream->WriteL(audio->iBufDes));
                     TRAPD(werr, audio->iStream->WriteL(audio->iBufDes));
 
 
-                    if (werr != KErrNone)
-                    {
+                    if (werr != KErrNone) {
                         /*asks ProcessThreadCB to bring another mix chunk*/
                         /*asks ProcessThreadCB to bring another mix chunk*/
                         audio->iState = EStatePlaying;
                         audio->iState = EStatePlaying;
-                    }
-                    else
-                    {
+                    } else {
                         /*swap buffers so while this buffer is being played we can get the mix of the next one if we can*/
                         /*swap buffers so while this buffer is being played we can get the mix of the next one if we can*/
                         phdata->fill_index = 1 - phdata->fill_index;
                         phdata->fill_index = 1 - phdata->fill_index;
                     }
                     }
@@ -270,16 +254,15 @@ TInt AudioThreadCB(TAny *aParams)
 }
 }
 
 
 /***************************************************
 /***************************************************
-* MaoscOpenComplete -
-*
-* Opens the audiostream
-*
-* *******************************************************/
+ * MaoscOpenComplete -
+ *
+ * Opens the audiostream
+ *
+ * *******************************************************/
 
 
 void CAudio::MaoscOpenComplete(TInt aError)
 void CAudio::MaoscOpenComplete(TInt aError)
 {
 {
-    if (aError == KErrNone)
-    {
+    if (aError == KErrNone) {
         /*setting the volume to max, users can change the volume later of their channels individually in code*/
         /*setting the volume to max, users can change the volume later of their channels individually in code*/
         iStream->SetVolume(iStream->MaxVolume());
         iStream->SetVolume(iStream->MaxVolume());
         iStreamStarted = ETrue;
         iStreamStarted = ETrue;
@@ -296,11 +279,8 @@ void CAudio::MaoscOpenComplete(TInt aError)
 
 
         /* Kickstart: device is guaranteed valid now*/
         /* Kickstart: device is guaranteed valid now*/
         this->iState = EStatePlaying;
         this->iState = EStatePlaying;
-       
 
 
-    }
-    else
-    {
+    } else {
         SDL_Log("Error: Failed to open audio stream: %d", aError);
         SDL_Log("Error: Failed to open audio stream: %d", aError);
     }
     }
 }
 }
@@ -314,17 +294,12 @@ void CAudio::MaoscOpenComplete(TInt aError)
 
 
 void CAudio::MaoscBufferCopied(TInt aError, const TDesC8 & /*aBuffer*/)
 void CAudio::MaoscBufferCopied(TInt aError, const TDesC8 & /*aBuffer*/)
 {
 {
-    if (aError == KErrNone)
-    {
+    if (aError == KErrNone) {
         iState = EStatePlaying;
         iState = EStatePlaying;
-    }
-    else if (aError == KErrAbort)
-    {
+    } else if (aError == KErrAbort) {
         /* The stream has been stopped.*/
         /* The stream has been stopped.*/
         iState = EStateDone;
         iState = EStateDone;
-    }
-    else
-    {
+    } else {
         SDL_Log("Error: Failed to copy audio buffer: %d", aError);
         SDL_Log("Error: Failed to copy audio buffer: %d", aError);
     }
     }
 }
 }
@@ -338,13 +313,12 @@ void CAudio::MaoscBufferCopied(TInt aError, const TDesC8 & /*aBuffer*/)
 
 
 void CAudio::MaoscPlayComplete(TInt aError)
 void CAudio::MaoscPlayComplete(TInt aError)
 {
 {
-   
+
     /* If we finish due to an underflow, we'll need to restart playback.
     /* If we finish due to an underflow, we'll need to restart playback.
      Normally KErrUnderlow is raised   at stream end, but in our case the API
      Normally KErrUnderlow is raised   at stream end, but in our case the API
      should never see the stream end -- we are continuously feeding it more
      should never see the stream end -- we are continuously feeding it more
      data!  Many underflow errors mean that the latency target is too low.*/
      data!  Many underflow errors mean that the latency target is too low.*/
-    if (aError == KErrUnderflow)
-    {
+    if (aError == KErrUnderflow) {
         /*  Restart the stream hardware */
         /*  Restart the stream hardware */
         iStream->Stop();
         iStream->Stop();
         TInt ignoredError;
         TInt ignoredError;
@@ -355,22 +329,17 @@ void CAudio::MaoscPlayComplete(TInt aError)
 
 
         return;
         return;
     } else if (aError != KErrNone) {
     } else if (aError != KErrNone) {
-        
     }
     }
 
 
     /* We shouldn't get here.*/
     /* We shouldn't get here.*/
     SDL_Log("%s: %d", SDL_FUNCTION, aError);
     SDL_Log("%s: %d", SDL_FUNCTION, aError);
 }
 }
 
 
-
-
 TBool AudioIsReady()
 TBool AudioIsReady()
 {
 {
     return gAudioRunning;
     return gAudioRunning;
 }
 }
 
 
-
-
 RThread audioThread;
 RThread audioThread;
 
 
 void InitAudio(TInt *aLatency)
 void InitAudio(TInt *aLatency)

+ 10 - 3
src/main/ngage/SDL_sysmain_main.cpp

@@ -87,9 +87,15 @@ GLDEF_C TInt E32Main()
               TInt targetLatency = 225;
               TInt targetLatency = 225;
               InitAudio(&targetLatency);
               InitAudio(&targetLatency);
 
 
-              // Wait until audio is ready.
-              while (!AudioIsReady()) {
+              // Wait until audio is ready (timeout after ~5 seconds).
+              TInt audioWaitMs = 0;
+              while (!AudioIsReady() && audioWaitMs < 5000) {
                   User::After(100000); // 100ms.
                   User::After(100000); // 100ms.
+                  audioWaitMs += 100;
+              }
+              if (!AudioIsReady()) {
+                  SDL_Log("Error: Audio failed to initialise within timeout");
+                  User::Leave(KErrTimedOut);
               }
               }
 
 
               // Create and start the rendering backend.
               // Create and start the rendering backend.
@@ -104,8 +110,8 @@ GLDEF_C TInt E32Main()
               // Start the active scheduler to handle events.
               // Start the active scheduler to handle events.
               CActiveScheduler::Start();
               CActiveScheduler::Start();
 
 
-              CleanupStack::PopAndDestroy(gRenderer);
               CleanupStack::PopAndDestroy(mainApp);
               CleanupStack::PopAndDestroy(mainApp);
+              CleanupStack::PopAndDestroy(gRenderer);
 
 
               User::SwitchHeap(oldHeap);
               User::SwitchHeap(oldHeap);
 
 
@@ -154,6 +160,7 @@ static bool callbacks_initialized = false;
 
 
 static void ShutdownApp(SDL_AppResult result)
 static void ShutdownApp(SDL_AppResult result)
 {
 {
+    callbacks_initialized = false;
     DeinitAudio();
     DeinitAudio();
     SDL_AppQuit(NULL, result);
     SDL_AppQuit(NULL, result);
     SDL_Quit();
     SDL_Quit();