Fixed bug 1128
authorSam Lantinga <slouken@libsdl.org>
Thu, 17 Feb 2011 09:13:12 -0800
changeset 53352bb1233924f1
parent 5333 5d3a2cd4e8c9
child 5336 66d3c4a6d6f8
Fixed bug 1128

Patrick Baggett 2011-02-16 22:58:33 PST

This enhancement is for both x86/x64 Windows.

The SDL implementation of mutexes uses the Win32 API interprocess
synchronization primitive called a "Mutex". This implementation is subpar
because it has a much higher overhead than an intraprocess mutex. The exact
technical details are below, but my tests have shown that for reasonably high
contention (10 threads on 4 physical cores), it has 13x higher overhead than
the Win32 CriticalSection API.

If this enhancement is accepted, I will write a patch to implement SDL mutexes
using the critical section API, which should dramatically reduce overhead and
improve scalability.


-- Tech details --
Normally, Win32 Mutexes are used across process boundaries to synchronize
separate processes. In order to lock or unlock them, a user->kernel space
transition is necessary, even in the uncontented case on a single CPU machine.
Win32 CriticalSection objects can only be used within the same process virtual
address space and thus to lock one, does not require a user->kernel space
transition for the uncontended case, and additionally may spin a short while
before going into kernel wait. This small spin allows a thread to obtain the
lock if the mutex is released shortly after the thread starts spinning, in
effect bypassing the overhead of user->kernel space transition which has higher
overhead than the spinning itself.
src/thread/windows/SDL_sysmutex.c
     1.1 --- a/src/thread/windows/SDL_sysmutex.c	Thu Feb 17 02:23:48 2011 -0800
     1.2 +++ b/src/thread/windows/SDL_sysmutex.c	Thu Feb 17 09:13:12 2011 -0800
     1.3 @@ -30,7 +30,7 @@
     1.4  
     1.5  struct SDL_mutex
     1.6  {
     1.7 -    HANDLE id;
     1.8 +    CRITICAL_SECTION cs;
     1.9  };
    1.10  
    1.11  /* Create a mutex */
    1.12 @@ -38,17 +38,29 @@
    1.13  SDL_CreateMutex(void)
    1.14  {
    1.15      SDL_mutex *mutex;
    1.16 +    static DWORD (WINAPI*pf_SetCriticalSectionSpinCount)(LPCRITICAL_SECTION, DWORD) = NULL;
    1.17 +    static HMODULE kernel32 = NULL;
    1.18 +
    1.19 +    /* One time logic - detect WinNT */
    1.20 +    if(kernel32 == NULL) {
    1.21 +        kernel32 = GetModuleHandleA("kernel32.dll");
    1.22 +		if(kernel32) {
    1.23 +            /* Attempt to resolve symbol -- Win9x gets NULL */
    1.24 +            pf_SetCriticalSectionSpinCount = (DWORD (WINAPI*)(LPCRITICAL_SECTION, DWORD))GetProcAddress(kernel32, "SetCriticalSectionSpinCount");
    1.25 +        }
    1.26 +		else
    1.27 +			kernel32 = (HMODULE)0x01; /* don't try to init again */
    1.28 +	}
    1.29 +
    1.30  
    1.31      /* Allocate mutex memory */
    1.32      mutex = (SDL_mutex *) SDL_malloc(sizeof(*mutex));
    1.33      if (mutex) {
    1.34 -        /* Create the mutex, with initial value signaled */
    1.35 -        mutex->id = CreateMutex(NULL, FALSE, NULL);
    1.36 -        if (!mutex->id) {
    1.37 -            SDL_SetError("Couldn't create mutex");
    1.38 -            SDL_free(mutex);
    1.39 -            mutex = NULL;
    1.40 -        }
    1.41 +        /* Initialize */
    1.42 +        InitializeCriticalSection(&mutex->cs);
    1.43 +
    1.44 +        /* On SMP systems, a non-zero spin count generally helps performance */
    1.45 +        if(pf_SetCriticalSectionSpinCount) pf_SetCriticalSectionSpinCount(&mutex->cs, 2000);
    1.46      } else {
    1.47          SDL_OutOfMemory();
    1.48      }
    1.49 @@ -60,10 +72,7 @@
    1.50  SDL_DestroyMutex(SDL_mutex * mutex)
    1.51  {
    1.52      if (mutex) {
    1.53 -        if (mutex->id) {
    1.54 -            CloseHandle(mutex->id);
    1.55 -            mutex->id = 0;
    1.56 -        }
    1.57 +        DeleteCriticalSection(&mutex->cs);
    1.58          SDL_free(mutex);
    1.59      }
    1.60  }
    1.61 @@ -76,10 +85,8 @@
    1.62          SDL_SetError("Passed a NULL mutex");
    1.63          return -1;
    1.64      }
    1.65 -    if (WaitForSingleObject(mutex->id, INFINITE) == WAIT_FAILED) {
    1.66 -        SDL_SetError("Couldn't wait on mutex");
    1.67 -        return -1;
    1.68 -    }
    1.69 +
    1.70 +    EnterCriticalSection(&mutex->cs);
    1.71      return (0);
    1.72  }
    1.73  
    1.74 @@ -91,10 +98,8 @@
    1.75          SDL_SetError("Passed a NULL mutex");
    1.76          return -1;
    1.77      }
    1.78 -    if (ReleaseMutex(mutex->id) == FALSE) {
    1.79 -        SDL_SetError("Couldn't release mutex");
    1.80 -        return -1;
    1.81 -    }
    1.82 +
    1.83 +    LeaveCriticalSection(&mutex->cs);
    1.84      return (0);
    1.85  }
    1.86