//
// Copyright (C) 2006 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.vm;
import java.io.PrintWriter;
import java.util.Arrays;
import gov.nasa.jpf.util.HashData;
/**
* Represents the variable, hash-collapsed pooled data associated with an object
* that is not related to the object values (->Fields), but to the use of the
* object for synchronization purposes (locks and signals).
*
*/
public class Monitor implements Cloneable {
static ThreadInfo[] emptySet = new ThreadInfo[0];
/** the thread owning the lock */
private ThreadInfo lockingThread;
/** the nesting level for recursive lock acquisition */
private int lockCount;
/**
* the list of threads that try to acquire the lock (can be in blocked, waiting,
* interrupted or running state).
*/
ThreadInfo[] lockedThreads;
/**
* Creates a new empty monitor.
*/
public Monitor () {
lockingThread = null;
lockCount = 0;
lockedThreads = emptySet;
}
private Monitor (ThreadInfo locking, int count, ThreadInfo[] locked) {
lockingThread = locking;
lockCount = count;
lockedThreads = locked.clone();
Arrays.sort(lockedThreads);
}
public void printFields (PrintWriter pw) {
int i;
pw.print(this);
pw.print(" [");
if (lockingThread != null) {
pw.print( "locked by: ");
pw.print( lockingThread.getName());
} else {
pw.print( "unlocked");
}
pw.print(", lockCount: ");
pw.print( lockCount);
pw.print(", locked: {");
for (i=0; i<lockedThreads.length; i++) {
if (i > 0) pw.print(',');
pw.print(lockedThreads[i].getName());
pw.print(':');
pw.print(lockedThreads[i].getStateName());
}
pw.println("}]");
}
// for debugging purposes
public void dump() {
PrintWriter pw = new PrintWriter(System.out);
printFields(pw);
pw.flush();
}
Monitor cloneWithLocked (ThreadInfo ti) {
return new Monitor(lockingThread, lockCount, add(lockedThreads, ti));
}
Monitor cloneWithoutLocked (ThreadInfo ti) {
return new Monitor(lockingThread, lockCount, remove(lockedThreads, ti));
}
public Monitor clone () {
try {
// no need to clone the empty set (which should be the majority of cases)
Monitor m = (Monitor) super.clone();
if (lockedThreads != emptySet) {
m.lockedThreads = lockedThreads.clone();
}
return m;
} catch (CloneNotSupportedException cnsx) {
throw new InternalError("should not happen");
}
}
/**
* Compares to another object.
*/
public boolean equals (Object o) {
if (o == null) {
return false;
}
if (!(o instanceof Monitor)) {
return false;
}
Monitor m = (Monitor) o;
if (lockingThread != m.getLockingThread()) {
return false;
}
if (lockCount != m.getLockCount()) {
return false;
}
ThreadInfo[] list = m.lockedThreads;
if (lockedThreads.length != list.length) {
return false;
}
for (int i = 0; i < lockedThreads.length; i++) {
if (lockedThreads[i] != list[i]) {
return false;
}
}
return true;
}
public void hash (HashData hd) {
if (lockingThread != null) {
hd.add(lockingThread.getId());
}
hd.add(lockCount);
for (int i = 0; i < lockedThreads.length; i++) {
hd.add(lockedThreads[i].getId());
}
}
public int hashCode () {
HashData hd = new HashData();
hash(hd);
return hd.getValue();
}
/**
* Returns the number of nested locks acquired.
*/
public int getLockCount () {
return lockCount;
}
/**
* Returns the identifier of the thread holding the lock.
*/
public ThreadInfo getLockingThread () {
return lockingThread;
}
/**
* Returns the list of locked threads
*/
public ThreadInfo[] getLockedThreads() {
return lockedThreads;
}
public boolean hasLockedThreads () {
return (lockedThreads.length > 0);
}
public boolean hasWaitingThreads () {
for (int i=0; i<lockedThreads.length; i++) {
if (lockedThreads[i].isWaiting()) {
return true;
}
}
return false;
}
public int getNumberOfWaitingThreads() {
int n=0;
for (ThreadInfo ti : lockedThreads){
if (ti.isWaiting()){
n++;
}
}
return n;
}
public ThreadInfo[] getWaitingThreads() {
int n = getNumberOfWaitingThreads();
if (n > 0){
ThreadInfo[] list = new ThreadInfo[n];
int i=0;
for (int j=0; j<lockedThreads.length && i<n; j++){
ThreadInfo ti = lockedThreads[j];
if (ti.isWaiting()){
list[i++] = ti;
}
}
return list;
} else {
return emptySet;
}
}
public int getNumberOfBlockedThreads() {
int n=0;
for (ThreadInfo ti : lockedThreads){
if (ti.isBlocked()){
n++;
}
}
return n;
}
public ThreadInfo[] getBlockedThreads() {
int n = getNumberOfBlockedThreads();
if (n > 0){
ThreadInfo[] list = new ThreadInfo[n];
int i=0;
for (int j=0; j<lockedThreads.length && i<n; j++){
ThreadInfo ti = lockedThreads[j];
if (ti.isBlocked()){
list[i++] = ti;
}
}
return list;
} else {
return emptySet;
}
}
public int getNumberOfBlockedOrWaitingThreads() {
int n=0;
for (ThreadInfo ti : lockedThreads){
if (ti.isBlocked() || ti.isWaiting()){
n++;
}
}
return n;
}
public ThreadInfo[] getBlockedOrWaitingThreads() {
int n = getNumberOfBlockedThreads();
if (n > 0){
ThreadInfo[] list = new ThreadInfo[n];
int i=0;
for (int j=0; j<lockedThreads.length && i<n; j++){
ThreadInfo ti = lockedThreads[j];
if (ti.isBlocked() || ti.isWaiting()){
list[i++] = ti;
}
}
return list;
} else {
return emptySet;
}
}
/**
* Returns true if it is possible to lock the monitor.
*/
public boolean canLock (ThreadInfo th) {
if (lockingThread == null) {
return true;
}
return (lockingThread == th);
}
void setLockingThread (ThreadInfo ti) {
lockingThread = ti;
}
void incLockCount () {
lockCount++;
}
void decLockCount () {
assert lockCount > 0 : "negative lockCount";
lockCount--;
}
void setLockCount (int lc) {
assert lc >= 0 : "attempt to set negative lockCount";
lockCount = lc;
}
public int objectHashCode () {
return super.hashCode();
}
void resetLockedThreads () {
lockedThreads = emptySet;
}
public boolean isLocking(ThreadInfo ti){
if (lockedThreads != null){
for (ThreadInfo lti : lockedThreads){
if (lti == ti){
return true;
}
}
}
return false;
}
static boolean containsLocked(ThreadInfo[] list, ThreadInfo ti){
int len = list.length;
for (int i=0; i<len; i++){
if (list[i] == ti){
return true;
}
}
return false;
}
static ThreadInfo[] add (ThreadInfo[] list, ThreadInfo ti) {
int len = list.length;
//--- first, check if its already there
if (containsLocked(list, ti)){
// this is required because interrupted parks/joins can try to
// re-park/join from their respective handlers (they don't hold locks)
return list;
}
ThreadInfo[] newList = new ThreadInfo[len+1];
int pos = 0;
for (; pos < len && ti.compareTo(list[pos]) > 0; pos++) {
newList[pos] = list[pos];
}
newList[pos] = ti;
for (; pos < len; pos++) {
newList[pos+1] = list[pos];
}
return newList;
}
void addLocked (ThreadInfo ti) {
lockedThreads = add(lockedThreads, ti);
}
static ThreadInfo[] remove (ThreadInfo[] list, ThreadInfo ti) {
int len = list.length;
if (len == 0) { // nothing to remove from
return list;
} else if (len == 1) { // one element list optimization
if (list[0] == ti) {
return emptySet;
} else {
return list;
}
} else {
//--- first, check if its already there
if (!containsLocked(list, ti)) {
// no known case yet, but we keep it symmetric
// <2do> maybe worth a warning
return list;
}
for (int i=0; i<len; i++) {
if (list[i] == ti) {
int newLen = len-1;
ThreadInfo[] newList = new ThreadInfo[newLen];
if (i > 0) {
System.arraycopy(list, 0, newList, 0, i);
}
if (i < newLen) {
System.arraycopy(list, i+1, newList, i, newLen-i);
}
return newList;
}
}
// else, not in list:
return list;
}
}
void removeLocked (ThreadInfo ti) {
lockedThreads = remove(lockedThreads, ti);
}
}