/*
 *  ZThreads, a platform-independant, multithreading and 
 *  synchroniation library
 *
 *  Copyright (C) 2001, 2002 Eric Crahen, See LGPL.TXT for details
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 *  SUNY @ Buffalo, hereby disclaims all copyright interest in the
 *  ZThreads library written by Eric Crahen
 */

#include "zthread/POSIX_FastRecursiveMutex.h"
#include <assert.h>
#include <errno.h>

using namespace ZThread;

#ifndef __linux__

FastRecursiveMutex::FastRecursiveMutex()
  throw(Synchronization_Exception)  : _owner(0), _count(0) {

  ZTCHECK(pthread_mutex_init(&_mtx, 0));
  ZTCHECK(pthread_mutex_init(&_lock, 0));

}

FastRecursiveMutex::~FastRecursiveMutex() throw() {

  ZTCHECK(pthread_mutex_destroy(&_mtx));
  ZTCHECK(pthread_mutex_destroy(&_lock));
  
}

// Acquire using a 2 mutex scheme. Not all systems support a native recusrive mutex
void FastRecursiveMutex::acquire() 
  throw(Synchronization_Exception) {

  pthread_t self = pthread_self();
  bool wasLocked = false;

  // Try to lock the blocking mutex first
  if(pthread_mutex_trylock(&_mtx) == 0) 
    wasLocked = true;
  
  else {
    
    // Check to see it it is already owned by this thread
    ZTCHECK(pthread_mutex_lock(&_lock));

    // Update the count
    if(pthread_equal(_owner, self)) {
      _count++; 
      wasLocked = true;

    }

    ZTCHECK(pthread_mutex_unlock(&_lock));

    // Wait for the lock
    if(!wasLocked)
      ZTCHECK(pthread_mutex_lock(&_mtx));
    
  }


  ZTCHECK(pthread_mutex_lock(&_lock));

  // Take ownership
  _owner = self;
  _count++;
  
  ZTCHECK(pthread_mutex_unlock(&_lock));

}


bool FastRecursiveMutex::tryAcquire(unsigned long timeout)
  throw(Synchronization_Exception) {

  pthread_t self = pthread_self();
  bool wasLocked = false;

  // Try to lock the blocking mutex first
  if(pthread_mutex_trylock(&_mtx) == 0) 
    wasLocked = true;
  
  else {
    
    // Check to see it it is already owned by this thread
    ZTCHECK(pthread_mutex_lock(&_lock));

    // Update the count
    if(pthread_equal(_owner, self)) {
      _count++; 
      wasLocked = true;

    }

    ZTCHECK(pthread_mutex_unlock(&_lock));

    // Return w/o waiting
    if(!wasLocked)
      return false;
    
  }

  ZTCHECK(pthread_mutex_lock(&_lock));

  // Take ownership
  _owner = self;
  _count++;
  
  ZTCHECK(pthread_mutex_unlock(&_lock));

  return true;

}

void FastRecursiveMutex::release() 
  throw(Synchronization_Exception) {
    
  pthread_t self = pthread_self();

  ZTCHECK(pthread_mutex_lock(&_lock));

  // If the lock was owned and the count has reached 0, giveup ownership
  // and release the blocking lock
  if(pthread_equal(_owner, self) && --_count == 0) {
    
    _owner = 0;
    ZTCHECK(pthread_mutex_unlock(&_mtx));

  }

  ZTCHECK(pthread_mutex_unlock(&_lock));


}

#else

const pthread_mutexattr_t  FastRecursiveMutex::_attr = { PTHREAD_MUTEX_RECURSIVE_NP };


FastRecursiveMutex::FastRecursiveMutex()
  throw(Synchronization_Exception) {

  ZTCHECK(pthread_mutex_init(&_mtx, &_attr));

}

FastRecursiveMutex::~FastRecursiveMutex() throw() {

  ZTCHECK(pthread_mutex_destroy(&_mtx));
  
}


void FastRecursiveMutex::acquire() 
  throw(Synchronization_Exception) {
  
  ZTCHECK(pthread_mutex_lock(&_mtx));
  
}


bool FastRecursiveMutex::tryAcquire(unsigned long timeout)
  throw(Synchronization_Exception) {

  return pthread_mutex_trylock(&_mtx) == 0;

}

void FastRecursiveMutex::release() 
  throw(Synchronization_Exception) {
 
  ZTCHECK(pthread_mutex_unlock(&_mtx));

}

#endif
