Made it safe to update joysticks from multiple threads, fixes crash in Steam
authorSam Lantinga <slouken@libsdl.org>
Tue, 29 Nov 2016 05:04:42 -0800
changeset 10659419028eda223
parent 10658 66c0c39a4e19
child 10660 1ae4e8001b73
Made it safe to update joysticks from multiple threads, fixes crash in Steam
src/joystick/SDL_joystick.c
     1.1 --- a/src/joystick/SDL_joystick.c	Mon Nov 28 08:05:45 2016 -0800
     1.2 +++ b/src/joystick/SDL_joystick.c	Tue Nov 29 05:04:42 2016 -0800
     1.3 @@ -35,6 +35,24 @@
     1.4  static SDL_bool SDL_joystick_allows_background_events = SDL_FALSE;
     1.5  static SDL_Joystick *SDL_joysticks = NULL;
     1.6  static SDL_Joystick *SDL_updating_joystick = NULL;
     1.7 +static SDL_mutex *SDL_joystick_lock = NULL; /* This needs to support recursive locks */
     1.8 +
     1.9 +static void
    1.10 +SDL_LockJoystickList()
    1.11 +{
    1.12 +    if (SDL_joystick_lock) {
    1.13 +        SDL_LockMutex(SDL_joystick_lock);
    1.14 +    }
    1.15 +}
    1.16 +
    1.17 +static void
    1.18 +SDL_UnlockJoystickList()
    1.19 +{
    1.20 +    if (SDL_joystick_lock) {
    1.21 +        SDL_UnlockMutex(SDL_joystick_lock);
    1.22 +    }
    1.23 +}
    1.24 +
    1.25  
    1.26  static void
    1.27  SDL_JoystickAllowBackgroundEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
    1.28 @@ -51,6 +69,11 @@
    1.29  {
    1.30      int status;
    1.31  
    1.32 +    /* Create the joystick list lock */
    1.33 +    if (!SDL_joystick_lock) {
    1.34 +        SDL_joystick_lock = SDL_CreateMutex();
    1.35 +    }
    1.36 +
    1.37      /* See if we should allow joystick events while in the background */
    1.38      SDL_AddHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
    1.39                          SDL_JoystickAllowBackgroundEventsChanged, NULL);
    1.40 @@ -109,6 +132,8 @@
    1.41          return (NULL);
    1.42      }
    1.43  
    1.44 +    SDL_LockJoystickList();
    1.45 +
    1.46      joysticklist = SDL_joysticks;
    1.47      /* If the joystick is already open, return it
    1.48      * it is important that we have a single joystick * for each instance id
    1.49 @@ -117,6 +142,7 @@
    1.50          if (SDL_SYS_GetInstanceIdOfDeviceIndex(device_index) == joysticklist->instance_id) {
    1.51                  joystick = joysticklist;
    1.52                  ++joystick->ref_count;
    1.53 +                SDL_UnlockJoystickList();
    1.54                  return (joystick);
    1.55          }
    1.56          joysticklist = joysticklist->next;
    1.57 @@ -126,12 +152,14 @@
    1.58      joystick = (SDL_Joystick *) SDL_malloc((sizeof *joystick));
    1.59      if (joystick == NULL) {
    1.60          SDL_OutOfMemory();
    1.61 +        SDL_UnlockJoystickList();
    1.62          return NULL;
    1.63      }
    1.64  
    1.65      SDL_memset(joystick, 0, (sizeof *joystick));
    1.66      if (SDL_SYS_JoystickOpen(joystick, device_index) < 0) {
    1.67          SDL_free(joystick);
    1.68 +        SDL_UnlockJoystickList();
    1.69          return NULL;
    1.70      }
    1.71  
    1.72 @@ -163,6 +191,7 @@
    1.73          || ((joystick->nbuttons > 0) && !joystick->buttons)) {
    1.74          SDL_OutOfMemory();
    1.75          SDL_JoystickClose(joystick);
    1.76 +        SDL_UnlockJoystickList();
    1.77          return NULL;
    1.78      }
    1.79      if (joystick->axes) {
    1.80 @@ -189,6 +218,8 @@
    1.81  
    1.82      SDL_SYS_JoystickUpdate(joystick);
    1.83  
    1.84 +    SDL_UnlockJoystickList();
    1.85 +
    1.86      return (joystick);
    1.87  }
    1.88  
    1.89 @@ -380,14 +411,16 @@
    1.90  SDL_Joystick *
    1.91  SDL_JoystickFromInstanceID(SDL_JoystickID joyid)
    1.92  {
    1.93 -    SDL_Joystick *joystick = SDL_joysticks;
    1.94 -    while (joystick) {
    1.95 +    SDL_Joystick *joystick;
    1.96 +
    1.97 +    SDL_LockJoystickList();
    1.98 +    for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
    1.99          if (joystick->instance_id == joyid) {
   1.100 +            SDL_UnlockJoystickList();
   1.101              return joystick;
   1.102          }
   1.103 -        joystick = joystick->next;
   1.104      }
   1.105 -
   1.106 +    SDL_UnlockJoystickList();
   1.107      return NULL;
   1.108  }
   1.109  
   1.110 @@ -417,12 +450,16 @@
   1.111          return;
   1.112      }
   1.113  
   1.114 +    SDL_LockJoystickList();
   1.115 +
   1.116      /* First decrement ref count */
   1.117      if (--joystick->ref_count > 0) {
   1.118 +        SDL_UnlockJoystickList();
   1.119          return;
   1.120      }
   1.121  
   1.122      if (joystick == SDL_updating_joystick) {
   1.123 +        SDL_UnlockJoystickList();
   1.124          return;
   1.125      }
   1.126  
   1.127 @@ -453,6 +490,8 @@
   1.128      SDL_free(joystick->balls);
   1.129      SDL_free(joystick->buttons);
   1.130      SDL_free(joystick);
   1.131 +
   1.132 +    SDL_UnlockJoystickList();
   1.133  }
   1.134  
   1.135  void
   1.136 @@ -461,6 +500,8 @@
   1.137      /* Make sure we're not getting called in the middle of updating joysticks */
   1.138      SDL_assert(!SDL_updating_joystick);
   1.139  
   1.140 +    SDL_LockJoystickList();
   1.141 +
   1.142      /* Stop the event polling */
   1.143      while (SDL_joysticks) {
   1.144          SDL_joysticks->ref_count = 1;
   1.145 @@ -470,9 +511,16 @@
   1.146      /* Quit the joystick setup */
   1.147      SDL_SYS_JoystickQuit();
   1.148  
   1.149 +    SDL_UnlockJoystickList();
   1.150 +
   1.151  #if !SDL_EVENTS_DISABLED
   1.152      SDL_QuitSubSystem(SDL_INIT_EVENTS);
   1.153  #endif
   1.154 +
   1.155 +    if (SDL_joystick_lock) {
   1.156 +        SDL_DestroyMutex(SDL_joystick_lock);
   1.157 +        SDL_joystick_lock = NULL;
   1.158 +    }
   1.159  }
   1.160  
   1.161  
   1.162 @@ -735,11 +783,11 @@
   1.163  void
   1.164  SDL_JoystickUpdate(void)
   1.165  {
   1.166 -    SDL_Joystick *joystick;
   1.167 +    SDL_Joystick *joystick, *joysticknext;
   1.168  
   1.169 -    joystick = SDL_joysticks;
   1.170 -    while (joystick) {
   1.171 -        SDL_Joystick *joysticknext;
   1.172 +    SDL_LockJoystickList();
   1.173 +
   1.174 +    for (joystick = SDL_joysticks; joystick; joystick = joysticknext) {
   1.175          /* save off the next pointer, the Update call may cause a joystick removed event
   1.176           * and cause our joystick pointer to be freed
   1.177           */
   1.178 @@ -782,6 +830,8 @@
   1.179         dangling hardware data from removed devices can be free'd
   1.180       */
   1.181      SDL_SYS_JoystickDetect();
   1.182 +
   1.183 +    SDL_UnlockJoystickList();
   1.184  }
   1.185  
   1.186  int