view hotspot/src/os/solaris/vm/mutex_solaris.cpp @ 2:16f2b6c91171 trunk

[svn] Load openjdk/jdk7/b14 into jdk/trunk.
author xiomara
date Fri, 22 Jun 2007 00:46:43 +0000
parents a4ed3fb96592
children 27e0bf49438e
line wrap: on
line source

#ifdef USE_PRAGMA_IDENT_SRC
#pragma ident "@(#)mutex_solaris.cpp	1.65 07/05/29 11:37:57 JVM"
#endif
/*
 * Copyright 1998-2007 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 *  
 */

# include "incls/_precompiled.incl"
# include "incls/_mutex_solaris.cpp.incl"

// Solaris-specific include, therefore not in includeDB_*
# include "os_share_solaris.hpp"

// put OS-includes here
# include <signal.h>

// Implementation of Mutex

// A simple Mutex for VM locking: it is not guaranteed to interoperate with
// the fast object locking, so exclusively use Mutex locking or exclusively
// use fast object locking.

Mutex::Mutex(int rank, const char *name, bool allow_vm_block)
  debug_only( : _rank(rank) )
{
  _lock_event     = new os::Solaris::Event;
  _suppress_signal = false;
  _owner          = INVALID_THREAD;
  _name           = name;

#ifdef ASSERT
  if (CountVMLocks) {
    _histogram         = new MutexHistogramElement(name);
    _contend_histogram = new MutexContentionHistogramElement(name);
  }
#endif

#ifndef PRODUCT
  _lock_count     = -1; // unused in solaris
  _allow_vm_block = allow_vm_block;
  debug_only(_next = NULL;)
  debug_only(_last_owner = INVALID_THREAD;)
#endif
}

Mutex::~Mutex() {  
  os::Solaris::Event* const _Lock_Event = (os::Solaris::Event*)_lock_event;

  assert(_owner == INVALID_THREAD, "Owned Mutex being deleted");
  assert(_lock_count == -1, "Mutex being deleted with non -1 lock count");
  delete _Lock_Event;
}


void Mutex::unlock() {
  os::Solaris::Event* const _Lock_Event = (os::Solaris::Event*)_lock_event;

  assert(_owner == Thread::current(), "Mutex not being unlocked by owner");

  set_owner(INVALID_THREAD);

  if (_suppress_signal) {
    assert(SafepointSynchronize::is_at_safepoint() &&
           Thread::current()->is_VM_thread(), "can't sneak");
    _suppress_signal = false;
  }
  else {
    assert(_lock_count >= 0, "Mutex being unlocked without positive lock count");
    debug_only(_lock_count--;)
    _Lock_Event->unlock();
  }
}


// Can be called by non-Java threads (JVM_RawMonitorExit)
void Mutex::jvm_raw_unlock() {
  os::Solaris::Event* const _Lock_Event = (os::Solaris::Event*)_lock_event;
  // Do not call set_owner, as this would break.
  _owner = INVALID_THREAD;
  if (_suppress_signal) {
    assert(SafepointSynchronize::is_at_safepoint() &&
           Thread::current()->is_VM_thread(), "can't sneak");
    _suppress_signal = false;
  }
  else {
    debug_only(_lock_count--;)
    _Lock_Event->unlock();
  }
}


void Mutex::wait_for_lock_blocking_implementation(JavaThread *thread) {
  ThreadBlockInVM tbivm(thread);

  wait_for_lock_implementation();
}


#ifndef PRODUCT
void Mutex::print_on(outputStream* st) const {
  os::Solaris::Event* const _Lock_Event = (os::Solaris::Event*)_lock_event;

  st->print_cr("Mutex: [0x%lx/0x%lx] %s - owner: 0x%lx", this, _Lock_Event, _name, _owner);
}
#endif


//
// Monitor
//


Monitor::Monitor(int rank, const char *name, bool allow_vm_block) : Mutex(rank, name, allow_vm_block) {
  _event   = NULL;		
  _counter = 0;
  _tickets = 0;
  _waiters = 0;
}


Monitor::~Monitor() {
}  


bool Monitor::wait(bool no_safepoint_check, long timeout,
                   bool as_suspend_equivalent) {
  os::Solaris::Event* const _Lock_Event = (os::Solaris::Event*)_lock_event;
  Thread* thread = Thread::current();

  assert(_owner != INVALID_THREAD, "Wait on unknown thread");
  assert(_owner == thread, "Wait on Monitor not by owner");

  // The design rule for use of mutexes of rank special or less is
  // that we are guaranteed not to block while holding such mutexes.
  // Here we verify that the least ranked mutex that we hold,
  // modulo the mutex we are about to relinquish, satisfies that
  // constraint, since we are about to block in a wait.
  #ifdef ASSERT
    Mutex* least = get_least_ranked_lock_besides_this(thread->owned_locks());
    assert(least != this, "Specification of get_least_... call above");
    if (least != NULL && least->rank() <= special) {
      tty->print("Attempting to wait on monitor %s/%d while holding"
                 " lock %s/%d -- possible deadlock",
                 name(), rank(), least->name(), least->rank());
      assert(false,
             "Shouldn't block(wait) while holding a lock of rank special");
    }
  #endif // ASSERT

  long c = _counter;

#ifdef ASSERT
  // Don't catch signals while blocked; let the running threads have the signals.
  // (This allows a debugger to break into the running thread.)
  sigset_t oldsigs;
  sigset_t* allowdebug_blocked = os::Solaris::allowdebug_blocked_signals();
  thr_sigsetmask(SIG_BLOCK, allowdebug_blocked, &oldsigs);
#endif

  _waiters++;
  // Loop until condition variable is signaled.  Tickets will
  // reflect the number of threads which have been notified. The counter
  // field is used to make sure we don't respond to notifications that
  // have occurred *before* we started waiting, and is incremented each
  // time the condition variable is signaled.
  // Use a ticket scheme to guard against spurious wakeups.
  int wait_status;

  while (true) {

    if (no_safepoint_check) {

      // conceptually set the owner to INVALID_THREAD in anticipation of yielding the lock in wait
      set_owner(Mutex::INVALID_THREAD);

      // (SafepointTimeout is not implemented)
      if(timeout == 0) {
	wait_status = _Lock_Event->wait();
      }
      else {
	wait_status = _Lock_Event->timedwait(timeout);
      }
    } else {
      JavaThread *jt = (JavaThread *)thread;
      // save thread state around the lock

      // conceptually set the owner to INVALID_THREAD in anticipation of yielding the lock in wait
      set_owner(Mutex::INVALID_THREAD);

      // Enter safepoint region
      ThreadBlockInVM tbivm(jt);
      OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);

      if (as_suspend_equivalent) {
        jt->set_suspend_equivalent();
        // cleared by handle_special_suspend_equivalent_condition() or
        // java_suspend_self()
      }

      if(timeout == 0) {
        wait_status = _Lock_Event->wait();
      }
      else {
        wait_status = _Lock_Event->timedwait(timeout);
      }

      // were we externally suspended while we were waiting?
      if (as_suspend_equivalent &&
          jt->handle_special_suspend_equivalent_condition()) {
        //
        // Our event wait has finished and we own the _Lock_Event, but
        // while we were waiting another thread suspended us. We don't
        // want to hold the _Lock_Event while suspended because that
        // would surprise the thread that suspended us.
        //
        _Lock_Event->unlock();
        jt->java_suspend_self();
        _Lock_Event->lock();
      }
    } // if no_safepoint_check

    // conceptually reaquire the lock (the actual Solaris lock is already reacquired after waiting)
    set_owner(thread);

    // We get to this point if either:
    // a) a notify has been executed by some other thread and woke us up
    // b) a signal has been delivered to this thread and terminated wait
    // c) the above two events happened while we were waiting - that is a signal
    //    was delivered while notify was executed by some other thread.

    // Handle cases a) and c) here. We consume one ticket even in case c) when notify
    // and a signal arrive together
    if (_tickets != 0 && _counter != c) {
      break;
    }
    
    // If wait was interrupted by a signal or timeout, do not use up a ticket
    if (wait_status == EINTR || wait_status == ETIME) {
      ++_tickets;		// will be decremented again below
      break;
    }


  }
  _waiters--;
  _tickets--;

#ifdef ASSERT
  thr_sigsetmask(SIG_SETMASK, &oldsigs, NULL);
#endif
   
  // return true if timed out
  return (wait_status == ETIME);
}


// Notify a single thread waiting on this condition variable
bool Monitor::notify() {
  os::Solaris::Event* const _Lock_Event = (os::Solaris::Event*)_lock_event;

  assert(_owner != INVALID_THREAD, "notify on unknown thread");
  assert(_owner == Thread::current(), "notify on Monitor not by owner");

  if (_waiters > _tickets) {
    
    _Lock_Event->signal();
    
    _tickets++;
    _counter++;
  }

  return true;

}


// Notify all threads waiting on this ConditionVariable
bool Monitor::notify_all() {
  os::Solaris::Event* const _Lock_Event = (os::Solaris::Event*)_lock_event;

  assert(_owner != INVALID_THREAD, "notify on unknown thread");
  assert(_owner == Thread::current(), "notify on Monitor not by owner");

  if (_waiters > 0) {

    _Lock_Event->broadcast();

    _tickets = _waiters;
    _counter++;
  }

  return true;
}

// JSR166
// -------------------------------------------------------

/*
 * The solaris and linux implementations of park/unpark are fairly
 * conservative for now, but can be improved. They currently use a
 * mutex/condvar pair, plus _counter. 
 * Park decrements _counter if > 0, else does a condvar wait.  Unpark
 * sets count to 1 and signals condvar.  Only one thread ever waits 
 * on the condvar. Contention seen when trying to park implies that someone 
 * is unparking you, so don't wait. And spurious returns are fine, so there 
 * is no need to track notifications.
 */

#define NANOSECS_PER_SEC 1000000000
#define NANOSECS_PER_MILLISEC 1000000
#define MAX_SECS 100000000
/*
 * This code is common to linux and solaris and will be moved to a
 * common place in dolphin.
 *
 * The passed in time value is either a relative time in nanoseconds
 * or an absolute time in milliseconds. Either way it has to be unpacked
 * into suitable seconds and nanoseconds components and stored in the
 * given timespec structure. 
 * Given time is a 64-bit value and the time_t used in the timespec is only 
 * a signed-32-bit value (except on 64-bit Linux) we have to watch for
 * overflow if times way in the future are given. Further on Solaris versions
 * prior to 10 there is a restriction (see cond_timedwait) that the specified
 * number of seconds, in abstime, is less than current_time  + 100,000,000.
 * As it will be 28 years before "now + 100000000" will overflow we can
 * ignore overflow and just impose a hard-limit on seconds using the value
 * of "now + 100,000,000". This places a limit on the timeout of about 3.17
 * years from "now".
 */
static void unpackTime(timespec* absTime, bool isAbsolute, jlong time) {
  assert (time > 0, "convertTime");

  struct timeval now;
  int status = gettimeofday(&now, NULL);
  assert(status == 0, "gettimeofday");

  time_t max_secs = now.tv_sec + MAX_SECS;

  if (isAbsolute) {
    jlong secs = time / 1000;
    if (secs > max_secs) {
      absTime->tv_sec = max_secs;
    }
    else {
      absTime->tv_sec = secs;
    }
    absTime->tv_nsec = (time % 1000) * NANOSECS_PER_MILLISEC;   
  }
  else {
    jlong secs = time / NANOSECS_PER_SEC;
    if (secs >= MAX_SECS) {
      absTime->tv_sec = max_secs;
      absTime->tv_nsec = 0;
    }
    else {
      absTime->tv_sec = now.tv_sec + secs;
      absTime->tv_nsec = (time % NANOSECS_PER_SEC) + now.tv_usec*1000;
      if (absTime->tv_nsec >= NANOSECS_PER_SEC) {
        absTime->tv_nsec -= NANOSECS_PER_SEC;
        ++absTime->tv_sec; // note: this must be <= max_secs
      }
    }
  }
  assert(absTime->tv_sec >= 0, "tv_sec < 0");
  assert(absTime->tv_sec <= max_secs, "tv_sec > max_secs");
  assert(absTime->tv_nsec >= 0, "tv_nsec < 0");
  assert(absTime->tv_nsec < NANOSECS_PER_SEC, "tv_nsec >= nanos_per_sec");
}

void Parker::park(bool isAbsolute, jlong time) {

  // Optional fast-path check:
  // Return immediately if a permit is available.
  if (_counter > 0) { 
      _counter = 0 ; 
      return ; 
  }

  // Optional fast-exit: Check interrupt before trying to wait
  Thread* thread = Thread::current();
  assert(thread->is_Java_thread(), "Must be JavaThread");
  JavaThread *jt = (JavaThread *)thread;
  if (Thread::is_interrupted(thread, false)) {
    return;
  }

  // First, demultiplex/decode time arguments
  timespec absTime;  
  if (time < 0) { // don't wait at all
    return; 
  }
  if (time > 0) {
    // Warning: this code might be exposed to the old Solaris time
    // round-down bugs.  Grep "roundingFix" for details.  
    unpackTime(&absTime, isAbsolute, time);
  }

  // Enter safepoint region
  // Beware of deadlocks such as 6317397. 
  // The per-thread Parker:: _mutex is a classic leaf-lock.
  // In particular a thread must never block on the Threads_lock while
  // holding the Parker:: mutex.  If safepoints are pending both the
  // the ThreadBlockInVM() CTOR and DTOR may grab Threads_lock.  
  ThreadBlockInVM tbivm(jt);

  // Don't wait if cannot get lock since interference arises from
  // unblocking.  Also. check interrupt before trying wait
  if (Thread::is_interrupted(thread, false) || 
      os::Solaris::mutex_trylock(_mutex) != 0) {
    return;
  }

  int status ; 

  if (_counter > 0)  { // no wait needed
    _counter = 0;
    status = os::Solaris::mutex_unlock(_mutex);
    assert (status == 0, "invariant") ; 
    return;
  }

#ifdef ASSERT
  // Don't catch signals while blocked; let the running threads have the signals.
  // (This allows a debugger to break into the running thread.)
  sigset_t oldsigs;
  sigset_t* allowdebug_blocked = os::Solaris::allowdebug_blocked_signals();
  thr_sigsetmask(SIG_BLOCK, allowdebug_blocked, &oldsigs);
#endif
  
  OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);
  jt->set_suspend_equivalent();
  // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self()

  // Do this the hard way by blocking ...
  // See http://monaco.sfbay/detail.jsf?cr=5094058.
  // TODO-FIXME: for Solaris SPARC set fprs.FEF=0 prior to parking.  
  // Only for SPARC >= V8PlusA
#if defined(__sparc) && defined(COMPILER2)
  if (ClearFPUAtPark) { _mark_fpu_nosave() ; }
#endif
  
  if (time == 0) {
    status = os::Solaris::cond_wait (_cond, _mutex) ; 
  } else { 
    status = os::Solaris::cond_timedwait (_cond, _mutex, &absTime); 
  }
  // Note that an untimed cond_wait() can sometimes return ETIME on older
  // versions of the Solaris.  
  assert_status(status == 0 || status == EINTR || 
		status == ETIME || status == ETIMEDOUT, 
		status, "cond_timedwait");

#ifdef ASSERT
  thr_sigsetmask(SIG_SETMASK, &oldsigs, NULL);
#endif
  _counter = 0 ; 
  status = os::Solaris::mutex_unlock(_mutex);
  assert_status(status == 0, status, "mutex_unlock") ; 

  // If externally suspended while waiting, re-suspend
  if (jt->handle_special_suspend_equivalent_condition()) {
    jt->java_suspend_self();
  }

}

void Parker::unpark() {
  int s, status ; 
  status = os::Solaris::mutex_lock (_mutex) ; 
  assert (status == 0, "invariant") ; 
  s = _counter;
  _counter = 1;
  status = os::Solaris::mutex_unlock (_mutex) ; 
  assert (status == 0, "invariant") ; 

  if (s < 1) {
    status = os::Solaris::cond_signal (_cond) ; 
    assert (status == 0, "invariant") ; 
  }
}