Some spinlock code, just for you

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Some spinlock code, just for you

Post by sje »

Spinlocks

I've been doing some experimentation with the use of spinlocks for the purpose of controlling threaded access to a transposition table used for movepath enumeration (i.e., perft). Unfortunately, there is no one simple and universal way to code spinlocks. In my work, I have written and tested three different implementations and I'm sharing the source. None of these implementations are assembly language as I'd like to avoid that when possible.

The first implementation is the use of Apple Mac OS/X native spinlocks. These are fast, but are Apple-specific.

The second implementation is the use of pthreads spinlocks. These are an optional addition to the Posix pthreads package and are not available in Apple's current pthreads implementation. They are available in modern Linux distributions; but for some reason the Ubuntu man pages are not up-to-date and so no mention of pthreads spinlocks can be found there.

The third implementation is to use pthreads mutex objects. These are likely available in every pthreads implementation but are no faster and probably slower than pthreads spinlocks.

A spinlock C++ class declaration:

Code: Select all

class SpinLock
{
  public:
    SpinLock(void);
    ~SpinLock(void);

    void Lock(void);
    void Unlock(void);

  private:
    volatile void *lockptr;  // Pointer to the allocated lock in use
};
And the implementation:

Code: Select all

#include <pthread.h>
#include <cassert>
#include <sstream>

#include "Definitions.h"
#include "Enumerations.h"
#include "SystemUtilities.h"
#include "SpinLock.h"

#if &#40;HostOsApple&#41;
#include <libkern/OSAtomic.h>
#endif

SpinLock&#58;&#58;SpinLock&#40;void&#41;
&#123;
#if &#40;HostOsApple&#41;
  lockptr = &#40;void *) new OSSpinLock;
  *&#40;OSSpinLock *) lockptr = OS_SPINLOCK_INIT;
#endif

#if &#40;HostOsLinux&#41;
  lockptr = new pthread_spinlock_t;
  if &#40;pthread_spin_init&#40;&#40;pthread_spinlock_t *) lockptr, PTHREAD_PROCESS_PRIVATE&#41;)
    Die&#40;"SpinLock&#58;&#58;SpinLock/pthread_spin_init");
#endif

#if &#40;HostOsUnknown&#41;
  lockptr = new pthread_mutex_t;
  pthread_mutexattr_t *mutexattrptr = new pthread_mutexattr_t;

  pthread_mutexattr_init&#40;mutexattrptr&#41;;
  pthread_mutexattr_settype&#40;mutexattrptr, PTHREAD_MUTEX_ERRORCHECK&#41;;
  if &#40;pthread_mutex_init&#40;&#40;pthread_mutex_t *) lockptr, mutexattrptr&#41;)
    Die&#40;"SpinLock&#58;&#58;SpinLock/pthread_mutex_init");
  pthread_mutexattr_destroy&#40;mutexattrptr&#41;;
  delete mutexattrptr;
#endif
&#125;

SpinLock&#58;&#58;~SpinLock&#40;void&#41;
&#123;
#if &#40;HostOsApple&#41;
  delete &#40;OSSpinLock *) lockptr;
#endif

#if &#40;HostOsLinux&#41;
  if &#40;pthread_spin_destroy&#40;&#40;pthread_spinlock_t *) lockptr&#41;)
    Die&#40;"SpinLock&#58;&#58;~SpinLock/pthread_spin_destroy");
  delete &#40;pthread_spinlock_t *) lockptr;
#endif

#if &#40;HostOsUnknown&#41;
  if &#40;pthread_mutex_destroy&#40;&#40;pthread_mutex_t *) lockptr&#41;)
    Die&#40;"SpinLock&#58;&#58;~SpinLock/pthread_mutex_destroy");
  delete &#40;pthread_mutex_t *) lockptr;
#endif
&#125;

void SpinLock&#58;&#58;Lock&#40;void&#41;
&#123;
#if &#40;HostOsApple&#41;
  OSSpinLockLock&#40;&#40;OSSpinLock *) lockptr&#41;;
#endif

#if &#40;HostOsLinux&#41;
  if &#40;pthread_spin_lock&#40;&#40;pthread_spinlock_t *) lockptr&#41;)
    Die&#40;"SpinLock&#58;&#58;Lock/pthread_spin_lock");
#endif

#if &#40;HostOsUnknown&#41;
  if &#40;pthread_mutex_lock&#40;&#40;pthread_mutex_t *) lockptr&#41;)
    Die&#40;"SpinLock&#58;&#58;Lock/pthread_mutex_lock");
#endif
&#125;

void SpinLock&#58;&#58;Unlock&#40;void&#41;
&#123;
#if &#40;HostOsApple&#41;
  OSSpinLockUnlock&#40;&#40;OSSpinLock *) lockptr&#41;;
#endif

#if &#40;HostOsLinux&#41;
  if &#40;pthread_spin_unlock&#40;&#40;pthread_spinlock_t *) lockptr&#41;)
    Die&#40;"SpinLock&#58;&#58;Unlock/pthread_spin_unlock");
#endif

#if &#40;HostOsUnknown&#41;
  if &#40;pthread_mutex_unlock&#40;&#40;pthread_mutex_t *) lockptr&#41;)
    Die&#40;"SpinLock&#58;&#58;Unlock/pthread_mutex_unlock");
#endif
&#125;
Dann Corbit
Posts: 12542
Joined: Wed Mar 08, 2006 8:57 pm
Location: Redmond, WA USA

Re: Some spinlock code, just for you

Post by Dann Corbit »

sje wrote:Spinlocks

I've been doing some experimentation with the use of spinlocks for the purpose of controlling threaded access to a transposition table used for movepath enumeration (i.e., perft). Unfortunately, there is no one simple and universal way to code spinlocks. In my work, I have written and tested three different implementations and I'm sharing the source. None of these implementations are assembly language as I'd like to avoid that when possible.

The first implementation is the use of Apple Mac OS/X native spinlocks. These are fast, but are Apple-specific.

The second implementation is the use of pthreads spinlocks. These are an optional addition to the Posix pthreads package and are not available in Apple's current pthreads implementation. They are available in modern Linux distributions; but for some reason the Ubuntu man pages are not up-to-date and so no mention of pthreads spinlocks can be found there.

The third implementation is to use pthreads mutex objects. These are likely available in every pthreads implementation but are no faster and probably slower than pthreads spinlocks.

A spinlock C++ class declaration:

Code: Select all

class SpinLock
&#123;
  public&#58;
    SpinLock&#40;void&#41;;
    ~SpinLock&#40;void&#41;;

    void Lock&#40;void&#41;;
    void Unlock&#40;void&#41;;

  private&#58;
    volatile void *lockptr;  // Pointer to the allocated lock in use
&#125;;
And the implementation:

Code: Select all

#include <pthread.h>
#include <cassert>
#include <sstream>

#include "Definitions.h"
#include "Enumerations.h"
#include "SystemUtilities.h"
#include "SpinLock.h"

#if &#40;HostOsApple&#41;
#include <libkern/OSAtomic.h>
#endif

SpinLock&#58;&#58;SpinLock&#40;void&#41;
&#123;
#if &#40;HostOsApple&#41;
  lockptr = &#40;void *) new OSSpinLock;
  *&#40;OSSpinLock *) lockptr = OS_SPINLOCK_INIT;
#endif

#if &#40;HostOsLinux&#41;
  lockptr = new pthread_spinlock_t;
  if &#40;pthread_spin_init&#40;&#40;pthread_spinlock_t *) lockptr, PTHREAD_PROCESS_PRIVATE&#41;)
    Die&#40;"SpinLock&#58;&#58;SpinLock/pthread_spin_init");
#endif

#if &#40;HostOsUnknown&#41;
  lockptr = new pthread_mutex_t;
  pthread_mutexattr_t *mutexattrptr = new pthread_mutexattr_t;

  pthread_mutexattr_init&#40;mutexattrptr&#41;;
  pthread_mutexattr_settype&#40;mutexattrptr, PTHREAD_MUTEX_ERRORCHECK&#41;;
  if &#40;pthread_mutex_init&#40;&#40;pthread_mutex_t *) lockptr, mutexattrptr&#41;)
    Die&#40;"SpinLock&#58;&#58;SpinLock/pthread_mutex_init");
  pthread_mutexattr_destroy&#40;mutexattrptr&#41;;
  delete mutexattrptr;
#endif
&#125;

SpinLock&#58;&#58;~SpinLock&#40;void&#41;
&#123;
#if &#40;HostOsApple&#41;
  delete &#40;OSSpinLock *) lockptr;
#endif

#if &#40;HostOsLinux&#41;
  if &#40;pthread_spin_destroy&#40;&#40;pthread_spinlock_t *) lockptr&#41;)
    Die&#40;"SpinLock&#58;&#58;~SpinLock/pthread_spin_destroy");
  delete &#40;pthread_spinlock_t *) lockptr;
#endif

#if &#40;HostOsUnknown&#41;
  if &#40;pthread_mutex_destroy&#40;&#40;pthread_mutex_t *) lockptr&#41;)
    Die&#40;"SpinLock&#58;&#58;~SpinLock/pthread_mutex_destroy");
  delete &#40;pthread_mutex_t *) lockptr;
#endif
&#125;

void SpinLock&#58;&#58;Lock&#40;void&#41;
&#123;
#if &#40;HostOsApple&#41;
  OSSpinLockLock&#40;&#40;OSSpinLock *) lockptr&#41;;
#endif

#if &#40;HostOsLinux&#41;
  if &#40;pthread_spin_lock&#40;&#40;pthread_spinlock_t *) lockptr&#41;)
    Die&#40;"SpinLock&#58;&#58;Lock/pthread_spin_lock");
#endif

#if &#40;HostOsUnknown&#41;
  if &#40;pthread_mutex_lock&#40;&#40;pthread_mutex_t *) lockptr&#41;)
    Die&#40;"SpinLock&#58;&#58;Lock/pthread_mutex_lock");
#endif
&#125;

void SpinLock&#58;&#58;Unlock&#40;void&#41;
&#123;
#if &#40;HostOsApple&#41;
  OSSpinLockUnlock&#40;&#40;OSSpinLock *) lockptr&#41;;
#endif

#if &#40;HostOsLinux&#41;
  if &#40;pthread_spin_unlock&#40;&#40;pthread_spinlock_t *) lockptr&#41;)
    Die&#40;"SpinLock&#58;&#58;Unlock/pthread_spin_unlock");
#endif

#if &#40;HostOsUnknown&#41;
  if &#40;pthread_mutex_unlock&#40;&#40;pthread_mutex_t *) lockptr&#41;)
    Die&#40;"SpinLock&#58;&#58;Unlock/pthread_mutex_unlock");
#endif
&#125;
There is an open source project called ACE that does a really nice job of abstracting operating system specific operations.
http://www1.cse.wustl.edu/~schmidt/ACE.html