static char rcsid[] = "$Id: ompc_lock.c,v 1.5 2000/07/31 14:52:32 y-tanaka Exp $";
/* 
 * $RWC_Release: Omni-1.6 $
 * $RWC_Copyright:
 *  Omni Compiler Software Version 1.5-1.6
 *  Copyright (C) 2002 PC Cluster Consortium
 *  
 *  This software is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License version
 *  2.1 published by the Free Software Foundation.
 *  
 *  Omni Compiler Software Version 1.0-1.4
 *  Copyright (C) 1999, 2000, 2001.
 *   Tsukuba Research Center, Real World Computing Partnership, Japan.
 *  
 *  Please check the Copyright and License information in the files named
 *  COPYRIGHT and LICENSE under the top  directory of the Omni Compiler
 *  Software release kit.
 *  
 *  
 *  $
 */

#include "ompclib.h"

extern struct ompc_thread *_ompc_current_thread(void);

/* Lock/Unlock */
#ifndef USE_SPIN_LOCK

# ifdef USE_SOL_THREAD
void _ompc_init_lock(_ompc_lock_t *lp)
{
    ST_POLLING();
    mutex_init(lp, NULL, NULL);
}

void _ompc_lock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    mutex_lock((mutex_t *)lp);
}

void _ompc_unlock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    mutex_unlock((mutex_t *)lp);
}

void _ompc_destroy_lock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    mutex_destroy((mutex_t *)lp);
}

int _ompc_test_lock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    return (mutex_trylock((mutex_t *)lp) == 0); 
}

# endif /* USE_SOL_THREAD */

# ifdef USE_PTHREAD
void _ompc_init_lock(_ompc_lock_t *lp)
{
    ST_POLLING();
    pthread_mutex_init((pthread_mutex_t *)lp, NULL);
}

void _ompc_lock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    pthread_mutex_lock((pthread_mutex_t *)lp);
}

void _ompc_unlock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    pthread_mutex_unlock((pthread_mutex_t *)lp);
}

void _ompc_destroy_lock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    pthread_mutex_destroy((pthread_mutex_t *)lp);
}

int _ompc_test_lock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    return (pthread_mutex_trylock((pthread_mutex_t *)lp) == 0);
}

# endif /* USE_PTHREAD */

#else /* !USE_SPIN_LOCK */

void _ompc_init_lock(_ompc_lock_t *lp)
{
    ST_POLLING();
    *lp = 0;
# ifdef OMNI_CPU_MIPS
    asm volatile("sync" : : );
# endif /* OMNI_CPU_MIPS */
}

void _ompc_destroy_lock(volatile _ompc_lock_t *lp){
    ST_POLLING();
    /* do nothing */
}

# ifdef OMNI_CPU_SPARC
void LockWithLdstUB(volatile int *);
void UnlockWithLdstUB(volatile int *);
int  TestLockWithLdstUB(volatile int *);

void _ompc_lock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    LockWithLdstUB(lp);
}

void _ompc_unlock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    UnlockWithLdstUB(lp);
}

int _ompc_test_lock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    return !TestLockWithLdstUB(lp);
}

void
__dummy()
{
    asm(".align  8");
    asm(".skip   16");
    asm(".type   LockWithLdstUB,#function");
    asm(".global LockWithLdstUB");
    asm("LockWithLdstUB:");
    asm("retry:  ldstub  [%o0],%o1       ! atomic load store");
    asm("tst     %o1");
    asm("be      out");
    asm("nop");
    asm("loop:   ldub    [%o0],%o1       ! load and test");
    asm("tst     %o1");
    asm("bne     loop");
    asm("nop");
    asm("ba,a    retry");
    asm("out:    nop");
    asm("jmp     %o7+8   ! return");
    asm("nop");

    asm(".type   TestLockWithLdstUB,#function");
    asm(".global TestLockWithLdstUB");
    asm("TestLockWithLdstUB:");
    asm("ldstub  [%o0],%o0       ! atomic load store");
    asm("jmp     %o7+8   ! return");
    asm("nop");

    asm(".type   UnlockWithLdstUB,#function");
    asm(".global UnlockWithLdstUB");
    asm("UnlockWithLdstUB:");
    asm("stbar");
    asm("stb     %g0,[%o0]       ! clear lock");
    asm("jmp     %o7+8           ! return");
    asm("nop");
}
# endif /* OMNI_CPU_SPARC */

# ifdef OMNI_CPU_I386
int _xchg_1(volatile int *p)
{
  asm ("	movl 8(%ebp),%edx		");
  asm ("	movl $1,%eax			");
  asm ("	xchgl 0(%edx),%eax		");
}

void _ompc_lock(volatile _ompc_lock_t *lp)
{
 again:
    while(*lp != 0) ST_POLLING();/* spin wait */
    if(_xchg_1(lp) != 0) goto again;
}

void _ompc_unlock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    *lp = 0;
}

int _ompc_test_lock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    if(_xchg_1(lp) != 0) return 0;
    else return 1;
}

# endif /* OMNI_CPU_I386 */

# ifdef OMNI_CPU_MIPS
/* call SGI library */
static int PSM_compare_and_swap_c(long volatile *p, long oldn, long newn)
{
    st_dont_postprocess();
    /* 1st argument is $4 */
    /* 2nd argument is $5 */
    /* 3rd argument is $6 */
    /* Return value is $2 */
    asm volatile(
                 "ll    $2,($4)\n"
                 ".set  noreorder\n"
                 ".set  nomacro\n"
                 "bne   $2,$5,$LPSM_CAS_C1\n"
                 "move  $2,$0\n"
                 ".set  macro\n"
                 ".set  reorder\n"
                 "sc    $6,($4)\n"
                 "sltu  $2,$0,$6\n"
	 "$LPSM_CAS_C1:\n"
    );
}

static int PSM_mutex_trylock(PSM_mutex_t *mutex_p)
{
    long volatile *p;
    p = mutex_p;
    if (*p) {
        return 0;
    }
    return PSM_compare_and_swap_c(mutex_p, 0, 1);
}

void _ompc_lock(volatile _ompc_lock_t *lp)
{
    while (PSM_mutex_trylock((_ompc_lock_t *)lp) == 0)
        ST_POLLING();
}

void _ompc_unlock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    *lp = 0;
    asm volatile("sync" : : );
}

int _ompc_test_lock(volatile _ompc_lock_t *lp)
{
    ST_POLLING();
    return PSM_mutex_trylock((_ompc_lock_t *)lp);
}

# endif /* OMNI_CPU_MIPS */

#endif /* !USE_SPIN_LOCK */

void _ompc_init_nest_lock (_ompc_nest_lock_t *lp)
{
  _ompc_init_lock (&lp->lock);
#ifndef USE_SPIN_LOCK
  _ompc_init_lock (&lp->wait);
#endif
  lp->count = 0;
  ST_POLLING();
}

void _ompc_nest_lock (volatile _ompc_nest_lock_t *lp)
{
  struct ompc_thread *tp = _ompc_current_thread();
  int	id = tp->ser_num;

#ifndef USE_SPIN_LOCK
  int		wl;

 retry:
  if (lp->count != 0  &&  lp->id != id) {
    _ompc_lock (&lp->wait);
    if (_ompc_test_lock (&lp->lock) == 0) {
      _ompc_unlock (&lp->wait);
      goto retry;
    }
    wl = 1;
  } else {
    _ompc_lock (&lp->lock);
    wl = 0;
  }
  if (lp->count != 0) {
    if (id == lp->id) {		      /* already lock by own thread */
      lp->count ++;
    } else {			      /* already lock by othre thread, fail */
      _ompc_unlock (&lp->lock);
      if (wl == 1) {
	_ompc_unlock (&lp->wait);
      }
      goto retry;
    }
  } else {	   		      /* no thread lock, yet */
    if (wl == 0) {
      _ompc_lock (&lp->wait);
    }
    lp->id    = id;
    lp->count = 1;
  }
  _ompc_unlock (&lp->lock);
#else 
 retry:
  _ompc_lock (&lp->lock);
  if (lp->count != 0) {
    if (id == lp->id) {		      /* already lock by own thread */
      lp->count ++;
    } else {			      /* already lock by othre thread, fail */
      _ompc_unlock (&lp->lock);	      /* dirty implement */
      goto retry;
    }
  } else {	   		      /* no thread lock, yet */
    lp->id    = id;
    lp->count = 1;
  }
  _ompc_unlock (&lp->lock);
#endif
  ST_POLLING();
}

void _ompc_nest_unlock (volatile _ompc_nest_lock_t *lp)
{
  _ompc_lock (&lp->lock);
  lp->count -= 1;
#ifndef USE_SPIN_LOCK
  if (lp->count == 0) {
    _ompc_unlock (&lp->lock);
    _ompc_unlock (&lp->wait);
  } else {
    _ompc_unlock (&lp->lock);
  }
#else
  _ompc_unlock (&lp->lock);
#endif
  ST_POLLING();
}

void _ompc_destroy_nest_lock (volatile _ompc_nest_lock_t *lp)
{
  _ompc_destroy_lock (&lp->lock);
#ifndef USE_SPIN_LOCK
  _ompc_destroy_lock (&lp->wait);
#endif
  ST_POLLING();
}

int _ompc_test_nest_lock (volatile _ompc_nest_lock_t *lp)
{
  struct ompc_thread *tp = _ompc_current_thread();
  int	id = tp->ser_num;


  if (lp->count != 0  &&  lp->id != id) {
    return 0;
  }
  if (_ompc_test_lock (&lp->lock) == 0) {
    return 0;
  }
  if (lp->count != 0) {
    if (id == lp->id) {		      /* already lock by own thread */
      lp->count ++;
    } else {			      /* already lock by othre thread, fail */
      _ompc_unlock (&lp->lock);
      return 0;
    }
  } else {	   		      /* no thread lock, yet */
#ifndef USE_SPIN_LOCK
    _ompc_lock (&lp->wait);
#endif
    lp->id    = id;
    lp->count = 1;
  }
  _ompc_unlock (&lp->lock);
  ST_POLLING();
  return lp->count;
}

