Fixed bug 4142 - Concurrency issues in Android backend
authorSylvain Becker <sylvain.becker@gmail.com>
Thu, 03 Jan 2019 14:18:06 +0100
changeset 1249693771c30420b
parent 12495 219a154f54cb
child 12497 9e13f3286831
Fixed bug 4142 - Concurrency issues in Android backend

Use a semaphore to prevent concurrency issues between Java Activity and Native thread code, when using 'Android_Window'.
(Eg. Java sending Touch events, while native code is destroying the main SDL_Window. )
src/core/android/SDL_android.c
src/video/android/SDL_androidevents.c
src/video/android/SDL_androidvideo.c
src/video/android/SDL_androidvideo.h
src/video/android/SDL_androidwindow.c
     1.1 --- a/src/core/android/SDL_android.c	Thu Jan 03 13:38:33 2019 +0100
     1.2 +++ b/src/core/android/SDL_android.c	Thu Jan 03 14:18:06 2019 +0100
     1.3 @@ -322,6 +322,17 @@
     1.4  {
     1.5      __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeSetupJNI()");
     1.6  
     1.7 +    /* Use a semaphore to prevent concurrency issues between Java Activity and Native thread code, when using 'Android_Window'.
     1.8 +     * (Eg. Java sending Touch events, while native code is destroying the main SDL_Window. )
     1.9 +     */
    1.10 +    if (Android_ActivitySem == NULL) {
    1.11 +        Android_ActivitySem = SDL_CreateSemaphore(1); /* Could this be created twice if onCreate() is called a second time ? */
    1.12 +    }
    1.13 +
    1.14 +    if (Android_ActivitySem == NULL) {
    1.15 +        __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "failed to create Android_ActivitySem semaphore");
    1.16 +    }
    1.17 +
    1.18      Android_JNI_SetupThread();
    1.19  
    1.20      mActivityClass = (jclass)((*mEnv)->NewGlobalRef(mEnv, cls));
    1.21 @@ -558,7 +569,11 @@
    1.22                                      jint surfaceWidth, jint surfaceHeight,
    1.23                                      jint deviceWidth, jint deviceHeight, jint format, jfloat rate)
    1.24  {
    1.25 +    SDL_SemWait(Android_ActivitySem);
    1.26 +
    1.27      Android_SetScreenResolution(Android_Window, surfaceWidth, surfaceHeight, deviceWidth, deviceHeight, format, rate);
    1.28 +
    1.29 +    SDL_SemPost(Android_ActivitySem);
    1.30  }
    1.31  
    1.32  JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeOrientationChanged)(
    1.33 @@ -650,6 +665,8 @@
    1.34  /* Surface Created */
    1.35  JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceChanged)(JNIEnv *env, jclass jcls)
    1.36  {
    1.37 +    SDL_SemWait(Android_ActivitySem);
    1.38 +
    1.39      if (Android_Window && Android_Window->driverdata)
    1.40      {
    1.41          SDL_VideoDevice *_this = SDL_GetVideoDevice();
    1.42 @@ -666,11 +683,15 @@
    1.43  
    1.44          /* GL Context handling is done in the event loop because this function is run from the Java thread */
    1.45      }
    1.46 +
    1.47 +    SDL_SemPost(Android_ActivitySem);
    1.48  }
    1.49  
    1.50  /* Surface Destroyed */
    1.51  JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeSurfaceDestroyed)(JNIEnv *env, jclass jcls)
    1.52  {
    1.53 +    SDL_SemWait(Android_ActivitySem);
    1.54 +
    1.55      if (Android_Window && Android_Window->driverdata)
    1.56      {
    1.57          SDL_VideoDevice *_this = SDL_GetVideoDevice();
    1.58 @@ -689,6 +710,8 @@
    1.59  
    1.60          /* GL Context handling is done in the event loop because this function is run from the Java thread */
    1.61      }
    1.62 +
    1.63 +    SDL_SemPost(Android_ActivitySem);
    1.64  }
    1.65  
    1.66  /* Keydown */
    1.67 @@ -722,7 +745,11 @@
    1.68                                      jint touch_device_id_in, jint pointer_finger_id_in,
    1.69                                      jint action, jfloat x, jfloat y, jfloat p)
    1.70  {
    1.71 +    SDL_SemWait(Android_ActivitySem);
    1.72 +
    1.73      Android_OnTouch(Android_Window, touch_device_id_in, pointer_finger_id_in, action, x, y, p);
    1.74 +
    1.75 +    SDL_SemPost(Android_ActivitySem);
    1.76  }
    1.77  
    1.78  /* Mouse */
    1.79 @@ -730,7 +757,11 @@
    1.80                                      JNIEnv *env, jclass jcls,
    1.81                                      jint button, jint action, jfloat x, jfloat y, jboolean relative)
    1.82  {
    1.83 +    SDL_SemWait(Android_ActivitySem);
    1.84 +
    1.85      Android_OnMouse(Android_Window, button, action, x, y, relative);
    1.86 +
    1.87 +    SDL_SemPost(Android_ActivitySem);
    1.88  }
    1.89  
    1.90  /* Accelerometer */
    1.91 @@ -778,6 +809,8 @@
    1.92  JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativePause)(
    1.93                                      JNIEnv *env, jclass cls)
    1.94  {
    1.95 +    SDL_SemWait(Android_ActivitySem);
    1.96 +
    1.97      __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativePause()");
    1.98  
    1.99      if (Android_Window) {
   1.100 @@ -790,12 +823,16 @@
   1.101           * so the event loop knows to pause and (optionally) block itself */
   1.102          if (!SDL_SemValue(Android_PauseSem)) SDL_SemPost(Android_PauseSem);
   1.103      }
   1.104 +
   1.105 +    SDL_SemPost(Android_ActivitySem);
   1.106  }
   1.107  
   1.108  /* Resume */
   1.109  JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeResume)(
   1.110                                      JNIEnv *env, jclass cls)
   1.111  {
   1.112 +    SDL_SemWait(Android_ActivitySem);
   1.113 +
   1.114      __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "nativeResume()");
   1.115  
   1.116      if (Android_Window) {
   1.117 @@ -816,6 +853,8 @@
   1.118           */
   1.119          if (!SDL_SemValue(Android_ResumeSem)) SDL_SemPost(Android_ResumeSem);
   1.120      }
   1.121 +
   1.122 +    SDL_SemPost(Android_ActivitySem);
   1.123  }
   1.124  
   1.125  JNIEXPORT void JNICALL SDL_JAVA_INTERFACE_INPUT_CONNECTION(nativeCommitText)(
     2.1 --- a/src/video/android/SDL_androidevents.c	Thu Jan 03 13:38:33 2019 +0100
     2.2 +++ b/src/video/android/SDL_androidevents.c	Thu Jan 03 14:18:06 2019 +0100
     2.3 @@ -39,8 +39,8 @@
     2.4  static void ANDROIDAUDIO_PauseDevices(void) {}
     2.5  #endif
     2.6  
     2.7 -static void 
     2.8 -android_egl_context_restore(SDL_Window *window) 
     2.9 +static void
    2.10 +android_egl_context_restore(SDL_Window *window)
    2.11  {
    2.12      SDL_Event event;
    2.13      SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    2.14 @@ -53,8 +53,8 @@
    2.15      }
    2.16  }
    2.17  
    2.18 -static void 
    2.19 -android_egl_context_backup(SDL_Window *window) 
    2.20 +static void
    2.21 +android_egl_context_backup(SDL_Window *window)
    2.22  {
    2.23      /* Keep a copy of the EGL Context so we can try to restore it when we resume */
    2.24      SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    2.25 @@ -80,7 +80,10 @@
    2.26  #if SDL_ANDROID_BLOCK_ON_PAUSE
    2.27      if (isPaused && !isPausing) {
    2.28          /* Make sure this is the last thing we do before pausing */
    2.29 +        SDL_SemWait(Android_ActivitySem);
    2.30          android_egl_context_backup(Android_Window);
    2.31 +        SDL_SemPost(Android_ActivitySem);
    2.32 +
    2.33          ANDROIDAUDIO_PauseDevices();
    2.34          if (SDL_SemWait(Android_ResumeSem) == 0) {
    2.35  #else
    2.36 @@ -91,7 +94,9 @@
    2.37              ANDROIDAUDIO_ResumeDevices();
    2.38              /* Restore the GL Context from here, as this operation is thread dependent */
    2.39              if (!SDL_HasEvent(SDL_QUIT)) {
    2.40 +                SDL_SemWait(Android_ActivitySem);
    2.41                  android_egl_context_restore(Android_Window);
    2.42 +                SDL_SemPost(Android_ActivitySem);
    2.43              }
    2.44          }
    2.45      }
    2.46 @@ -110,7 +115,10 @@
    2.47          }
    2.48  #else
    2.49          if (SDL_SemTryWait(Android_PauseSem) == 0) {
    2.50 +            SDL_SemWait(Android_ActivitySem);
    2.51              android_egl_context_backup(Android_Window);
    2.52 +            SDL_SemPost(Android_ActivitySem);
    2.53 +
    2.54              ANDROIDAUDIO_PauseDevices();
    2.55              isPaused = 1;
    2.56          }
     3.1 --- a/src/video/android/SDL_androidvideo.c	Thu Jan 03 13:38:33 2019 +0100
     3.2 +++ b/src/video/android/SDL_androidvideo.c	Thu Jan 03 14:18:06 2019 +0100
     3.3 @@ -66,7 +66,7 @@
     3.4  static Uint32 Android_ScreenFormat = SDL_PIXELFORMAT_UNKNOWN;
     3.5  static int Android_ScreenRate = 0;
     3.6  
     3.7 -SDL_sem *Android_PauseSem = NULL, *Android_ResumeSem = NULL;
     3.8 +SDL_sem *Android_PauseSem = NULL, *Android_ResumeSem = NULL, *Android_ActivitySem = NULL;
     3.9  
    3.10  static int
    3.11  Android_Available(void)
     4.1 --- a/src/video/android/SDL_androidvideo.h	Thu Jan 03 13:38:33 2019 +0100
     4.2 +++ b/src/video/android/SDL_androidvideo.h	Thu Jan 03 14:18:06 2019 +0100
     4.3 @@ -41,7 +41,7 @@
     4.4  extern int Android_SurfaceHeight;
     4.5  extern int Android_DeviceWidth;
     4.6  extern int Android_DeviceHeight;
     4.7 -extern SDL_sem *Android_PauseSem, *Android_ResumeSem;
     4.8 +extern SDL_sem *Android_PauseSem, *Android_ResumeSem, *Android_ActivitySem;
     4.9  
    4.10  #endif /* SDL_androidvideo_h_ */
    4.11  
     5.1 --- a/src/video/android/SDL_androidwindow.c	Thu Jan 03 13:38:33 2019 +0100
     5.2 +++ b/src/video/android/SDL_androidwindow.c	Thu Jan 03 14:18:06 2019 +0100
     5.3 @@ -41,12 +41,14 @@
     5.4  {
     5.5      SDL_WindowData *data;
     5.6      int retval = 0;
     5.7 -    
     5.8 +
     5.9 +    SDL_SemWait(Android_ActivitySem);
    5.10 +
    5.11      if (Android_Window) {
    5.12          retval = SDL_SetError("Android only supports one window");
    5.13          goto endfunction;
    5.14      }
    5.15 -    
    5.16 +
    5.17      Android_PauseSem = SDL_CreateSemaphore(0);
    5.18      Android_ResumeSem = SDL_CreateSemaphore(0);
    5.19  
    5.20 @@ -67,15 +69,15 @@
    5.21      /* One window, it always has focus */
    5.22      SDL_SetMouseFocus(window);
    5.23      SDL_SetKeyboardFocus(window);
    5.24 -    
    5.25 +
    5.26      data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
    5.27      if (!data) {
    5.28          retval = SDL_OutOfMemory();
    5.29          goto endfunction;
    5.30      }
    5.31 -    
    5.32 +
    5.33      data->native_window = Android_JNI_GetNativeWindow();
    5.34 -    
    5.35 +
    5.36      if (!data->native_window) {
    5.37          SDL_free(data);
    5.38          retval = SDL_SetError("Could not fetch native window");
    5.39 @@ -99,7 +101,9 @@
    5.40      Android_Window = window;
    5.41  
    5.42  endfunction:
    5.43 -    
    5.44 +
    5.45 +    SDL_SemPost(Android_ActivitySem);
    5.46 +
    5.47      return retval;
    5.48  }
    5.49  
    5.50 @@ -146,14 +150,16 @@
    5.51  
    5.52  void
    5.53  Android_DestroyWindow(_THIS, SDL_Window *window)
    5.54 -{ 
    5.55 +{
    5.56 +    SDL_SemWait(Android_ActivitySem);
    5.57 +
    5.58      if (window == Android_Window) {
    5.59          Android_Window = NULL;
    5.60          if (Android_PauseSem) SDL_DestroySemaphore(Android_PauseSem);
    5.61          if (Android_ResumeSem) SDL_DestroySemaphore(Android_ResumeSem);
    5.62          Android_PauseSem = NULL;
    5.63          Android_ResumeSem = NULL;
    5.64 -        
    5.65 +
    5.66          if (window->driverdata) {
    5.67              SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
    5.68              if (data->egl_surface != EGL_NO_SURFACE) {
    5.69 @@ -166,6 +172,8 @@
    5.70              window->driverdata = NULL;
    5.71          }
    5.72      }
    5.73 +
    5.74 +    SDL_SemPost(Android_ActivitySem);
    5.75  }
    5.76  
    5.77  SDL_bool