/******************************************************************************* * Copyright (c) 2009 MATERNA Information & Communications. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html. For further * project-related information visit http://www.ws4d.org. The most recent * version of the JMEDS framework can be obtained from * http://sourceforge.net/projects/ws4d-javame. ******************************************************************************/ package org.ws4d.java.concurrency; import org.ws4d.java.structures.HashMap; import org.ws4d.java.structures.Iterator; import org.ws4d.java.structures.LinkedList; import org.ws4d.java.structures.List; import org.ws4d.java.util.WS4DIllegalStateException; /** * Implementation of a multiple-readers/single-writer lock support. */ public class LockSupport implements Lockable { /** * Enables or disables the visualization of lock access and release. */ // private static boolean showLocks = false; /** * Contains the packages or classes which should be used to show locks. */ // private static Set showLocksFor = null; // // private static final int SHOW_LOCK_STATE_EXCLUSIVE_WAIT = 0; // // private static final int SHOW_LOCK_STATE_EXCLUSIVE_GOT = 1; // // private static final int SHOW_LOCK_STATE_EXCLUSIVE_RELEASE = 2; // // private static final int SHOW_LOCK_STATE_SHARED_WAIT = 3; // // private static final int SHOW_LOCK_STATE_SHARED_GOT = 4; // // private static final int SHOW_LOCK_STATE_SHARED_RELEASE = 5; /** * Constant which indicates that the subject is currently unlocked. */ private static final int UNLOCKED = 0; /** * Constant which indicates that the subject has only shared locks on it. */ private static final int SHARED_LOCKED = 1; /** * Constant which indicates that the subject is locked exclusively. */ private static final int EXCLUSIVE_LOCKED = 2; /** Map(Thread, Lock) The locks which are currently held by the threads. */ private final HashMap allocatedLocks; private int allocatedSharedLockNum = 0; private boolean isExclusivelyLocked = false; /** List(Lock) List containing lock requests. */ private final List waitingForLock; /** a thread is waiting to get an exclusive lock */ private boolean firstInListAwaitsExclusive = false; /** a thread who has a shared lock, demands upgrade */ private boolean sharedAwaitingExclusive = false; // static { // if (showLocks) { // showLocksFor = new HashSet(); // /* // * Add a some packages or classes to show locks for! // */ // showLocksFor.add("org.ws4d.java.service.DefaultDevice.start"); // showLocksFor.add("org.ws4d.java.dispatch.DeviceServiceRegistry"); // } // } /** * Show log if possible. Depends on the possibility to receive the stack * trace from the given toolkit. * * @param state */ // static void logLockTrace(int state, Lock lock) { // if (Log.isDebug() && showLocks) { // String[] trace = Log.getStackTrace(new Exception()); // if (trace != null) { // String t = "[thread=" + Thread.currentThread() + ", trace=" + trace[2] + ", lock=" + lock + "]"; // Iterator it = showLocksFor.iterator(); // while (it.hasNext()) { // String s = (String) it.next(); // if (trace[2].indexOf(s) >= 0) { // switch (state) { // case SHOW_LOCK_STATE_EXCLUSIVE_WAIT: // Log.debug("=LOCK W(exclusive): " + t + ".", Log.DEBUG_LAYER_FRAMEWORK); // break; // case SHOW_LOCK_STATE_EXCLUSIVE_GOT: // Log.debug("+LOCK G(exclusive): " + t + ".", Log.DEBUG_LAYER_FRAMEWORK); // break; // case SHOW_LOCK_STATE_EXCLUSIVE_RELEASE: // Log.debug("-LOCK R(exclusive): " + t + ".", Log.DEBUG_LAYER_FRAMEWORK); // break; // case SHOW_LOCK_STATE_SHARED_WAIT: // Log.debug("=LOCK W(shared): " + t + ".", Log.DEBUG_LAYER_FRAMEWORK); // break; // case SHOW_LOCK_STATE_SHARED_GOT: // Log.debug("+LOCK G(shared): " + t + ".", Log.DEBUG_LAYER_FRAMEWORK); // break; // case SHOW_LOCK_STATE_SHARED_RELEASE: // Log.debug("-LOCK R(shared): " + t + ".", Log.DEBUG_LAYER_FRAMEWORK); // break; // } // } // } // } // } // } /** * Constructs a new LockSupport. */ public LockSupport() { super(); this.allocatedLocks = new HashMap(); this.waitingForLock = new LinkedList(); } /* * (non-Javadoc) * @see java.lang.Object#toString() */ public synchronized String toString() { StringBuffer sb = new StringBuffer(); sb.append("LockSupport (").append(hashCode()).append("): ["); sb.append(" ASL=").append(allocatedSharedLockNum); sb.append(", AEL=").append(isExclusivelyLocked); sb.append(", allocatedLocks=").append(allocatedLocks); sb.append(", waiting=").append(waitingForLock); sb.append(" ]"); return sb.toString(); } /* * (non-Javadoc) * @see org.ws4d.java.concurrency.locks.Lockable#sharedLock() */ public void sharedLock() { Lock lock = null; // logLockTrace(SHOW_LOCK_STATE_SHARED_WAIT, lock); synchronized (this) { lock = getLockForCurrentThread(); if (lock.tryAllocateShared()) { // logLockTrace(SHOW_LOCK_STATE_SHARED_GOT, lock); return; } lock.prepareAwaitSharedAllocation(); } lock.awaitSharedAllocation(); // logLockTrace(SHOW_LOCK_STATE_SHARED_GOT, lock); } /* * (non-Javadoc) * @see org.ws4d.java.concurrency.locks.Lockable#exclusiveLock() */ public void exclusiveLock() { Lock lock = null; // logLockTrace(SHOW_LOCK_STATE_EXCLUSIVE_WAIT, lock); synchronized (this) { lock = getLockForCurrentThread(); if (lock.tryAllocateExclusive()) { // logLockTrace(SHOW_LOCK_STATE_EXCLUSIVE_GOT, lock); return; } lock.prepareAwaitExclusiveAllocation(); } lock.awaitExclusiveAllocation(); // logLockTrace(SHOW_LOCK_STATE_EXCLUSIVE_GOT, lock); } /* * (non-Javadoc) * @see org.ws4d.java.concurrency.locks.Lockable#trySharedLock() */ public synchronized boolean trySharedLock() { return getLockForCurrentThread().tryAllocateShared(); } /* * (non-Javadoc) * @see org.ws4d.java.concurrency.locks.Lockable#tryExclusiveLock() */ public synchronized boolean tryExclusiveLock() { return getLockForCurrentThread().tryAllocateExclusive(); } /* * (non-Javadoc) * @see org.ws4d.java.concurrency.locks.Lockable#releaseSharedLock() */ public synchronized void releaseSharedLock() { Lock lock = (Lock) allocatedLocks.get(Thread.currentThread()); if ((lock == null) || (lock.tSharedLocksNum == 0)) { throw new WS4DIllegalStateException("Current thread has no allocated shared lock!"); } if (lock.releaseShared()) { checkWaitingLockRequests(); // logLockTrace(SHOW_LOCK_STATE_SHARED_RELEASE, lock); } } /* * (non-Javadoc) * @see org.ws4d.java.concurrency.locks.Lockable#releaseExclusiveLock() */ public synchronized boolean releaseExclusiveLock() { Lock lock = (Lock) allocatedLocks.get(Thread.currentThread()); if ((lock == null) || (lock.tExclusiveLocksNum == 0)) { throw new WS4DIllegalStateException("Current thread has no allocated exclusive lock!"); } if (lock.releaseExclusive()) { checkWaitingLockRequests(); // logLockTrace(SHOW_LOCK_STATE_EXCLUSIVE_RELEASE, lock); return true; } // logLockTrace(SHOW_LOCK_STATE_EXCLUSIVE_RELEASE, lock); return false; } private int getState() { if (isExclusivelyLocked) { return EXCLUSIVE_LOCKED; } if (allocatedSharedLockNum > 0) { return SHARED_LOCKED; } return UNLOCKED; } private Lock getLockForCurrentThread() { Thread currentThread = Thread.currentThread(); Lock lock = (Lock) allocatedLocks.get(currentThread); if (lock == null) { lock = new Lock(currentThread); } return lock; } /** * Determines which queued lock requests can be performed. */ private void checkWaitingLockRequests() { for (Iterator it = waitingForLock.iterator(); it.hasNext();) { Lock lock = (Lock) it.next(); if (lock.tryAllocateAfterDelay()) { it.remove(); } else { break; } } if (waitingForLock.size() == 0) firstInListAwaitsExclusive = false; else firstInListAwaitsExclusive = ((Lock) waitingForLock.get(0)).isWaitingForExclusiveLock(); } /** * */ private class Lock { private final Thread thread; private volatile boolean tHasLock = false; private int tSharedLocksNum = 0; private volatile int tExclusiveLocksNum = 0; private static final int WAITING_FOR_EXCLUSIVE_LOCK = -1; private volatile long lockNumber = 0; public Lock(Thread thread) { super(); this.lockNumber++; this.thread = thread; } public boolean isWaitingForExclusiveLock() { return tExclusiveLocksNum == WAITING_FOR_EXCLUSIVE_LOCK; } /* * (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append("Lock [ n=").append(lockNumber); sb.append(", thread=").append(thread); sb.append(", hl=").append(tHasLock); sb.append(", SL=").append(tSharedLocksNum); sb.append(", EL=").append(tExclusiveLocksNum); sb.append(" ]"); return sb.toString(); } boolean tryAllocateShared() { switch (getState()) { case UNLOCKED: allocateShared(); return true; case SHARED_LOCKED: if (!tHasLock && firstInListAwaitsExclusive) { return false; } allocateShared(); return true; case EXCLUSIVE_LOCKED: if (tExclusiveLocksNum > 0) { allocateShared(); return true; } return false; default: return false; } } boolean tryAllocateExclusive() { switch (getState()) { case UNLOCKED: allocateExclusive(); return true; case SHARED_LOCKED: if (tSharedLocksNum == allocatedSharedLockNum) { allocateExclusive(); return true; } if (tSharedLocksNum > 0 && sharedAwaitingExclusive) { /* * This thread has a shared lock and tries to upgrade, * an other thread demands upgrade too, this will lead * to deadlock. */ throw new DeadlockException("Deadlock because two threads try to upgrade. Implement exception handling for this case."); } return false; case EXCLUSIVE_LOCKED: if (tExclusiveLocksNum > 0) { allocateExclusive(); return true; } return false; default: return false; } } /** * Checks if the lock can be allocated. * * @return <code>true</code> if the lock can be allocated, * <code>false</code> otherwise. */ boolean tryAllocateAfterDelay() { switch (getState()) { case UNLOCKED: if (tExclusiveLocksNum == WAITING_FOR_EXCLUSIVE_LOCK) { // wants // exclusive // lock isExclusivelyLocked = true; tExclusiveLocksNum = 1; } else { // wants shared lock allocatedSharedLockNum = 1; } allocateAfterDelay(); return true; case SHARED_LOCKED: if (tExclusiveLocksNum == WAITING_FOR_EXCLUSIVE_LOCK) { // wants // exclusive // lock if (tSharedLocksNum == allocatedSharedLockNum) { sharedAwaitingExclusive = false; isExclusivelyLocked = true; allocateAfterDelay(); return true; } else { return false; } } else { // wants shared lock allocatedSharedLockNum++; allocateAfterDelay(); return true; } case EXCLUSIVE_LOCKED: if (tExclusiveLocksNum == WAITING_FOR_EXCLUSIVE_LOCK) { // wants // exclusive // lock return false; } else { // wants shared lock if (tExclusiveLocksNum > 0) { allocatedSharedLockNum++; allocateAfterDelay(); return true; } else { return false; } } default: return false; } } private void allocateAfterDelay() { if (tExclusiveLocksNum == WAITING_FOR_EXCLUSIVE_LOCK) { tExclusiveLocksNum = 1; } allocate(); // trigger resume within delayAllocation() of target thread synchronized (this) { notify(); } } private void allocateShared() { tSharedLocksNum++; allocatedSharedLockNum++; allocate(); } private void allocateExclusive() { tExclusiveLocksNum++; isExclusivelyLocked = true; allocate(); } private void allocate() { tHasLock = true; allocatedLocks.put(thread, this); } boolean releaseShared() { tSharedLocksNum--; allocatedSharedLockNum--; if (tSharedLocksNum == 0 && tExclusiveLocksNum == 0) { allocatedLocks.remove(thread); tHasLock = false; return true; } return false; } boolean releaseExclusive() { tExclusiveLocksNum--; if (tExclusiveLocksNum == 0) { isExclusivelyLocked = false; if (tSharedLocksNum == 0) { allocatedLocks.remove(thread); tHasLock = false; } return true; } return false; } void prepareAwaitSharedAllocation() { tSharedLocksNum = 1; waitingForLock.add(this); if (waitingForLock.size() == 1) firstInListAwaitsExclusive = false; } void prepareAwaitExclusiveAllocation() { tExclusiveLocksNum = WAITING_FOR_EXCLUSIVE_LOCK; if (tSharedLocksNum > 0) { /* * Has shared lock but does not get exclusive now. */ sharedAwaitingExclusive = true; firstInListAwaitsExclusive = true; waitingForLock.add(0, this); } else { if (waitingForLock.size() == 0) firstInListAwaitsExclusive = true; waitingForLock.add(this); } } synchronized void awaitSharedAllocation() { while (!tHasLock) { try { wait(); } catch (InterruptedException e) { // time to check whether we can resume } } } synchronized void awaitExclusiveAllocation() { while (tExclusiveLocksNum == WAITING_FOR_EXCLUSIVE_LOCK) { try { wait(); } catch (InterruptedException e) { // time to check whether we can resume } } } } }