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;
+    }
+}