Added support for clang thread-safety analysis

The annotations have been added to SDL_mutex.h and have been made public so applications can enable this for their own code.

Clang assumes that locking and unlocking can't fail, but SDL has the concept of a NULL mutex, so the mutex functions have been changed not to report errors if a mutex hasn't been initialized. We do have mutexes that might be accessed when they are NULL, notably in the event system, so this is an important change.

This commit cleans up a bunch of rare race conditions in the joystick and game controller code so now everything should be completely protected by the joystick lock.

To test this, change the compiler to "clang -Wthread-safety -Werror=thread-safety -DSDL_THREAD_SAFETY_ANALYSIS"
This commit is contained in:
Sam Lantinga 2022-12-13 14:03:40 -08:00
parent 582fb3901a
commit d59caffe2c
42 changed files with 1667 additions and 1174 deletions

View file

@ -852,6 +852,8 @@ static int allocate_hatdata(SDL_Joystick *joystick)
{
int i;
SDL_AssertJoysticksLocked();
joystick->hwdata->hats =
(struct hwdata_hat *)SDL_malloc(joystick->nhats *
sizeof(struct hwdata_hat));
@ -869,6 +871,8 @@ static int allocate_balldata(SDL_Joystick *joystick)
{
int i;
SDL_AssertJoysticksLocked();
joystick->hwdata->balls =
(struct hwdata_ball *)SDL_malloc(joystick->nballs *
sizeof(struct hwdata_ball));
@ -925,6 +929,8 @@ static void ConfigJoystick(SDL_Joystick *joystick, int fd)
SDL_bool use_deadzones = SDL_GetHintBoolean(SDL_HINT_LINUX_JOYSTICK_DEADZONES, SDL_FALSE);
SDL_bool use_hat_deadzones = SDL_GetHintBoolean(SDL_HINT_LINUX_HAT_DEADZONES, SDL_TRUE);
SDL_AssertJoysticksLocked();
/* See if this device uses the new unified event API */
if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) >= 0) &&
(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) >= 0) &&
@ -1132,6 +1138,8 @@ static void ConfigJoystick(SDL_Joystick *joystick, int fd)
on error. Returns -1 on error, 0 on success. */
static int PrepareJoystickHwdata(SDL_Joystick *joystick, SDL_joylist_item *item)
{
SDL_AssertJoysticksLocked();
joystick->hwdata->item = item;
joystick->hwdata->guid = item->guid;
joystick->hwdata->effect.id = -1;
@ -1180,6 +1188,8 @@ static int LINUX_JoystickOpen(SDL_Joystick *joystick, int device_index)
{
SDL_joylist_item *item = JoystickByDevIndex(device_index);
SDL_AssertJoysticksLocked();
if (item == NULL) {
return SDL_SetError("No such device");
}
@ -1210,6 +1220,8 @@ static int LINUX_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rum
{
struct input_event event;
SDL_AssertJoysticksLocked();
if (joystick->hwdata->ff_rumble) {
struct ff_effect *effect = &joystick->hwdata->effect;
@ -1256,6 +1268,8 @@ static Uint32 LINUX_JoystickGetCapabilities(SDL_Joystick *joystick)
{
Uint32 result = 0;
SDL_AssertJoysticksLocked();
if (joystick->hwdata->ff_rumble || joystick->hwdata->ff_sine) {
result |= SDL_JOYCAP_RUMBLE;
}
@ -1280,7 +1294,7 @@ static int LINUX_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enab
static void HandleHat(SDL_Joystick *stick, int hatidx, int axis, int value)
{
const int hatnum = stick->hwdata->hats_indices[hatidx];
int hatnum;
struct hwdata_hat *the_hat;
struct hat_axis_correct *correct;
const Uint8 position_map[3][3] = {
@ -1289,6 +1303,9 @@ static void HandleHat(SDL_Joystick *stick, int hatidx, int axis, int value)
{ SDL_HAT_LEFTDOWN, SDL_HAT_DOWN, SDL_HAT_RIGHTDOWN }
};
SDL_AssertJoysticksLocked();
hatnum = stick->hwdata->hats_indices[hatidx];
the_hat = &stick->hwdata->hats[hatnum];
correct = &stick->hwdata->hat_correct[hatidx];
/* Hopefully we detected any analog axes and left them as is rather than trying
@ -1326,6 +1343,8 @@ static void HandleHat(SDL_Joystick *stick, int hatidx, int axis, int value)
static void HandleBall(SDL_Joystick *stick, Uint8 ball, int axis, int value)
{
SDL_AssertJoysticksLocked();
stick->hwdata->balls[ball].axis[axis] += value;
}
@ -1333,6 +1352,8 @@ static int AxisCorrect(SDL_Joystick *joystick, int which, int value)
{
struct axis_correct *correct;
SDL_AssertJoysticksLocked();
correct = &joystick->hwdata->abs_correct[which];
if (correct->minimum != correct->maximum) {
if (correct->use_deadzones) {
@ -1368,6 +1389,8 @@ static void PollAllValues(SDL_Joystick *joystick)
unsigned long keyinfo[NBITS(KEY_MAX)];
int i;
SDL_AssertJoysticksLocked();
/* Poll all axis */
for (i = ABS_X; i < ABS_MAX; i++) {
/* We don't need to test for digital hats here, they won't have has_abs[] set */
@ -1424,6 +1447,8 @@ static void HandleInputEvents(SDL_Joystick *joystick)
struct input_event events[32];
int i, len, code, hat_index;
SDL_AssertJoysticksLocked();
if (joystick->hwdata->fresh) {
PollAllValues(joystick);
joystick->hwdata->fresh = SDL_FALSE;
@ -1515,6 +1540,8 @@ static void HandleClassicEvents(SDL_Joystick *joystick)
struct js_event events[32];
int i, len, code, hat_index;
SDL_AssertJoysticksLocked();
joystick->hwdata->fresh = SDL_FALSE;
while ((len = read(joystick->hwdata->fd, events, (sizeof events))) > 0) {
len /= sizeof(events[0]);
@ -1557,6 +1584,8 @@ static void LINUX_JoystickUpdate(SDL_Joystick *joystick)
{
int i;
SDL_AssertJoysticksLocked();
if (joystick->hwdata->m_bSteamController) {
SDL_UpdateSteamController(joystick);
return;
@ -1585,6 +1614,8 @@ static void LINUX_JoystickUpdate(SDL_Joystick *joystick)
/* Function to close a joystick after use */
static void LINUX_JoystickClose(SDL_Joystick *joystick)
{
SDL_AssertJoysticksLocked();
if (joystick->hwdata) {
if (joystick->hwdata->effect.id >= 0) {
ioctl(joystick->hwdata->fd, EVIOCRMFF, joystick->hwdata->effect.id);
@ -1645,6 +1676,8 @@ static SDL_bool LINUX_JoystickGetGamepadMapping(int device_index, SDL_GamepadMap
SDL_joylist_item *item = JoystickByDevIndex(device_index);
unsigned int mapped;
SDL_AssertJoysticksLocked();
if (item->checked_mapping) {
if (item->mapping) {
SDL_memcpy(out, item->mapping, sizeof(*out));