/* Portable Spinlock Code.
** This code is in the public domain.
** Author:  Walt Karas
**
** WARNING:  This code has not been tested in any way.
*/

#include "spinlock.h"

void spinlock_init(spinlock *sl)
  {
    register spinlock_proc_num i;

    sl->has_lock = SPINLOCK_NO_PROC;

    for (i = 0; i < SPINLOCK_NUM_PROCS; i++)
      sl->flag[i] = 0;
  }

/* NOTE:  For this function to work properly, the actual order of reads
** and writes to the spinlock in memory has to be the same as the
** nominal order of reads and writes to the spinlock in the code.
** Making the spinlock volatile should guarantee this, but one should
** not be so trusting.  On a MIPS implementation I have some experience
** with, a sync instruction is needed between load/store instructions
** to make sure that actual memory operation occurs in the order of
** load/store execution.  But the gcc port that was available did not
** seem to generate "sync" instructions as a result of declaring
** something to be volatile.  To use this code for that target,
** it would be necessary to intersperce "asm" directives at the
** appropriate places in the code to insert the "sync" instructions.
*/
int spinlock_try(volatile spinlock *sl, spinlock_proc_num p_num)
  {
    register spinlock_proc_num i;
    int result = 0;

    if (sl->has_lock == SPINLOCK_NO_PROC)
      {
	/* The spinlock is available, so this processor tells the other
	** processors it's going after the spinlock.
	*/
	sl->flag[p_num] = 1;

	if (sl->has_lock == SPINLOCK_NO_PROC)
	  {
	    /* At this point, the processors that are still in contention
	    ** for the spinlock are the ones that managed to read
	    ** "has_lock" before it was written by any processor.  The
	    ** last processor to execute this next assignment statement
	    ** will get the spinlock.
	    */
	    sl->has_lock = p_num;

	    i = 0;
	    for ( ; ; )
	      {
		if (!(sl->flag[i]) || (i == p_num))
		  {
		    i++;
		    if (i == SPINLOCK_NUM_PROCS)
		      {
			/* The flags for all the other processors have
			** been lowered, and "has_lock" still contains the
			** number of this processor, so this processor
			** got the spinlock.
			*/
			result = 1;
			break;
		      }
		  }
		else if (sl->has_lock != p_num)
		  /* Another processor wrote "has_lock" after this one,
		  ** so this processor did not get the spinlock.
		  */
		  break;
	      }
	  }

	sl->flag[p_num] = 0;
      }

    return(result);
  }

void spinlock_wait(volatile spinlock *sl, spinlock_proc_num p_num)
  {
    while (!spinlock_try(sl, p_num))
      ;
  }

void spinlock_release(volatile spinlock *sl, spinlock_proc_num p_num)
  {
    /* Only release if this processor has the spinlock. */
    if (sl->has_lock == p_num)
      sl->has_lock = SPINLOCK_NO_PROC;
  }

spinlock_proc_num spinlock_who(volatile spinlock *sl)
  {
    return(sl->has_lock);
  }

    Source: geocities.com/wkaras