Mercurial > projects > dwt-addons
diff dwtx/core/internal/jobs/LockManager.d @ 122:9d0585bcb7aa
Add core.jobs package
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Tue, 12 Aug 2008 02:34:21 +0200 |
parents | |
children | 862b05e0334a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/core/internal/jobs/LockManager.d Tue Aug 12 02:34:21 2008 +0200 @@ -0,0 +1,301 @@ +/******************************************************************************* + * Copyright (c) 2003, 2006 IBM Corporation and others. + * 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 + * + * Contributors: + * IBM - Initial API and implementation + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ +module dwtx.core.internal.jobs.LockManager; + +import tango.core.Thread; +import dwt.dwthelper.utils; +import dwtx.dwtxhelper.Collection; + +import dwtx.core.internal.runtime.RuntimeLog; +import dwtx.core.runtime.CoreException; +import dwtx.core.runtime.IStatus; +import dwtx.core.runtime.MultiStatus; +import dwtx.core.runtime.Status; +import dwtx.core.runtime.jobs.ISchedulingRule; +import dwtx.core.runtime.jobs.LockListener; + +import dwtx.core.internal.jobs.OrderedLock; +import dwtx.core.internal.jobs.DeadlockDetector; +import dwtx.core.internal.jobs.Deadlock; +import dwtx.core.internal.jobs.JobManager; +import dwtx.core.internal.jobs.Worker; + +/** + * Stores the only reference to the graph that contains all the known + * relationships between locks, rules, and the threads that own them. + * Synchronizes all access to the graph on the only instance that exists in this class. + * + * Also stores the state of suspended locks so that they can be re-acquired with + * the proper lock depth. + */ +public class LockManager { + /** + * This class captures the state of suspended locks. + * Locks are suspended if deadlock is detected. + */ + private static class LockState { + private int depth; + private OrderedLock lock; + + /** + * Suspends ownership of the given lock, and returns the saved state. + */ + protected static LockState suspend(OrderedLock lock) { + LockState state = new LockState(); + state.lock = lock; + state.depth = lock.forceRelease_package(); + return state; + } + + /** + * Re-acquires a suspended lock and reverts to the correct lock depth. + */ + public void resume() { + //spin until the lock is successfully acquired + //NOTE: spinning here allows the UI thread to service pending syncExecs + //if the UI thread is waiting to acquire a lock. + while (true) { + try { + if (lock.acquire(Long.MAX_VALUE)) + break; + } catch (InterruptedException e) { + //ignore and loop + } + } + lock.setDepth_package(depth); + } + } + + //the lock listener for this lock manager + protected LockListener lockListener; + /* + * The internal data structure that stores all the relationships + * between the locks (or rules) and the threads that own them. + */ + private DeadlockDetector locks; + /* + * Stores thread - stack pairs where every entry in the stack is an array + * of locks that were suspended while the thread was acquiring more locks + * (a stack is needed because when a thread tries to re-aquire suspended locks, + * it can cause deadlock, and some locks it owns can be suspended again) + */ + private HashMap suspendedLocks; + + public this() { +// super(); + locks = new DeadlockDetector(); + suspendedLocks = new HashMap(); + } + + /* (non-Javadoc) + * Method declared on LockListener + */ + public void aboutToRelease() { + if (lockListener is null) + return; + try { + lockListener.aboutToRelease(); + } catch (Exception e) { + handleException(e); +// } catch (LinkageError e) { +// handleException(e); + } + } + + /* (non-Javadoc) + * Method declared on LockListener + */ + public bool aboutToWait(Thread lockOwner) { + if (lockListener is null) + return false; + try { + return lockListener.aboutToWait(lockOwner); + } catch (Exception e) { + handleException(e); +// } catch (LinkageError e) { +// handleException(e); + } + return false; + } + + /** + * This thread has just acquired a lock. Update graph. + */ + void addLockThread(Thread thread, ISchedulingRule lock) { + if (locks is null) + return; + try { + synchronized (locks) { + locks.lockAcquired(thread, lock); + } + } catch (Exception e) { + handleInternalError(e); + } + } + + /** + * This thread has just been refused a lock. Update graph and check for deadlock. + */ + void addLockWaitThread(Thread thread, ISchedulingRule lock) { + if (locks is null) + return; + try { + Deadlock found = null; + synchronized (locks) { + found = locks.lockWaitStart(thread, lock); + } + if (found is null) + return; + // if deadlock was detected, the found variable will contain all the information about it, + // including which locks to suspend for which thread to resolve the deadlock. + ISchedulingRule[] toSuspend = found.getLocks(); + LockState[] suspended = new LockState[toSuspend.length]; + for (int i = 0; i < toSuspend.length; i++) + suspended[i] = LockState.suspend(cast(OrderedLock) toSuspend[i]); + synchronized (suspendedLocks) { + Stack prevLocks = cast(Stack) suspendedLocks.get(found.getCandidate()); + if (prevLocks is null) + prevLocks = new Stack(); + prevLocks.push(new ArrayWrapperObject(suspended)); + suspendedLocks.put(found.getCandidate(), prevLocks); + } + } catch (Exception e) { + handleInternalError(e); + } + } + + /** + * Handles exceptions that occur while calling third party code from within the + * LockManager. This is essentially an in-lined version of Platform.run(ISafeRunnable) + */ + private static void handleException(Exception e) { + IStatus status; + if (cast(CoreException)e ) { + //logged message should not be translated + status = new MultiStatus(JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, "LockManager.handleException", e); //$NON-NLS-1$ + (cast(MultiStatus) status).merge((cast(CoreException) e).getStatus()); + } else { + status = new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, "LockManager.handleException", e); //$NON-NLS-1$ + } + RuntimeLog.log(status); + } + + /** + * There was an internal error in the deadlock detection code. Shut the entire + * thing down to prevent further errors. Recovery is too complex as it + * requires freezing all threads and inferring the present lock state. + */ + private void handleInternalError(Exception t) { + try { + handleException(t); + handleException(new Exception(locks.toDebugString())); + } catch (Exception e2) { + //ignore failure to log or to create the debug string + } + //discard the deadlock detector for good + locks = null; + } + + /** + * Returns true IFF the underlying graph is empty. + * For debugging purposes only. + */ + public bool isEmpty() { + return locks.isEmpty(); + } + + /** + * Returns true IFF this thread either owns, or is waiting for, any locks or rules. + */ + public bool isLockOwner() { + //all job threads have to be treated as lock owners because UI thread + //may try to join a job + Thread current = Thread.getThis(); + if (cast(Worker)current ) + return true; + if (locks is null) + return false; + synchronized (locks) { + return locks.contains(Thread.getThis()); + } + } + + /** + * Creates and returns a new lock. + */ + public synchronized OrderedLock newLock() { + return new OrderedLock(this); + } + + /** + * Releases all the acquires that were called on the given rule. Needs to be called only once. + */ + void removeLockCompletely(Thread thread, ISchedulingRule rule) { + if (locks is null) + return; + try { + synchronized (locks) { + locks.lockReleasedCompletely(thread, rule); + } + } catch (Exception e) { + handleInternalError(e); + } + } + + /** + * This thread has just released a lock. Update graph. + */ + void removeLockThread(Thread thread, ISchedulingRule lock) { + try { + synchronized (locks) { + locks.lockReleased(thread, lock); + } + } catch (Exception e) { + handleInternalError(e); + } + } + + /** + * This thread has just stopped waiting for a lock. Update graph. + */ + void removeLockWaitThread(Thread thread, ISchedulingRule lock) { + try { + synchronized (locks) { + locks.lockWaitStop(thread, lock); + } + } catch (Exception e) { + handleInternalError(e); + } + } + + /** + * Resumes all the locks that were suspended while this thread was waiting to acquire another lock. + */ + void resumeSuspendedLocks(Thread owner) { + LockState[] toResume; + synchronized (suspendedLocks) { + Stack prevLocks = cast(Stack) suspendedLocks.get(owner); + if (prevLocks is null) + return; + toResume = arrayFromObject!(LockState)( prevLocks.pop() ); + if (prevLocks.empty()) + suspendedLocks.remove(owner); + } + for (int i = 0; i < toResume.length; i++) + toResume[i].resume(); + } + + public void setLockListener(LockListener listener) { + this.lockListener = listener; + } +}