//
// Copyright (C) 2000, 2001 Andrey Slepuhin <pooh@msu.ru>
//
// libp++ is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// libp++ is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with libp++; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// $Source$
// $Revision$
// $Date$
// Author: Andrey Slepuhin <pooh@msu.ru>

#ifndef __pxx_spinlock_hh__
#define __pxx_spinlock_hh__

#include <sched.h>

namespace pxx
{

#if defined(__i386__)

typedef volatile unsigned spinlock_t ;

static inline
spinlock_t xchg (spinlock_t* ptr, spinlock_t x) {
  __asm__ (
    "xchg %b0, %1"
    : "=q" (x)
    : "m" (*ptr), "0" (x)
    : "memory"
  );
  return x;
}

class Spinlock
{

protected:

  mutable spinlock_t internal_lock ;

public:

  inline Spinlock () :
    internal_lock (0)
  {}

  inline ~Spinlock ()
  {}

  inline void lock () const
  {
#ifndef DEBUG_SPINLOCK
    while (xchg(&internal_lock, 1) != 0);
#else // DEBUG_SPINLOCK
    unsigned long n = 100000000;
    while (n--) {
      if (xchg(&internal_lock, 1) == 0) return;
    }
    *(int*)0x1234 = 1;
#endif // DEBUG_SPINLOCK
  }

  inline void unlock () const
  {
    xchg(&internal_lock, 0);
  }

  inline bool trylock () const
  {
    return (xchg(&internal_lock, 1) == 0);
  }


};

#elif defined(__alpha__)
typedef volatile unsigned spinlock_t ;

class Spinlock
{

protected:

  mutable spinlock_t internal_lock ;

public:

  inline Spinlock () :
    internal_lock (0)
  {}

  inline ~Spinlock ()
  {}

  inline void lock () const
  {
    unsigned int tmp;
    asm volatile
      ("1:	ldl_l	%0,%1\n"
       "		blbs	%0,2f\n"
       "		or	%0,1,%0\n"
       "		stl_c	%0,%1\n"
       "		beq	%0,2f\n"
       "		mb\n"
       ".subsection 2\n"
       "2:	ldl	%0,%1\n"
       "		blbs	%0,2b\n"
       "		br	1b\n"
       ".previous"
       : "=r" (tmp), "=m" (internal_lock)
       : "m" (internal_lock));
  }

  inline void unlock () const
  {
    asm volatile ("mb");
    internal_lock = 0;
  }

  inline bool trylock () const
  {
    unsigned long int oldval;
    unsigned long int temp;

    asm volatile
      ("1:	ldl_l	%0,%1\n"
       "		and	%0,%3,%2\n"
       "		bne	%2,2f\n"
       "		xor	%0,%3,%0\n"
       "		stl_c	%0,%1\n"
       "		beq	%0,3f\n"
       "		mb\n"
       "2:\n"
       ".subsection 2\n"
       "3:	br	1b\n"
       ".previous"
       : "=&r" (temp), "=m" (internal_lock), "=&r" (oldval)
       : "Ir" (1UL), "m" (internal_lock));

    return oldval == 0;
  }


};

#endif

}

#endif // __pxx_spinlock_hh__
