changeset 122:9d0585bcb7aa

Add core.jobs package
author Frank Benoit <benoit@tionex.de>
date Tue, 12 Aug 2008 02:34:21 +0200
parents c0304616ea23
children ddeafd007ce4
files dwtx/core/internal/jobs/Deadlock.d dwtx/core/internal/jobs/DeadlockDetector.d dwtx/core/internal/jobs/ImplicitJobs.d dwtx/core/internal/jobs/InternalJob.d dwtx/core/internal/jobs/JobActivator.d dwtx/core/internal/jobs/JobChangeEvent.d dwtx/core/internal/jobs/JobListeners.d dwtx/core/internal/jobs/JobManager.d dwtx/core/internal/jobs/JobMessages.d dwtx/core/internal/jobs/JobOSGiUtils.d dwtx/core/internal/jobs/JobQueue.d dwtx/core/internal/jobs/JobStatus.d dwtx/core/internal/jobs/LockManager.d dwtx/core/internal/jobs/ObjectMap.d dwtx/core/internal/jobs/OrderedLock.d dwtx/core/internal/jobs/Queue.d dwtx/core/internal/jobs/Semaphore.d dwtx/core/internal/jobs/StringPool.d dwtx/core/internal/jobs/ThreadJob.d dwtx/core/internal/jobs/Worker.d dwtx/core/internal/jobs/WorkerPool.d dwtx/core/internal/runtime/RuntimeLog.d dwtx/core/runtime/PlatformObject.d dwtx/core/runtime/jobs/IJobChangeEvent.d dwtx/core/runtime/jobs/IJobChangeListener.d dwtx/core/runtime/jobs/IJobManager.d dwtx/core/runtime/jobs/IJobStatus.d dwtx/core/runtime/jobs/ILock.d dwtx/core/runtime/jobs/ISchedulingRule.d dwtx/core/runtime/jobs/Job.d dwtx/core/runtime/jobs/JobChangeAdapter.d dwtx/core/runtime/jobs/LockListener.d dwtx/core/runtime/jobs/MultiRule.d dwtx/core/runtime/jobs/ProgressProvider.d dwtx/core/runtime/jobs/package.html dwtx/osgi/util/NLS.d res/dwtx.core.internal.jobs.messages.properties
diffstat 37 files changed, 8452 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/Deadlock.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * 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.Deadlock;
+
+import tango.core.Thread;
+
+import dwt.dwthelper.utils;
+
+import dwtx.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * The deadlock class stores information about a deadlock that just occurred.
+ * It contains an array of the threads that were involved in the deadlock
+ * as well as the thread that was chosen to be suspended and an array of locks
+ * held by that thread that are going to be suspended to resolve the deadlock.
+ */
+class Deadlock {
+    //all the threads which are involved in the deadlock
+    private Thread[] threads;
+    //the thread whose locks will be suspended to resolve deadlock
+    private Thread candidate;
+    //the locks that will be suspended
+    private ISchedulingRule[] locks;
+
+    public this(Thread[] threads, ISchedulingRule[] locks, Thread candidate) {
+        this.threads = threads;
+        this.locks = locks;
+        this.candidate = candidate;
+    }
+
+    public ISchedulingRule[] getLocks() {
+        return locks;
+    }
+
+    public Thread getCandidate() {
+        return candidate;
+    }
+
+    public Thread[] getThreads() {
+        return threads;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/DeadlockDetector.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,721 @@
+/*******************************************************************************
+ * 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 Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.internal.jobs.DeadlockDetector;
+
+import tango.core.Thread;
+import tango.io.Stdout;
+import tango.text.convert.Format;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.core.internal.runtime.RuntimeLog;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.MultiStatus;
+import dwtx.core.runtime.Status;
+import dwtx.core.runtime.jobs.ILock;
+import dwtx.core.runtime.jobs.ISchedulingRule;
+import dwtx.core.internal.jobs.Deadlock;
+import dwtx.core.internal.jobs.JobManager;
+
+/**
+ * Stores all the relationships between locks (rules are also considered locks),
+ * and the threads that own them. All the relationships are stored in a 2D integer array.
+ * The rows in the array are threads, while the columns are locks.
+ * Two corresponding arrayLists store the actual threads and locks.
+ * The index of a thread in the first arrayList is the index of the row in the graph.
+ * The index of a lock in the second arrayList is the index of the column in the graph.
+ * An entry greater than 0 in the graph is the number of times a thread in the entry's row
+ * acquired the lock in the entry's column.
+ * An entry of -1 means that the thread is waiting to acquire the lock.
+ * An entry of 0 means that the thread and the lock have no relationship.
+ *
+ * The difference between rules and locks is that locks can be suspended, while
+ * rules are implicit locks and as such cannot be suspended.
+ * To resolve deadlock, the graph will first try to find a thread that only owns
+ * locks. Failing that, it will find a thread in the deadlock that owns at least
+ * one lock and suspend it.
+ *
+ * Deadlock can only occur among locks, or among locks in combination with rules.
+ * Deadlock among rules only is impossible. Therefore, in any deadlock one can always
+ * find a thread that owns at least one lock that can be suspended.
+ *
+ * The implementation of the graph assumes that a thread can only own 1 rule at
+ * any one time. It can acquire that rule several times, but a thread cannot
+ * acquire 2 non-conflicting rules at the same time.
+ *
+ * The implementation of the graph will sometimes also find and resolve bogus deadlocks.
+ *      graph:              assuming this rule hierarchy:
+ *         R2 R3 L1                     R1
+ *      J1  1  0  0                    /  \
+ *      J2  0  1 -1                   R2  R3
+ *      J3 -1  0  1
+ *
+ * If in the above situation job4 decides to acquire rule1, then the graph will transform
+ * to the following:
+ *         R2 R3 R1 L1
+ *      J1  1  0  1  0
+ *      J2  1  1  1 -1
+ *      J3 -1  0  0  1
+ *      J4  0  0 -1  0
+ *
+ * and the graph will assume that job2 and job3 are deadlocked and suspend lock1 of job3.
+ * The reason the deadlock is bogus is that the deadlock is unlikely to actually happen (the threads
+ * are currently not deadlocked, but might deadlock later on when it is too late to detect it)
+ * Therefore, in order to make sure that no deadlock is possible,
+ * the deadlock will still be resolved at this point.
+ */
+class DeadlockDetector {
+    private static int NO_STATE = 0;
+    //state variables in the graph
+    private static int WAITING_FOR_LOCK = -1;
+    //empty matrix
+    private static const int[][] EMPTY_MATRIX = null;
+    //matrix of relationships between threads and locks
+    private int[][] graph = EMPTY_MATRIX;
+    //index is column in adjacency matrix for the lock
+    private final ArrayList locks;
+    //index is row in adjacency matrix for the thread
+    private final ArrayList lockThreads;
+    //whether the graph needs to be resized
+    private bool resize = false;
+
+    public this(){
+        locks = new ArrayList();
+        lockThreads = new ArrayList();
+    }
+
+    /**
+     * Recursively check if any of the threads that prevent the current thread from running
+     * are actually deadlocked with the current thread.
+     * Add the threads that form deadlock to the deadlockedThreads list.
+     */
+    private bool addCycleThreads(ArrayList deadlockedThreads, Thread next) {
+        //get the thread that block the given thread from running
+        Thread[] blocking = blockingThreads(next);
+        //if the thread is not blocked by other threads, then it is not part of a deadlock
+        if (blocking.length is 0)
+            return false;
+        bool inCycle = false;
+        for (int i = 0; i < blocking.length; i++) {
+            //if we have already visited the given thread, then we found a cycle
+            if (deadlockedThreads.contains(blocking[i])) {
+                inCycle = true;
+            } else {
+                //otherwise, add the thread to our list and recurse deeper
+                deadlockedThreads.add(blocking[i]);
+                //if the thread is not part of a cycle, remove it from the list
+                if (addCycleThreads(deadlockedThreads, blocking[i]))
+                    inCycle = true;
+                else
+                    deadlockedThreads.remove(blocking[i]);
+            }
+        }
+        return inCycle;
+    }
+
+    /**
+     * Get the thread(s) that own the lock this thread is waiting for.
+     */
+    private Thread[] blockingThreads(Thread current) {
+        //find the lock this thread is waiting for
+        ISchedulingRule lock = cast(ISchedulingRule) getWaitingLock(current);
+        return getThreadsOwningLock(lock);
+    }
+
+    /**
+     * Check that the addition of a waiting thread did not produce deadlock.
+     * If deadlock is detected return true, else return false.
+     */
+    private bool checkWaitCycles(int[] waitingThreads, int lockIndex) {
+        /**
+         * find the lock that this thread is waiting for
+         * recursively check if this is a cycle (i.e. a thread waiting on itself)
+         */
+        for (int i = 0; i < graph.length; i++) {
+            if (graph[i][lockIndex] > NO_STATE) {
+                if (waitingThreads[i] > NO_STATE) {
+                    return true;
+                }
+                //keep track that we already visited this thread
+                waitingThreads[i]++;
+                for (int j = 0; j < graph[i].length; j++) {
+                    if (graph[i][j] is WAITING_FOR_LOCK) {
+                        if (checkWaitCycles(waitingThreads, j))
+                            return true;
+                    }
+                }
+                //this thread is not involved in a cycle yet, so remove the visited flag
+                waitingThreads[i]--;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true IFF the matrix contains a row for the given thread.
+     * (meaning the given thread either owns locks or is waiting for locks)
+     */
+    bool contains(Thread t) {
+        return lockThreads.contains(t);
+    }
+
+    /**
+     * A new rule was just added to the graph.
+     * Find a rule it conflicts with and update the new rule with the number of times
+     * it was acquired implicitly when threads acquired conflicting rule.
+     */
+    private void fillPresentEntries(ISchedulingRule newLock, int lockIndex) {
+        //fill in the entries for the new rule from rules it conflicts with
+        for (int j = 0; j < locks.size(); j++) {
+            if ((j !is lockIndex) && (newLock.isConflicting(cast(ISchedulingRule) locks.get(j)))) {
+                for (int i = 0; i < graph.length; i++) {
+                    if ((graph[i][j] > NO_STATE) && (graph[i][lockIndex] is NO_STATE))
+                        graph[i][lockIndex] = graph[i][j];
+                }
+            }
+        }
+        //now back fill the entries for rules the current rule conflicts with
+        for (int j = 0; j < locks.size(); j++) {
+            if ((j !is lockIndex) && (newLock.isConflicting(cast(ISchedulingRule) locks.get(j)))) {
+                for (int i = 0; i < graph.length; i++) {
+                    if ((graph[i][lockIndex] > NO_STATE) && (graph[i][j] is NO_STATE))
+                        graph[i][j] = graph[i][lockIndex];
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns all the locks owned by the given thread
+     */
+    private Object[] getOwnedLocks(Thread current) {
+        ArrayList ownedLocks = new ArrayList(1);
+        int index = indexOf(current, false);
+
+        for (int j = 0; j < graph[index].length; j++) {
+            if (graph[index][j] > NO_STATE)
+                ownedLocks.add(locks.get(j));
+        }
+        if (ownedLocks.size() is 0)
+            Assert.isLegal(false, "A thread with no locks is part of a deadlock."); //$NON-NLS-1$
+        return ownedLocks.toArray();
+    }
+
+    /**
+     * Returns an array of threads that form the deadlock (usually 2).
+     */
+    private Thread[] getThreadsInDeadlock(Thread cause) {
+        ArrayList deadlockedThreads = new ArrayList(2);
+        /**
+         * if the thread that caused deadlock doesn't own any locks, then it is not part
+         * of the deadlock (it just caused it because of a rule it tried to acquire)
+         */
+        if (ownsLocks(cause))
+            deadlockedThreads.add(cause);
+        addCycleThreads(deadlockedThreads, cause);
+        return arraycast!(Thread)( deadlockedThreads.toArray());
+    }
+
+    /**
+     * Returns the thread(s) that own the given lock.
+     */
+    private Thread[] getThreadsOwningLock(ISchedulingRule rule) {
+        if (rule is null)
+            return new Thread[0];
+        int lockIndex = indexOf(rule, false);
+        ArrayList blocking = new ArrayList(1);
+        for (int i = 0; i < graph.length; i++) {
+            if (graph[i][lockIndex] > NO_STATE)
+                blocking.add(lockThreads.get(i));
+        }
+        if ((blocking.size() is 0) && (JobManager.DEBUG_LOCKS))
+            Stdout.formatln(Format("Lock {} is involved in deadlock but is not owned by any thread.", rule )); //$NON-NLS-1$ //$NON-NLS-2$
+        if ((blocking.size() > 1) && (cast(ILock)rule ) && (JobManager.DEBUG_LOCKS))
+            Stdout.formatln(Format("Lock {} is owned by more than 1 thread, but it is not a rule.", rule )); //$NON-NLS-1$ //$NON-NLS-2$
+        return arraycast!(Thread)( blocking.toArray());
+    }
+
+    /**
+     * Returns the lock the given thread is waiting for.
+     */
+    private Object getWaitingLock(Thread current) {
+        int index = indexOf(current, false);
+        //find the lock that this thread is waiting for
+        for (int j = 0; j < graph[index].length; j++) {
+            if (graph[index][j] is WAITING_FOR_LOCK)
+                return locks.get(j);
+        }
+        //it can happen that a thread is not waiting for any lock (it is not really part of the deadlock)
+        return null;
+    }
+
+    /**
+     * Returns the index of the given lock in the lock array. If the lock is
+     * not present in the array, it is added to the end.
+     */
+    private int indexOf(ISchedulingRule lock, bool add) {
+        int index = locks.indexOf(cast(Object)lock);
+        if ((index < 0) && add) {
+            locks.add(cast(Object)lock);
+            resize = true;
+            index = locks.size() - 1;
+        }
+        return index;
+    }
+
+    /**
+     * Returns the index of the given thread in the thread array. If the thread
+     * is not present in the array, it is added to the end.
+     */
+    private int indexOf(Thread owner, bool add) {
+        int index = lockThreads.indexOf(owner);
+        if ((index < 0) && add) {
+            lockThreads.add(owner);
+            resize = true;
+            index = lockThreads.size() - 1;
+        }
+        return index;
+    }
+
+    /**
+     * Returns true IFF the adjacency matrix is empty.
+     */
+    bool isEmpty() {
+        return (locks.size() is 0) && (lockThreads.size() is 0) && (graph.length is 0);
+    }
+
+    /**
+     * The given lock was acquired by the given thread.
+     */
+    void lockAcquired(Thread owner, ISchedulingRule lock) {
+        int lockIndex = indexOf(lock, true);
+        int threadIndex = indexOf(owner, true);
+        if (resize)
+            resizeGraph();
+        if (graph[threadIndex][lockIndex] is WAITING_FOR_LOCK)
+            graph[threadIndex][lockIndex] = NO_STATE;
+        /**
+         * acquire all locks that conflict with the given lock
+         * or conflict with a lock the given lock will acquire implicitly
+         * (locks are acquired implicitly when a conflicting lock is acquired)
+         */
+        ArrayList conflicting = new ArrayList(1);
+        //only need two passes through all the locks to pick up all conflicting rules
+        int NUM_PASSES = 2;
+        conflicting.add(cast(Object)lock);
+        graph[threadIndex][lockIndex]++;
+        for (int i = 0; i < NUM_PASSES; i++) {
+            for (int k = 0; k < conflicting.size(); k++) {
+                ISchedulingRule current = cast(ISchedulingRule) conflicting.get(k);
+                for (int j = 0; j < locks.size(); j++) {
+                    ISchedulingRule possible = cast(ISchedulingRule) locks.get(j);
+                    if (current.isConflicting(possible) && !conflicting.contains(cast(Object)possible)) {
+                        conflicting.add(cast(Object)possible);
+                        graph[threadIndex][j]++;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * The given lock was released by the given thread. Update the graph.
+     */
+    void lockReleased(Thread owner, ISchedulingRule lock) {
+        int lockIndex = indexOf(lock, false);
+        int threadIndex = indexOf(owner, false);
+        //make sure the lock and thread exist in the graph
+        if (threadIndex < 0) {
+            if (JobManager.DEBUG_LOCKS)
+                Stdout.formatln("[lockReleased] Lock {} was already released by thread {}", lock, owner.name()); //$NON-NLS-1$ //$NON-NLS-2$
+            return;
+        }
+        if (lockIndex < 0) {
+            if (JobManager.DEBUG_LOCKS)
+                Stdout.formatln("[lockReleased] Thread {} already released lock {}", owner.name(), lock); //$NON-NLS-1$ //$NON-NLS-2$
+            return;
+        }
+        //if this lock was suspended, set it to NO_STATE
+        if ((cast(ILock)lock ) && (graph[threadIndex][lockIndex] is WAITING_FOR_LOCK)) {
+            graph[threadIndex][lockIndex] = NO_STATE;
+            return;
+        }
+        //release all locks that conflict with the given lock
+        //or release all rules that are owned by the given thread, if we are releasing a rule
+        for (int j = 0; j < graph[threadIndex].length; j++) {
+            if ((lock.isConflicting(cast(ISchedulingRule) locks.get(j))) || (!(cast(ILock)lock ) && !(cast(ILock)locks.get(j)) && (graph[threadIndex][j] > NO_STATE))) {
+                if (graph[threadIndex][j] is NO_STATE) {
+                    if (JobManager.DEBUG_LOCKS)
+                        Stdout.formatln("[lockReleased] More releases than acquires for thread {} and lock {}", owner.name(), lock); //$NON-NLS-1$ //$NON-NLS-2$
+                } else {
+                    graph[threadIndex][j]--;
+                }
+            }
+        }
+        //if this thread just released the given lock, try to simplify the graph
+        if (graph[threadIndex][lockIndex] is NO_STATE)
+            reduceGraph(threadIndex, lock);
+    }
+
+    /**
+     * The given scheduling rule is no longer used because the job that invoked it is done.
+     * Release this rule regardless of how many times it was acquired.
+     */
+    void lockReleasedCompletely(Thread owner, ISchedulingRule rule) {
+        int ruleIndex = indexOf(rule, false);
+        int threadIndex = indexOf(owner, false);
+        //need to make sure that the given thread and rule were not already removed from the graph
+        if (threadIndex < 0) {
+            if (JobManager.DEBUG_LOCKS)
+                Stdout.formatln("[lockReleasedCompletely] Lock {} was already released by thread {}", rule, owner.name()); //$NON-NLS-1$ //$NON-NLS-2$
+            return;
+        }
+        if (ruleIndex < 0) {
+            if (JobManager.DEBUG_LOCKS)
+                Stdout.formatln("[lockReleasedCompletely] Thread {} already released lock {}", owner.name(), rule); //$NON-NLS-1$ //$NON-NLS-2$
+            return;
+        }
+        /**
+         * set all rules that are owned by the given thread to NO_STATE
+         * (not just rules that conflict with the rule we are releasing)
+         * if we are releasing a lock, then only update the one entry for the lock
+         */
+        for (int j = 0; j < graph[threadIndex].length; j++) {
+            if (!(cast(ILock)locks.get(j) ) && (graph[threadIndex][j] > NO_STATE))
+                graph[threadIndex][j] = NO_STATE;
+        }
+        reduceGraph(threadIndex, rule);
+    }
+
+    /**
+     * The given thread could not get the given lock and is waiting for it.
+     * Update the graph.
+     */
+    Deadlock lockWaitStart(Thread client, ISchedulingRule lock) {
+        setToWait(client, lock, false);
+        int lockIndex = indexOf(lock, false);
+        int[] temp = new int[lockThreads.size()];
+        //check if the addition of the waiting thread caused deadlock
+        if (!checkWaitCycles(temp, lockIndex))
+            return null;
+        //there is a deadlock in the graph
+        Thread[] threads = getThreadsInDeadlock(client);
+        Thread candidate = resolutionCandidate(threads);
+        ISchedulingRule[] locksToSuspend = realLocksForThread(candidate);
+        Deadlock deadlock = new Deadlock(threads, locksToSuspend, candidate);
+        //find a thread whose locks can be suspended to resolve the deadlock
+        if (JobManager.DEBUG_LOCKS)
+            reportDeadlock(deadlock);
+        if (JobManager.DEBUG_DEADLOCK)
+            throw new IllegalStateException(Format("Deadlock detected. Caused by thread {}.", client.name())); //$NON-NLS-1$
+        // Update the graph to indicate that the locks will now be suspended.
+        // To indicate that the lock will be suspended, we set the thread to wait for the lock.
+        // When the lock is forced to be released, the entry will be cleared.
+        for (int i = 0; i < locksToSuspend.length; i++)
+            setToWait(deadlock.getCandidate(), locksToSuspend[i], true);
+        return deadlock;
+    }
+
+    /**
+     * The given thread has stopped waiting for the given lock.
+     * Update the graph.
+     */
+    void lockWaitStop(Thread owner, ISchedulingRule lock) {
+        int lockIndex = indexOf(lock, false);
+        int threadIndex = indexOf(owner, false);
+        //make sure the thread and lock exist in the graph
+        if (threadIndex < 0) {
+            if (JobManager.DEBUG_LOCKS)
+                Stdout.formatln("Thread {} was already removed.", owner.name() ); //$NON-NLS-1$ //$NON-NLS-2$
+            return;
+        }
+        if (lockIndex < 0) {
+            if (JobManager.DEBUG_LOCKS)
+                Stdout.formatln("Lock {} was already removed.", lock ); //$NON-NLS-1$ //$NON-NLS-2$
+            return;
+        }
+        if (graph[threadIndex][lockIndex] !is WAITING_FOR_LOCK)
+            Assert.isTrue(false, Format("Thread {} was not waiting for lock {} so it could not time out.", owner.name(), (cast(Object)lock).toString())); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+        graph[threadIndex][lockIndex] = NO_STATE;
+        reduceGraph(threadIndex, lock);
+    }
+
+    /**
+     * Returns true IFF the given thread owns a single lock
+     */
+    private bool ownsLocks(Thread cause) {
+        int threadIndex = indexOf(cause, false);
+        for (int j = 0; j < graph[threadIndex].length; j++) {
+            if (graph[threadIndex][j] > NO_STATE)
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true IFF the given thread owns a single real lock.
+     * A real lock is a lock that can be suspended.
+     */
+    private bool ownsRealLocks(Thread owner) {
+        int threadIndex = indexOf(owner, false);
+        for (int j = 0; j < graph[threadIndex].length; j++) {
+            if (graph[threadIndex][j] > NO_STATE) {
+                Object lock = locks.get(j);
+                if (cast(ILock)lock )
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return true IFF this thread owns rule locks (i.e. implicit locks which
+     * cannot be suspended)
+     */
+    private bool ownsRuleLocks(Thread owner) {
+        int threadIndex = indexOf(owner, false);
+        for (int j = 0; j < graph[threadIndex].length; j++) {
+            if (graph[threadIndex][j] > NO_STATE) {
+                Object lock = locks.get(j);
+                if (!(cast(ILock)lock ))
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns an array of real locks that are owned by the given thread.
+     * Real locks are locks that implement the ILock interface and can be suspended.
+     */
+    private ISchedulingRule[] realLocksForThread(Thread owner) {
+        int threadIndex = indexOf(owner, false);
+        ArrayList ownedLocks = new ArrayList(1);
+        for (int j = 0; j < graph[threadIndex].length; j++) {
+            if ((graph[threadIndex][j] > NO_STATE) && (cast(ILock)locks.get(j) ))
+                ownedLocks.add(locks.get(j));
+        }
+        if (ownedLocks.size() is 0)
+            Assert.isLegal(false, "A thread with no real locks was chosen to resolve deadlock."); //$NON-NLS-1$
+        return arraycast!(ISchedulingRule)( ownedLocks.toArray());
+    }
+
+    /**
+     * The matrix has been simplified. Check if any unnecessary rows or columns
+     * can be removed.
+     */
+    private void reduceGraph(int row, ISchedulingRule lock) {
+        int numLocks = locks.size();
+        bool[] emptyColumns = new bool[numLocks];
+
+        /**
+         * find all columns that could possibly be empty
+         * (consist of locks which conflict with the given lock, or of locks which are rules)
+         */
+        for (int j = 0; j < numLocks; j++) {
+            if ((lock.isConflicting(cast(ISchedulingRule) locks.get(j))) || !(cast(ILock)locks.get(j)))
+                emptyColumns[j] = true;
+        }
+
+        bool rowEmpty = true;
+        int numEmpty = 0;
+        //check if the given row is empty
+        for (int j = 0; j < graph[row].length; j++) {
+            if (graph[row][j] !is NO_STATE) {
+                rowEmpty = false;
+                break;
+            }
+        }
+        /**
+         * Check if the possibly empty columns are actually empty.
+         * If a column is actually empty, remove the corresponding lock from the list of locks
+         * Start at the last column so that when locks are removed from the list,
+         * the index of the remaining locks is unchanged. Store the number of empty columns.
+         */
+        for (int j = emptyColumns.length - 1; j >= 0; j--) {
+            for (int i = 0; i < graph.length; i++) {
+                if (emptyColumns[j] && (graph[i][j] !is NO_STATE)) {
+                    emptyColumns[j] = false;
+                    break;
+                }
+            }
+            if (emptyColumns[j]) {
+                locks.remove(j);
+                numEmpty++;
+            }
+        }
+        //if no columns or rows are empty, return right away
+        if ((numEmpty is 0) && (!rowEmpty))
+            return;
+
+        if (rowEmpty)
+            lockThreads.remove(row);
+
+        //new graph (the list of locks and the list of threads are already updated)
+        final int numThreads = lockThreads.size();
+        numLocks = locks.size();
+        //optimize empty graph case
+        if (numThreads is 0 && numLocks is 0) {
+            graph = EMPTY_MATRIX;
+            return;
+        }
+        int[][] tempGraph = new int[][](numThreads,numLocks);
+
+        //the number of rows we need to skip to get the correct entry from the old graph
+        int numRowsSkipped = 0;
+        for (int i = 0; i < graph.length - numRowsSkipped; i++) {
+            if ((i is row) && rowEmpty) {
+                numRowsSkipped++;
+                //check if we need to skip the last row
+                if (i >= graph.length - numRowsSkipped)
+                    break;
+            }
+            //the number of columns we need to skip to get the correct entry from the old graph
+            //needs to be reset for every new row
+            int numColsSkipped = 0;
+            for (int j = 0; j < graph[i].length - numColsSkipped; j++) {
+                while (emptyColumns[j + numColsSkipped]) {
+                    numColsSkipped++;
+                    //check if we need to skip the last column
+                    if (j >= graph[i].length - numColsSkipped)
+                        break;
+                }
+                //need to break out of the outer loop
+                if (j >= graph[i].length - numColsSkipped)
+                    break;
+                tempGraph[i][j] = graph[i + numRowsSkipped][j + numColsSkipped];
+            }
+        }
+        graph = tempGraph;
+        Assert.isTrue(numThreads is graph.length, "Rows and threads don't match."); //$NON-NLS-1$
+        Assert.isTrue(numLocks is ((graph.length > 0) ? graph[0].length : 0), "Columns and locks don't match."); //$NON-NLS-1$
+    }
+
+    /**
+     * Adds a 'deadlock detected' message to the log with a stack trace.
+     */
+    private void reportDeadlock(Deadlock deadlock) {
+        String msg = "Deadlock detected. All locks owned by thread " ~ deadlock.getCandidate().name() ~ " will be suspended."; //$NON-NLS-1$ //$NON-NLS-2$
+        MultiStatus main = new MultiStatus(JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, msg, new IllegalStateException());
+        Thread[] threads = deadlock.getThreads();
+        for (int i = 0; i < threads.length; i++) {
+            Object[] ownedLocks = getOwnedLocks(threads[i]);
+            Object waitLock = getWaitingLock(threads[i]);
+            StringBuffer buf = new StringBuffer("Thread "); //$NON-NLS-1$
+            buf.append(threads[i].name());
+            buf.append(" has locks: "); //$NON-NLS-1$
+            for (int j = 0; j < ownedLocks.length; j++) {
+                buf.append(Format("{}",ownedLocks[j]));
+                buf.append((j < ownedLocks.length - 1) ? ", " : " "); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+            buf.append("and is waiting for lock "); //$NON-NLS-1$
+            buf.append(Format("{}",waitLock));
+            Status child = new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, buf.toString(), null);
+            main.add(child);
+        }
+        RuntimeLog.log(main);
+    }
+
+    /**
+     * The number of threads/locks in the graph has changed. Update the
+     * underlying matrix.
+     */
+    private void resizeGraph() {
+        // a new row and/or a new column was added to the graph.
+        // since new rows/columns are always added to the end, just transfer
+        // old entries to the new graph, with the same indices.
+        final int newRows = lockThreads.size();
+        final int newCols = locks.size();
+        //optimize 0x0 and 1x1 matrices
+        if (newRows is 0 && newCols is 0) {
+            graph = EMPTY_MATRIX;
+            return;
+        }
+        int[][] tempGraph = new int[][](newRows,newCols);
+        for (int i = 0; i < graph.length; i++)
+            System.arraycopy(graph[i], 0, tempGraph[i], 0, graph[i].length);
+        graph = tempGraph;
+        resize = false;
+    }
+
+    /**
+     * Get the thread whose locks can be suspended. (i.e. all locks it owns are
+     * actual locks and not rules). Return the first thread in the array by default.
+     */
+    private Thread resolutionCandidate(Thread[] candidates) {
+        //first look for a candidate that has no scheduling rules
+        for (int i = 0; i < candidates.length; i++) {
+            if (!ownsRuleLocks(candidates[i]))
+                return candidates[i];
+        }
+        //next look for any candidate with a real lock (a lock that can be suspended)
+        for (int i = 0; i < candidates.length; i++) {
+            if (ownsRealLocks(candidates[i]))
+                return candidates[i];
+        }
+        //unnecessary, return the first entry in the array by default
+        return candidates[0];
+    }
+
+    /**
+     * The given thread is waiting for the given lock. Update the graph.
+     */
+    private void setToWait(Thread owner, ISchedulingRule lock, bool suspend) {
+        bool needTransfer = false;
+        /**
+         * if we are adding an entry where a thread is waiting on a scheduling rule,
+         * then we need to transfer all positive entries for a conflicting rule to the
+         * newly added rule in order to synchronize the graph.
+         */
+        if (!suspend && !(cast(ILock)lock))
+            needTransfer = true;
+        int lockIndex = indexOf(lock, !suspend);
+        int threadIndex = indexOf(owner, !suspend);
+        if (resize)
+            resizeGraph();
+
+        graph[threadIndex][lockIndex] = WAITING_FOR_LOCK;
+        if (needTransfer)
+            fillPresentEntries(lock, lockIndex);
+    }
+
+    /**
+     * Prints out the current matrix to standard output.
+     * Only used for debugging.
+     */
+    public String toDebugString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append(" :: \n"); //$NON-NLS-1$
+        for (int j = 0; j < locks.size(); j++) {
+            sb.append(" ");
+            sb.append( locks.get(j).toString );
+            sb.append(",");
+        }
+        sb.append("\n");
+        for (int i = 0; i < graph.length; i++) {
+            sb.append(" ");
+            sb.append( (cast(Thread) lockThreads.get(i)).name() );
+            sb.append(" : ");
+            for (int j = 0; j < graph[i].length; j++) {
+                sb.append(" ");
+                sb.append(Integer.toString(graph[i][j])); //$NON-NLS-1$
+                sb.append(",");
+            }
+            sb.append("\n");
+        }
+        sb.append("-------\n"); //$NON-NLS-1$
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/ImplicitJobs.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,279 @@
+/*******************************************************************************
+ * 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.ImplicitJobs;
+
+import tango.text.convert.Format;
+import tango.core.Thread;
+import tango.io.Stdout;
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.core.internal.runtime.RuntimeLog;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.Status;
+import dwtx.core.runtime.jobs.ISchedulingRule;
+import dwtx.core.runtime.jobs.Job;
+
+import dwtx.core.internal.jobs.JobManager;
+import dwtx.core.internal.jobs.ThreadJob;
+import dwtx.core.internal.jobs.InternalJob;
+
+/**
+ * Implicit jobs are jobs that are running by virtue of a JobManager.begin/end
+ * pair. They act like normal jobs, except they are tied to an arbitrary thread
+ * of the client's choosing, and they can be nested.
+ */
+class ImplicitJobs {
+    /**
+     * Cached unused instance that can be reused
+     */
+    private ThreadJob jobCache = null;
+    protected JobManager manager;
+
+    /**
+     * Set of suspended scheduling rules.
+     */
+    private const Set suspendedRules;
+
+    /**
+     * Maps (Thread->ThreadJob), threads to the currently running job for that
+     * thread.
+     */
+    private final Map threadJobs;
+
+    this(JobManager manager) {
+        this.manager = manager;
+        suspendedRules = new HashSet(20);
+        threadJobs = new HashMap(20);
+    }
+
+    /* (Non-javadoc)
+     * @see IJobManager#beginRule
+     */
+    void begin(ISchedulingRule rule, IProgressMonitor monitor, bool suspend) {
+        if (JobManager.DEBUG_BEGIN_END)
+            JobManager.debug_(Format("Begin rule: {}", rule)); //$NON-NLS-1$
+        final Thread getThis = Thread.getThis();
+        ThreadJob threadJob;
+        synchronized (this) {
+            threadJob = cast(ThreadJob) threadJobs.get(getThis);
+            if (threadJob !is null) {
+                //nested rule, just push on stack and return
+                threadJob.push(rule);
+                return;
+            }
+            //no need to schedule a thread job for a null rule
+            if (rule is null)
+                return;
+            //create a thread job for this thread, use the rule from the real job if it has one
+            Job realJob = manager.currentJob();
+            if (realJob !is null && realJob.getRule() !is null)
+                threadJob = newThreadJob(realJob.getRule());
+            else {
+                threadJob = newThreadJob(rule);
+                threadJob.acquireRule = true;
+            }
+            //don't acquire rule if it is a suspended rule
+            if (isSuspended(rule))
+                threadJob.acquireRule = false;
+            //indicate if it is a system job to ensure isBlocking works correctly
+            threadJob.setRealJob(realJob);
+            threadJob.setThread(getThis);
+        }
+        try {
+            threadJob.push(rule);
+            //join the thread job outside sync block
+            if (threadJob.acquireRule) {
+                //no need to re-acquire any locks because the thread did not wait to get this lock
+                if (manager.runNow_package(threadJob))
+                    manager.getLockManager().addLockThread(Thread.getThis(), rule);
+                else
+                    threadJob = threadJob.joinRun(monitor);
+            }
+        } finally {
+            //remember this thread job  - only do this
+            //after the rule is acquired because it is ok for this thread to acquire
+            //and release other rules while waiting.
+            synchronized (this) {
+                threadJobs.put(getThis, threadJob);
+                if (suspend)
+                    suspendedRules.add(cast(Object)rule);
+            }
+            if (threadJob.isBlocked) {
+                threadJob.isBlocked = false;
+                manager.reportUnblocked(monitor);
+            }
+        }
+    }
+
+    /* (Non-javadoc)
+     * @see IJobManager#endRule
+     */
+    synchronized void end(ISchedulingRule rule, bool resume) {
+        if (JobManager.DEBUG_BEGIN_END)
+            JobManager.debug_(Format("End rule: {}", rule)); //$NON-NLS-1$
+        ThreadJob threadJob = cast(ThreadJob) threadJobs.get(Thread.getThis());
+        if (threadJob is null)
+            Assert.isLegal(rule is null, Format("endRule without matching beginRule: {}", rule)); //$NON-NLS-1$
+        else if (threadJob.pop(rule)) {
+            endThreadJob(threadJob, resume);
+        }
+    }
+
+    /**
+     * Called when a worker thread has finished running a job. At this
+     * point, the worker thread must not own any scheduling rules
+     * @param lastJob The last job to run in this thread
+     */
+    void endJob(InternalJob lastJob) {
+        final Thread getThis = Thread.getThis();
+        IStatus error;
+        synchronized (this) {
+            ThreadJob threadJob = cast(ThreadJob) threadJobs.get(getThis);
+            if (threadJob is null) {
+                if (lastJob.getRule() !is null)
+                    notifyWaitingThreadJobs();
+                return;
+            }
+            String msg = Format("Worker thread ended job: {}, but still holds rule: {}", lastJob, threadJob ); //$NON-NLS-1$ //$NON-NLS-2$
+            error = new Status(IStatus.ERROR, JobManager.PI_JOBS, 1, msg, null);
+            //end the thread job
+            endThreadJob(threadJob, false);
+        }
+        try {
+            RuntimeLog.log(error);
+        } catch (RuntimeException e) {
+            //failed to log, so print to console instead
+            Stderr.formatln("{}", error.getMessage());
+        }
+    }
+
+    private void endThreadJob(ThreadJob threadJob, bool resume) {
+        Thread getThis = Thread.getThis();
+        //clean up when last rule scope exits
+        threadJobs.remove(getThis);
+        ISchedulingRule rule = threadJob.getRule();
+        if (resume && rule !is null)
+            suspendedRules.remove(cast(Object)rule);
+        //if this job had a rule, then we are essentially releasing a lock
+        //note it is safe to do this even if the acquire was aborted
+        if (threadJob.acquireRule) {
+            manager.getLockManager().removeLockThread(getThis, rule);
+            notifyWaitingThreadJobs();
+        }
+        //if the job was started, we need to notify job manager to end it
+        if (threadJob.isRunning())
+            manager.endJob_package(threadJob, Status.OK_STATUS, false);
+        recycle(threadJob);
+    }
+
+    /**
+     * Returns true if this rule has been suspended, and false otherwise.
+     */
+    private bool isSuspended(ISchedulingRule rule) {
+        if (suspendedRules.size() is 0)
+            return false;
+        for (Iterator it = suspendedRules.iterator(); it.hasNext();)
+            if ((cast(ISchedulingRule) it.next()).contains(rule))
+                return true;
+        return false;
+    }
+
+    /**
+     * Returns a new or reused ThreadJob instance.
+     */
+    private ThreadJob newThreadJob(ISchedulingRule rule) {
+        if (jobCache !is null) {
+            ThreadJob job = jobCache;
+            job.setRule(rule);
+            job.acquireRule = job.isRunning_ = false;
+            job.realJob = null;
+            jobCache = null;
+            return job;
+        }
+        return new ThreadJob(manager, rule);
+    }
+
+    /**
+     * A job has just finished that was holding a scheduling rule, and the
+     * scheduling rule is now free.  Wake any blocked thread jobs so they can
+     * compete for the newly freed lock
+     */
+    private void notifyWaitingThreadJobs() {
+        synchronized (ThreadJob.mutex) {
+            ThreadJob.condition.notifyAll();
+        }
+    }
+
+    /**
+     * Indicates that a thread job is no longer in use and can be reused.
+     */
+    private void recycle(ThreadJob job) {
+        if (jobCache is null && job.recycle())
+            jobCache = job;
+    }
+
+    /**
+     * Implements IJobManager#resume(ISchedulingRule)
+     * @param rule
+     */
+    void resume(ISchedulingRule rule) {
+        //resume happens as a consequence of freeing the last rule in the stack
+        end(rule, true);
+        if (JobManager.DEBUG_BEGIN_END)
+            JobManager.debug_(Format("Resume rule: {}", rule)); //$NON-NLS-1$
+    }
+
+    /**
+     * Implements IJobManager#suspend(ISchedulingRule, IProgressMonitor)
+     * @param rule
+     * @param monitor
+     */
+    void suspend(ISchedulingRule rule, IProgressMonitor monitor) {
+        if (JobManager.DEBUG_BEGIN_END)
+            JobManager.debug_(Format("Suspend rule: {}", rule)); //$NON-NLS-1$
+        //the suspend job will be remembered once the rule is acquired
+        begin(rule, monitor, true);
+    }
+
+    /**
+     * Implements IJobManager#transferRule(ISchedulingRule, Thread)
+     */
+    synchronized void transfer(ISchedulingRule rule, Thread destinationThread) {
+        //nothing to do for null
+        if (rule is null)
+            return;
+        Thread getThis = Thread.getThis();
+        //nothing to do if transferring to the same thread
+        if (getThis is destinationThread)
+            return;
+        //ensure destination thread doesn't already have a rule
+        ThreadJob job = cast(ThreadJob) threadJobs.get(destinationThread);
+        Assert.isLegal(job is null);
+        //ensure calling thread owns the job being transferred
+        job = cast(ThreadJob) threadJobs.get(getThis);
+        Assert.isNotNull(job);
+        Assert.isLegal(job.getRule() is rule);
+        //transfer the thread job without ending it
+        job.setThread(destinationThread);
+        threadJobs.remove(getThis);
+        threadJobs.put(destinationThread, job);
+        //transfer lock
+        if (job.acquireRule) {
+            manager.getLockManager().removeLockThread(getThis, rule);
+            manager.getLockManager().addLockThread(destinationThread, rule);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/InternalJob.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,628 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2007 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.InternalJob;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.ListenerList;
+import dwtx.core.runtime.PlatformObject;
+import dwtx.core.runtime.QualifiedName;
+import dwtx.core.runtime.jobs.IJobChangeListener;
+import dwtx.core.runtime.jobs.ISchedulingRule;
+import dwtx.core.runtime.jobs.Job;
+import dwtx.core.runtime.jobs.MultiRule;
+
+import dwtx.core.internal.jobs.JobManager;
+import dwtx.core.internal.jobs.ObjectMap;
+
+import tango.core.Thread;
+import tango.text.convert.Format;
+
+/**
+ * Internal implementation class for jobs. Clients must not implement this class
+ * directly.  All jobs must be subclasses of the API <code>dwtx.core.runtime.jobs.Job</code> class.
+ */
+public abstract class InternalJob : PlatformObject, Comparable {
+    /**
+     * Job state code (value 16) indicating that a job has been removed from
+     * the wait queue and is about to start running. From an API point of view,
+     * this is the same as RUNNING.
+     */
+    static const int ABOUT_TO_RUN = 0x10;
+
+    /**
+     * Job state code (value 32) indicating that a job has passed scheduling
+     * precondition checks and is about to be added to the wait queue. From an API point of view,
+     * this is the same as WAITING.
+     */
+    static const int ABOUT_TO_SCHEDULE = 0x20;
+    /**
+     * Job state code (value 8) indicating that a job is blocked by another currently
+     * running job.  From an API point of view, this is the same as WAITING.
+     */
+    static const int BLOCKED = 0x08;
+
+    //flag mask bits
+    private static const int M_STATE = 0xFF;
+    private static const int M_SYSTEM = 0x0100;
+    private static const int M_USER = 0x0200;
+
+    /*
+     * flag on a job indicating that it was about to run, but has been canceled
+     */
+    private static const int M_ABOUT_TO_RUN_CANCELED = 0x0400;
+
+    private static JobManager manager_;
+    protected static JobManager manager(){
+        if( manager_ is null ){
+            synchronized( InternalJob.classinfo ){
+                if( manager_ is null ){
+                    manager_ = JobManager.getInstance();
+                }
+            }
+        }
+        return manager_;
+    }
+    private static int nextJobNumber = 0;
+
+    /**
+     * Start time constant indicating a job should be started at
+     * a time in the infinite future, causing it to sleep forever.
+     */
+    static const long T_INFINITE = Long.MAX_VALUE;
+    /**
+     * Start time constant indicating that the job has no start time.
+     */
+    static const long T_NONE = -1;
+
+    private /+volatile+/ int flags = Job.NONE;
+    private const int jobNumber;
+    private ListenerList listeners = null;
+    private IProgressMonitor monitor;
+    private String name;
+    /**
+     * The job ahead of me in a queue or list.
+     */
+    private InternalJob next_;
+    /**
+     * The job behind me in a queue or list.
+     */
+    private InternalJob previous_;
+    private int priority = Job.LONG;
+    /**
+     * Arbitrary properties (key,value) pairs, attached
+     * to a job instance by a third party.
+     */
+    private ObjectMap properties;
+    private IStatus result;
+    private ISchedulingRule schedulingRule;
+    /**
+     * If the job is waiting, this represents the time the job should start by.
+     * If this job is sleeping, this represents the time the job should wake up.
+     * If this job is running, this represents the delay automatic rescheduling,
+     * or -1 if the job should not be rescheduled.
+     */
+    private long startTime;
+
+    /**
+     * Stamp added when a job is added to the wait queue. Used to ensure
+     * jobs in the wait queue maintain their insertion order even if they are
+     * removed from the wait queue temporarily while blocked
+     */
+    private long waitQueueStamp = T_NONE;
+
+    /*
+     * The thread that is currently running this job
+     */
+    private /+volatile+/ Thread thread = null;
+
+    protected this(String name) {
+        Assert.isNotNull(name);
+        jobNumber = nextJobNumber++;
+        this.name = name;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#addJobListener(IJobChangeListener)
+     */
+    protected void addJobChangeListener(IJobChangeListener listener) {
+        if (listeners is null)
+            listeners = new ListenerList(ListenerList.IDENTITY);
+        listeners.add(cast(Object)listener);
+    }
+    package void addJobChangeListener_package(IJobChangeListener listener) {
+        addJobChangeListener(listener);
+    }
+
+    /**
+     * Adds an entry at the end of the list of which this item is the head.
+     */
+    final void addLast(InternalJob entry) {
+        InternalJob last = this;
+        //find the end of the queue
+        while (last.previous_ !is null)
+            last = last.previous_;
+        //add the new entry to the end of the queue
+        last.previous_ = entry;
+        entry.next_ = last;
+        entry.previous_ = null;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#belongsTo(Object)
+     */
+    protected bool belongsTo(Object family) {
+        return false;
+    }
+    package bool belongsTo_package(Object family) {
+        return belongsTo(family);
+    }
+
+    /* (non-Javadoc)
+     * @see Job#cancel()
+     */
+    protected package bool cancel() {
+        return manager.cancel_package(this);
+    }
+
+    /* (non-Javadoc)
+     * @see Job#canceling()
+     */
+    protected package void canceling() {
+        //default implementation does nothing
+    }
+
+    /* (on-Javadoc)
+     * @see java.lang.Comparable#compareTo(java.lang.Object)
+     */
+    public final int compareTo(Object otherJob) {
+        return (cast(InternalJob) otherJob).startTime >= startTime ? 1 : -1;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#done(IStatus)
+     */
+    protected void done(IStatus endResult) {
+        manager.endJob_package(this, endResult, true);
+    }
+
+    /**
+     * Returns the job listeners that are only listening to this job.  Returns
+     * <code>null</code> if this job has no listeners.
+     */
+    final ListenerList getListeners() {
+        return listeners;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#getName()
+     */
+    protected String getName() {
+        return name;
+    }
+    package String getName_package() {
+        return name;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#getPriority()
+     */
+    protected package int getPriority() {
+        return priority;
+    }
+
+    /**
+     * Returns the job's progress monitor, or null if it is not running.
+     */
+    final IProgressMonitor getProgressMonitor() {
+        return monitor;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#getProperty
+     */
+    protected Object getProperty(QualifiedName key) {
+        // thread safety: (Concurrency001 - copy on write)
+        Map temp = properties;
+        if (temp is null)
+            return null;
+        return temp.get(key);
+    }
+
+    /* (non-Javadoc)
+     * @see Job#getResult
+     */
+    protected IStatus getResult() {
+        return result;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#getRule
+     */
+    protected package ISchedulingRule getRule() {
+        return schedulingRule;
+    }
+    package ISchedulingRule getRule_package() {
+        return getRule();
+    }
+
+    /**
+     * Returns the time that this job should be started, awakened, or
+     * rescheduled, depending on the current state.
+     * @return time in milliseconds
+     */
+    final long getStartTime() {
+        return startTime;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#getState()
+     */
+    protected int getState() {
+        int state = flags & M_STATE;
+        switch (state) {
+            //blocked state is equivalent to waiting state for clients
+            case BLOCKED :
+                return Job.WAITING;
+            case ABOUT_TO_RUN :
+                return Job.RUNNING;
+            case ABOUT_TO_SCHEDULE :
+                return Job.WAITING;
+            default :
+                return state;
+        }
+    }
+    package int getState_package() {
+        return getState();
+    }
+
+    /* (non-javadoc)
+     * @see Job.getThread
+     */
+    protected Thread getThread() {
+        return thread;
+    }
+    package Thread getThread_package() {
+        return getThread();
+    }
+
+    /**
+     * Returns the raw job state, including internal states no exposed as API.
+     */
+    final int internalGetState() {
+        return flags & M_STATE;
+    }
+
+    /**
+     * Must be called from JobManager#setPriority
+     */
+    final void internalSetPriority(int newPriority) {
+        this.priority = newPriority;
+    }
+
+    /**
+     * Must be called from JobManager#setRule
+     */
+    final void internalSetRule(ISchedulingRule rule) {
+        this.schedulingRule = rule;
+    }
+
+    /**
+     * Must be called from JobManager#changeState
+     */
+    final void internalSetState(int i) {
+        flags = (flags & ~M_STATE) | i;
+    }
+
+    /**
+     * Returns whether this job was canceled when it was about to run
+     */
+    final bool isAboutToRunCanceled() {
+        return (flags & M_ABOUT_TO_RUN_CANCELED) !is 0;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#isBlocking()
+     */
+    protected bool isBlocking() {
+        return manager.isBlocking_package(this);
+    }
+
+    /**
+     * Returns true if this job conflicts with the given job, and false otherwise.
+     */
+    final bool isConflicting(InternalJob otherJob) {
+        ISchedulingRule otherRule = otherJob.getRule();
+        if (schedulingRule is null || otherRule is null)
+            return false;
+        //if one of the rules is a compound rule, it must be asked the question.
+        if (schedulingRule.classinfo is MultiRule.classinfo)
+            return schedulingRule.isConflicting(otherRule);
+        return otherRule.isConflicting(schedulingRule);
+    }
+
+    /* (non-javadoc)
+     * @see Job.isSystem()
+     */
+    protected bool isSystem() {
+        return (flags & M_SYSTEM) !is 0;
+    }
+    package bool isSystem_package() {
+        return isSystem();
+    }
+
+    /* (non-javadoc)
+     * @see Job.isUser()
+     */
+    protected bool isUser() {
+        return (flags & M_USER) !is 0;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#join()
+     */
+    protected void join() {
+        manager.join_package(this);
+    }
+
+    /**
+     * Returns the next_ entry (ahead of this one) in the list, or null if there is no next_ entry
+     */
+    final InternalJob next() {
+        return next_;
+    }
+
+    /**
+     * Returns the previous_ entry (behind this one) in the list, or null if there is no previous_ entry
+     */
+    final InternalJob previous() {
+        return previous_;
+    }
+
+    /**
+     * Removes this entry from any list it belongs to.  Returns the receiver.
+     */
+    final InternalJob remove() {
+        if (next_ !is null)
+            next_.setPrevious(previous_);
+        if (previous_ !is null)
+            previous_.setNext(next_);
+        next_ = previous_ = null;
+        return this;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#removeJobListener(IJobChangeListener)
+     */
+    protected void removeJobChangeListener(IJobChangeListener listener) {
+        if (listeners !is null)
+            listeners.remove(cast(Object)listener);
+    }
+    package void removeJobChangeListener_package(IJobChangeListener listener) {
+        removeJobChangeListener(listener);
+    }
+
+    /* (non-Javadoc)
+     * @see Job#run(IProgressMonitor)
+     */
+    protected abstract IStatus run(IProgressMonitor progressMonitor);
+    package IStatus run_package(IProgressMonitor progressMonitor){
+        return run(progressMonitor);
+    }
+
+    /* (non-Javadoc)
+     * @see Job#schedule(long)
+     */
+    protected void schedule(long delay) {
+        if (shouldSchedule())
+            manager.schedule_package(this, delay, false);
+    }
+    package void schedule_package(long delay) {
+        schedule(delay);
+    }
+
+    /**
+     * Sets whether this job was canceled when it was about to run
+     */
+    final void setAboutToRunCanceled(bool value) {
+        flags = value ? flags | M_ABOUT_TO_RUN_CANCELED : flags & ~M_ABOUT_TO_RUN_CANCELED;
+
+    }
+
+    /* (non-Javadoc)
+     * @see Job#setName(String)
+     */
+    protected void setName(String name) {
+        Assert.isNotNull(name);
+        this.name = name;
+    }
+
+    /**
+     * Sets the next_ entry in this linked list of jobs.
+     * @param entry
+     */
+    final void setNext(InternalJob entry) {
+        this.next_ = entry;
+    }
+
+    /**
+     * Sets the previous_ entry in this linked list of jobs.
+     * @param entry
+     */
+    final void setPrevious(InternalJob entry) {
+        this.previous_ = entry;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#setPriority(int)
+     */
+    protected void setPriority(int newPriority) {
+        switch (newPriority) {
+            case Job.INTERACTIVE :
+            case Job.SHORT :
+            case Job.LONG :
+            case Job.BUILD :
+            case Job.DECORATE :
+                manager.setPriority_package(this, newPriority);
+                break;
+            default :
+                throw new IllegalArgumentException(Integer.toString(newPriority));
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see Job#setProgressGroup(IProgressMonitor, int)
+     */
+    protected void setProgressGroup(IProgressMonitor group, int ticks) {
+        Assert.isNotNull(cast(Object)group);
+        IProgressMonitor pm = manager.createMonitor_package(this, group, ticks);
+        if (pm !is null)
+            setProgressMonitor(pm);
+    }
+
+    /**
+     * Sets the progress monitor to use for the next_ execution of this job,
+     * or for clearing the monitor when a job completes.
+     * @param monitor a progress monitor
+     */
+    final void setProgressMonitor(IProgressMonitor monitor) {
+        this.monitor = monitor;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#setProperty(QualifiedName,Object)
+     */
+    protected void setProperty(QualifiedName key, Object value) {
+        // thread safety: (Concurrency001 - copy on write)
+        if (value is null) {
+            if (properties is null)
+                return;
+            ObjectMap temp = cast(ObjectMap) properties.clone();
+            temp.remove(key);
+            if (temp.isEmpty())
+                properties = null;
+            else
+                properties = temp;
+        } else {
+            ObjectMap temp = properties;
+            if (temp is null)
+                temp = new ObjectMap(5);
+            else
+                temp = cast(ObjectMap) properties.clone();
+            temp.put(key, value);
+            properties = temp;
+        }
+    }
+
+    /**
+     * Sets or clears the result of an execution of this job.
+     * @param result a result status, or <code>null</code>
+     */
+    final void setResult(IStatus result) {
+        this.result = result;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#setRule(ISchedulingRule)
+     */
+    protected void setRule(ISchedulingRule rule) {
+        manager.setRule(this, rule);
+    }
+
+    /**
+     * Sets a time to start, wake up, or schedule this job,
+     * depending on the current state
+     * @param time a time in milliseconds
+     */
+    final void setStartTime(long time) {
+        startTime = time;
+    }
+
+    /* (non-javadoc)
+     * @see Job.setSystem
+     */
+    protected void setSystem(bool value) {
+        if (getState() !is Job.NONE)
+            throw new IllegalStateException();
+        flags = value ? flags | M_SYSTEM : flags & ~M_SYSTEM;
+    }
+
+    /* (non-javadoc)
+     * @see Job.setThread
+     */
+    protected void setThread(Thread thread) {
+        this.thread = thread;
+    }
+    package void setThread_package(Thread thread) {
+        setThread(thread);
+    }
+
+    /* (non-javadoc)
+     * @see Job.setUser
+     */
+    protected void setUser(bool value) {
+        if (getState() !is Job.NONE)
+            throw new IllegalStateException();
+        flags = value ? flags | M_USER : flags & ~M_USER;
+    }
+
+    /* (Non-javadoc)
+     * @see Job#shouldSchedule
+     */
+    protected bool shouldSchedule() {
+        return true;
+    }
+    package bool shouldSchedule_package() {
+        return shouldSchedule();
+    }
+
+    /* (non-Javadoc)
+     * @see Job#sleep()
+     */
+    protected bool sleep() {
+        return manager.sleep_package(this);
+    }
+
+    /* (non-Javadoc)
+     * Prints a string-based representation of this job instance.
+     * For debugging purposes only.
+     */
+    public String toString() {
+        return Format( "{}({})", getName(), jobNumber ); //$NON-NLS-1$//$NON-NLS-2$
+    }
+
+    /* (non-Javadoc)
+     * @see Job#wakeUp(long)
+     */
+    protected void wakeUp(long delay) {
+        manager.wakeUp_package(this, delay);
+    }
+
+    /**
+     * @param waitQueueStamp The waitQueueStamp to set.
+     */
+    void setWaitQueueStamp(long waitQueueStamp) {
+        this.waitQueueStamp = waitQueueStamp;
+    }
+
+    /**
+     * @return Returns the waitQueueStamp.
+     */
+    long getWaitQueueStamp() {
+        return waitQueueStamp;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/JobActivator.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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 Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.internal.jobs.JobActivator;
+
+import dwt.dwthelper.utils;
+
+// import org.osgi.framework.BundleActivator;
+// import org.osgi.framework.BundleContext;
+// import org.osgi.framework.ServiceRegistration;
+
+import dwtx.core.runtime.jobs.IJobManager;
+
+/**
+ * The Jobs plugin class.
+ */
+public class JobActivator /+: BundleActivator+/ {
+
+    /**
+     * Eclipse property. Set to <code>false</code> to avoid registering JobManager
+     * as an OSGi service.
+     */
+    private static const String PROP_REGISTER_JOB_SERVICE = "eclipse.service.jobs"; //$NON-NLS-1$
+/++
+    /**
+     * The bundle associated this plug-in
+     */
+    private static BundleContext bundleContext;
+
+    /**
+     * This plugin provides a JobManager service.
+     */
+    private ServiceRegistration jobManagerService = null;
+
+    /**
+     * This method is called upon plug-in activation
+     */
+    public void start(BundleContext context) throws Exception {
+        bundleContext = context;
+        JobOSGiUtils.getDefault().openServices();
+
+        bool shouldRegister = !"false".equalsIgnoreCase(context.getProperty(PROP_REGISTER_JOB_SERVICE)); //$NON-NLS-1$
+        if (shouldRegister)
+            registerServices();
+    }
+
+    /**
+     * This method is called when the plug-in is stopped
+     */
+    public void stop(BundleContext context) throws Exception {
+        unregisterServices();
+        JobManager.shutdown();
+        JobOSGiUtils.getDefault().closeServices();
+        bundleContext = null;
+    }
+
+    static BundleContext getContext() {
+        return bundleContext;
+    }
+
+    private void registerServices() {
+        jobManagerService = bundleContext.registerService(IJobManager.class.getName(), JobManager.getInstance(), new Hashtable());
+    }
+
+    private void unregisterServices() {
+        if (jobManagerService !is null) {
+            jobManagerService.unregister();
+            jobManagerService = null;
+        }
+    }
+++/
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/JobChangeEvent.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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.JobChangeEvent;
+
+import dwt.dwthelper.utils;
+
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.jobs.IJobChangeEvent;
+import dwtx.core.runtime.jobs.Job;
+
+public class JobChangeEvent : IJobChangeEvent {
+    /**
+     * The job on which this event occurred.
+     */
+    Job job = null;
+    /**
+     * The result returned by the job's run method, or <code>null</code> if
+     * not applicable.
+     */
+    IStatus result = null;
+    /**
+     * The amount of time to wait after scheduling the job before it should be run,
+     * or <code>-1</code> if not applicable for this type of event.
+     */
+    long delay = -1;
+    /**
+     * Whether this job is being immediately rescheduled.
+     */
+    bool reschedule = false;
+
+    /* (non-Javadoc)
+     * Method declared on IJobChangeEvent
+     */
+    public long getDelay() {
+        return delay;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IJobChangeEvent
+     */
+    public Job getJob() {
+        return job;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IJobChangeEvent
+     */
+    public IStatus getResult() {
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/JobListeners.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * 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.JobListeners;
+
+import dwt.dwthelper.utils;
+
+import dwtx.core.internal.runtime.RuntimeLog;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.ListenerList;
+import dwtx.core.runtime.OperationCanceledException;
+import dwtx.core.runtime.Status;
+import dwtx.core.runtime.jobs.IJobChangeEvent;
+import dwtx.core.runtime.jobs.IJobChangeListener;
+import dwtx.core.runtime.jobs.Job;
+import dwtx.osgi.util.NLS;
+import dwtx.core.internal.jobs.JobChangeEvent;
+import dwtx.core.internal.jobs.InternalJob;
+import dwtx.core.internal.jobs.JobOSGiUtils;
+import dwtx.core.internal.jobs.JobManager;
+import dwtx.core.internal.jobs.JobMessages;
+
+/**
+ * Responsible for notifying all job listeners about job lifecycle events.  Uses a
+ * specialized iterator to ensure the complex iteration logic is contained in one place.
+ */
+class JobListeners {
+    interface IListenerDoit {
+        public void notify(IJobChangeListener listener, IJobChangeEvent event);
+    }
+
+    private const IListenerDoit aboutToRun_;
+    private const IListenerDoit awake_;
+    private const IListenerDoit done_;
+    private const IListenerDoit running_;
+    private const IListenerDoit scheduled_;
+    private const IListenerDoit sleeping_;
+    /**
+     * The global job listeners.
+     */
+    protected const ListenerList global;
+
+    this(){
+        aboutToRun_ = new class IListenerDoit {
+            public void notify(IJobChangeListener listener, IJobChangeEvent event) {
+                listener.aboutToRun(event);
+            }
+        };
+        awake_ = new class IListenerDoit {
+            public void notify(IJobChangeListener listener, IJobChangeEvent event) {
+                listener.awake(event);
+            }
+        };
+        done_ = new class IListenerDoit {
+            public void notify(IJobChangeListener listener, IJobChangeEvent event) {
+                listener.done(event);
+            }
+        };
+        running_ = new class IListenerDoit {
+            public void notify(IJobChangeListener listener, IJobChangeEvent event) {
+                listener.running(event);
+            }
+        };
+        scheduled_ = new class IListenerDoit {
+            public void notify(IJobChangeListener listener, IJobChangeEvent event) {
+                listener.scheduled(event);
+            }
+        };
+        sleeping_ = new class IListenerDoit {
+            public void notify(IJobChangeListener listener, IJobChangeEvent event) {
+                listener.sleeping(event);
+            }
+        };
+        global = new ListenerList(ListenerList.IDENTITY);
+    }
+
+    /**
+     * TODO Could use an instance pool to re-use old event objects
+     */
+    static JobChangeEvent newEvent(Job job) {
+        JobChangeEvent instance = new JobChangeEvent();
+        instance.job = job;
+        return instance;
+    }
+
+    static JobChangeEvent newEvent(Job job, IStatus result) {
+        JobChangeEvent instance = new JobChangeEvent();
+        instance.job = job;
+        instance.result = result;
+        return instance;
+    }
+
+    static JobChangeEvent newEvent(Job job, long delay) {
+        JobChangeEvent instance = new JobChangeEvent();
+        instance.job = job;
+        instance.delay = delay;
+        return instance;
+    }
+
+    /**
+     * Process the given doit for all global listeners and all local listeners
+     * on the given job.
+     */
+    private void doNotify(IListenerDoit doit, IJobChangeEvent event) {
+        //notify all global listeners
+        Object[] listeners = global.getListeners();
+        int size = listeners.length;
+        for (int i = 0; i < size; i++) {
+            try {
+                if (listeners[i] !is null)
+                    doit.notify(cast(IJobChangeListener) listeners[i], event);
+            } catch (Exception e) {
+                handleException(listeners[i], e);
+//             } catch (LinkageError e) {
+//                 handleException(listeners[i], e);
+            }
+        }
+        //notify all local listeners
+        ListenerList list = (cast(InternalJob) event.getJob()).getListeners();
+        listeners = list is null ? null : list.getListeners();
+        if (listeners is null)
+            return;
+        size = listeners.length;
+        for (int i = 0; i < size; i++) {
+            try {
+                if (listeners[i] !is null)
+                    doit.notify(cast(IJobChangeListener) listeners[i], event);
+            } catch (Exception e) {
+                handleException(listeners[i], e);
+//             } catch (LinkageError e) {
+//                 handleException(listeners[i], e);
+            }
+        }
+    }
+
+    private void handleException(Object listener, Exception e) {
+        //this code is roughly copied from InternalPlatform.run(ISafeRunnable),
+        //but in-lined here for performance reasons
+        if (cast(OperationCanceledException)e )
+            return;
+        String pluginId = JobOSGiUtils.getDefault().getBundleId(listener);
+        if (pluginId is null)
+            pluginId = JobManager.PI_JOBS;
+        String message = NLS.bind(JobMessages.meta_pluginProblems, pluginId);
+        RuntimeLog.log(new Status(IStatus.ERROR, pluginId, JobManager.PLUGIN_ERROR, message, e));
+    }
+
+    public void add(IJobChangeListener listener) {
+        global.add(cast(Object)listener);
+    }
+
+    public void remove(IJobChangeListener listener) {
+        global.remove(cast(Object)listener);
+    }
+
+    public void aboutToRun(Job job) {
+        doNotify(aboutToRun_, newEvent(job));
+    }
+
+    public void awake(Job job) {
+        doNotify(awake_, newEvent(job));
+    }
+
+    public void done(Job job, IStatus result, bool reschedule) {
+        JobChangeEvent event = newEvent(job, result);
+        event.reschedule = reschedule;
+        doNotify(done_, event);
+    }
+
+    public void running(Job job) {
+        doNotify(running_, newEvent(job));
+    }
+
+    public void scheduled(Job job, long delay, bool reschedule) {
+        JobChangeEvent event = newEvent(job, delay);
+        event.reschedule = reschedule;
+        doNotify(scheduled_, event);
+    }
+
+    public void sleeping(Job job) {
+        doNotify(sleeping_, newEvent(job));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/JobManager.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,1358 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2007 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 Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.internal.jobs.JobManager;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import tango.io.Stdout;
+import tango.text.convert.Format;
+import tango.time.WallClock;
+import tango.time.Time;
+import tango.core.Thread;
+import tango.text.convert.Format;
+
+//don't use ICU because this is used for debugging only (see bug 135785)
+// import java.text.DateFormat;
+// import java.text.FieldPosition;
+// import java.text.SimpleDateFormat;
+
+import dwtx.core.internal.runtime.RuntimeLog;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IProgressMonitorWithBlocking;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.NullProgressMonitor;
+import dwtx.core.runtime.OperationCanceledException;
+import dwtx.core.runtime.Status;
+import dwtx.core.runtime.jobs.IJobChangeEvent;
+import dwtx.core.runtime.jobs.IJobChangeListener;
+import dwtx.core.runtime.jobs.IJobManager;
+import dwtx.core.runtime.jobs.ILock;
+import dwtx.core.runtime.jobs.ISchedulingRule;
+import dwtx.core.runtime.jobs.Job;
+import dwtx.core.runtime.jobs.JobChangeAdapter;
+import dwtx.core.runtime.jobs.LockListener;
+import dwtx.core.runtime.jobs.ProgressProvider;
+import dwtx.osgi.util.NLS;
+
+import dwtx.core.internal.jobs.ImplicitJobs;
+import dwtx.core.internal.jobs.WorkerPool;
+import dwtx.core.internal.jobs.JobListeners;
+import dwtx.core.internal.jobs.LockManager;
+import dwtx.core.internal.jobs.JobQueue;
+import dwtx.core.internal.jobs.InternalJob;
+import dwtx.core.internal.jobs.ThreadJob;
+import dwtx.core.internal.jobs.JobOSGiUtils;
+import dwtx.core.internal.jobs.Worker;
+import dwtx.core.internal.jobs.Semaphore;
+import dwtx.core.internal.jobs.JobChangeEvent;
+import dwtx.core.internal.jobs.JobMessages;
+import dwtx.core.internal.jobs.JobStatus;
+
+/**
+ * Implementation of API type IJobManager
+ *
+ * Implementation note: all the data structures of this class are protected
+ * by a single lock object held as a private field in this class.  The JobManager
+ * instance itself is not used because this class is publicly reachable, and third
+ * party clients may try to synchronize on it.
+ *
+ * The WorkerPool class uses its own monitor for synchronizing its data
+ * structures. To avoid deadlock between the two classes, the JobManager
+ * must NEVER call the worker pool while its own monitor is held.
+ */
+public class JobManager : IJobManager {
+
+    /**
+     * The unique identifier constant of this plug-in.
+     */
+    public static const String PI_JOBS = "dwtx.core.jobs"; //$NON-NLS-1$
+
+    /**
+     * Status code constant indicating an error occurred while running a plug-in.
+     * For backward compatibility with Platform.PLUGIN_ERROR left at (value = 2).
+     */
+    public static const int PLUGIN_ERROR = 2;
+
+    private static const String OPTION_DEADLOCK_ERROR = PI_JOBS ~ "/jobs/errorondeadlock"; //$NON-NLS-1$
+    private static const String OPTION_DEBUG_BEGIN_END = PI_JOBS ~ "/jobs/beginend"; //$NON-NLS-1$
+    private static const String OPTION_DEBUG_JOBS = PI_JOBS ~ "/jobs"; //$NON-NLS-1$
+    private static const String OPTION_DEBUG_JOBS_TIMING = PI_JOBS ~ "/jobs/timing"; //$NON-NLS-1$
+    private static const String OPTION_LOCKS = PI_JOBS ~ "/jobs/locks"; //$NON-NLS-1$
+    private static const String OPTION_SHUTDOWN = PI_JOBS ~ "/jobs/shutdown"; //$NON-NLS-1$
+
+    static bool DEBUG = false;
+    static bool DEBUG_BEGIN_END = false;
+    static bool DEBUG_DEADLOCK = false;
+    static bool DEBUG_LOCKS = false;
+    static bool DEBUG_TIMING = false;
+    static bool DEBUG_SHUTDOWN = false;
+//     private static DateFormat DEBUG_FORMAT;
+
+    /**
+     * The singleton job manager instance. It must be a singleton because
+     * all job instances maintain a reference (as an optimization) and have no way
+     * of updating it.
+     */
+    private static JobManager instance = null;
+    /**
+     * Scheduling rule used for validation of client-defined rules.
+     */
+    private static ISchedulingRule nullRule;
+    private static void initNullRule(){
+        if( nullRule !is null ) return;
+        nullRule = new class ISchedulingRule {
+            public bool contains(ISchedulingRule rule) {
+                return rule is this;
+            }
+
+            public bool isConflicting(ISchedulingRule rule) {
+                return rule is this;
+            }
+        };
+    }
+
+    /**
+     * True if this manager is active, and false otherwise.  A job manager
+     * starts out active, and becomes inactive if it has been shutdown
+     * and not restarted.
+     */
+    private /+volatile+/ bool active = true;
+
+    const ImplicitJobs implicitJobs;
+
+    private const JobListeners jobListeners;
+
+    /**
+     * The lock for synchronizing all activity in the job manager.  To avoid deadlock,
+     * this lock must never be held for extended periods, and must never be
+     * held while third party code is being called.
+     */
+    private const Object lock;
+
+    private const LockManager lockManager;
+
+    /**
+     * The pool of worker threads.
+     */
+    private WorkerPool pool;
+
+    private ProgressProvider progressProvider = null;
+    /**
+     * Jobs that are currently running. Should only be modified from changeState
+     */
+    private const HashSet running;
+
+    /**
+     * Jobs that are sleeping.  Some sleeping jobs are scheduled to wake
+     * up at a given start time, while others will sleep indefinitely until woken.
+     * Should only be modified from changeState
+     */
+    private const JobQueue sleeping;
+    /**
+     * True if this manager has been suspended, and false otherwise.  A job manager
+     * starts out not suspended, and becomes suspended when <code>suspend</code>
+     * is invoked. Once suspended, no jobs will start running until <code>resume</code>
+     * is called.
+     */
+    private bool suspended = false;
+
+    /**
+     * jobs that are waiting to be run. Should only be modified from changeState
+     */
+    private const JobQueue waiting;
+
+    /**
+     * Counter to record wait queue insertion order.
+     */
+    private long waitQueueCounter;
+
+    public static void debug_(String msg) {
+        StringBuffer msgBuf = new StringBuffer(msg.length + 40);
+        if (DEBUG_TIMING) {
+            //lazy initialize to avoid overhead when not debugging
+//             if (DEBUG_FORMAT is null)
+//                 DEBUG_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS"); //$NON-NLS-1$
+//             DEBUG_FORMAT.format(new Date(), msgBuf, new FieldPosition(0));
+            auto time = WallClock.now();
+            msgBuf.append(Format("{:d2}:{:d2}:{:d2}:{:d3}",
+                time.time.span.hours,
+                time.time.span.minutes,
+                time.time.span.seconds,
+                time.time.span.millis ));
+            msgBuf.append('-');
+        }
+        msgBuf.append('[');
+        msgBuf.append(Thread.getThis().toString());
+        msgBuf.append(']');
+        msgBuf.append(msg);
+        Stdout.formatln( "{}", msgBuf.toString());
+    }
+
+    /**
+     * Returns the job manager singleton. For internal use only.
+     */
+    static synchronized JobManager getInstance() {
+        if (instance is null)
+            new JobManager();
+        return instance;
+    }
+
+    /**
+     * For debugging purposes only
+     */
+    private static String printJobName(Job job) {
+        if (cast(ThreadJob)job ) {
+            Job realJob = (cast(ThreadJob) job).realJob;
+            if (realJob !is null)
+                return realJob.classinfo.name;
+            return Format("ThreadJob on rule: {}", job.getRule()); //$NON-NLS-1$
+        }
+        return job.classinfo.name;
+    }
+
+    /**
+     * For debugging purposes only
+     */
+    public static String printState(int state) {
+        switch (state) {
+            case Job.NONE :
+                return "NONE"; //$NON-NLS-1$
+            case Job.WAITING :
+                return "WAITING"; //$NON-NLS-1$
+            case Job.SLEEPING :
+                return "SLEEPING"; //$NON-NLS-1$
+            case Job.RUNNING :
+                return "RUNNING"; //$NON-NLS-1$
+            case InternalJob.BLOCKED :
+                return "BLOCKED"; //$NON-NLS-1$
+            case InternalJob.ABOUT_TO_RUN :
+                return "ABOUT_TO_RUN"; //$NON-NLS-1$
+            case InternalJob.ABOUT_TO_SCHEDULE :
+                return "ABOUT_TO_SCHEDULE";//$NON-NLS-1$
+        }
+        return "UNKNOWN"; //$NON-NLS-1$
+    }
+
+    /**
+     * Note that although this method is not API, clients have historically used
+     * it to force jobs shutdown in cases where OSGi shutdown does not occur.
+     * For this reason, this method should be considered near-API and should not
+     * be changed if at all possible.
+     */
+    public static void shutdown() {
+        if (instance !is null) {
+            instance.doShutdown();
+            instance = null;
+        }
+    }
+
+    private this() {
+        // DWT instance init
+        implicitJobs = new ImplicitJobs(this);
+        jobListeners = new JobListeners();
+        lock = new Object();
+        lockManager = new LockManager();
+
+        instance = this;
+
+        initDebugOptions();
+        synchronized (lock) {
+            waiting = new JobQueue(false);
+            sleeping = new JobQueue(true);
+            running = new HashSet(10);
+            pool = new WorkerPool(this);
+        }
+        pool.setDaemon(JobOSGiUtils.getDefault().useDaemonThreads());
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.IJobManager#addJobListener(dwtx.core.runtime.jobs.IJobChangeListener)
+     */
+    public void addJobChangeListener(IJobChangeListener listener) {
+        jobListeners.add(listener);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.IJobManager#beginRule(dwtx.core.runtime.jobs.ISchedulingRule, dwtx.core.runtime.IProgressMonitor)
+     */
+    public void beginRule(ISchedulingRule rule, IProgressMonitor monitor) {
+        validateRule(rule);
+        implicitJobs.begin(rule, monitorFor(monitor), false);
+    }
+
+    /**
+     * Cancels a job
+     */
+    protected bool cancel(InternalJob job) {
+        IProgressMonitor monitor = null;
+        synchronized (lock) {
+            switch (job.getState_package()) {
+                case Job.NONE :
+                    return true;
+                case Job.RUNNING :
+                    //cannot cancel a job that has already started (as opposed to ABOUT_TO_RUN)
+                    if (job.internalGetState() is Job.RUNNING) {
+                        monitor = job.getProgressMonitor();
+                        break;
+                    }
+                    //signal that the job should be canceled before it gets a chance to run
+                    job.setAboutToRunCanceled(true);
+                    return true;
+                default :
+                    changeState(job, Job.NONE);
+            }
+        }
+        //call monitor outside sync block
+        if (monitor !is null) {
+            if (!monitor.isCanceled()) {
+                monitor.setCanceled(true);
+                job.canceling();
+            }
+            return false;
+        }
+        //only notify listeners if the job was waiting or sleeping
+        jobListeners.done(cast(Job) job, Status.CANCEL_STATUS, false);
+        return true;
+    }
+    package bool cancel_package(InternalJob job) {
+        return cancel(job);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.IJobManager#cancel(java.lang.String)
+     */
+    public void cancel(Object family) {
+        //don't synchronize because cancel calls listeners
+        for (Iterator it = select(family).iterator(); it.hasNext();)
+            cancel(cast(InternalJob) it.next());
+    }
+
+    /**
+     * Atomically updates the state of a job, adding or removing from the
+     * necessary queues or sets.
+     */
+    private void changeState(InternalJob job, int newState) {
+        bool blockedJobs = false;
+        synchronized (lock) {
+            int oldState = job.internalGetState();
+            switch (oldState) {
+                case Job.NONE :
+                case InternalJob.ABOUT_TO_SCHEDULE :
+                    break;
+                case InternalJob.BLOCKED :
+                    //remove this job from the linked list of blocked jobs
+                    job.remove();
+                    break;
+                case Job.WAITING :
+                    try {
+                        waiting.remove(job);
+                    } catch (RuntimeException e) {
+                        Assert.isLegal(false, "Tried to remove a job that wasn't in the queue"); //$NON-NLS-1$
+                    }
+                    break;
+                case Job.SLEEPING :
+                    try {
+                        sleeping.remove(job);
+                    } catch (RuntimeException e) {
+                        Assert.isLegal(false, "Tried to remove a job that wasn't in the queue"); //$NON-NLS-1$
+                    }
+                    break;
+                case Job.RUNNING :
+                case InternalJob.ABOUT_TO_RUN :
+                    running.remove(job);
+                    //add any blocked jobs back to the wait queue
+                    InternalJob blocked = job.previous();
+                    job.remove();
+                    blockedJobs = blocked !is null;
+                    while (blocked !is null) {
+                        InternalJob previous = blocked.previous();
+                        changeState(blocked, Job.WAITING);
+                        blocked = previous;
+                    }
+                    break;
+                default :
+                    Assert.isLegal(false, Format("Invalid job state: {}, state: {}", job, oldState)); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+            job.internalSetState(newState);
+            switch (newState) {
+                case Job.NONE :
+                    job.setStartTime(InternalJob.T_NONE);
+                    job.setWaitQueueStamp(InternalJob.T_NONE);
+                case InternalJob.BLOCKED :
+                    break;
+                case Job.WAITING :
+                    waiting.enqueue(job);
+                    break;
+                case Job.SLEEPING :
+                    try {
+                        sleeping.enqueue(job);
+                    } catch (RuntimeException e) {
+                        throw new RuntimeException(Format("Error changing from state: ", oldState)); //$NON-NLS-1$
+                    }
+                    break;
+                case Job.RUNNING :
+                case InternalJob.ABOUT_TO_RUN :
+                    job.setStartTime(InternalJob.T_NONE);
+                    job.setWaitQueueStamp(InternalJob.T_NONE);
+                    running.add(job);
+                    break;
+                case InternalJob.ABOUT_TO_SCHEDULE :
+                    break;
+                default :
+                    Assert.isLegal(false, Format("Invalid job state: {}, state: {}", job, newState)); //$NON-NLS-1$ //$NON-NLS-2$
+            }
+        }
+        //notify queue outside sync block
+        if (blockedJobs)
+            pool.jobQueued();
+    }
+
+    /**
+     * Returns a new progress monitor for this job, belonging to the given
+     * progress group.  Returns null if it is not a valid time to set the job's group.
+     */
+    protected IProgressMonitor createMonitor(InternalJob job, IProgressMonitor group, int ticks) {
+        synchronized (lock) {
+            //group must be set before the job is scheduled
+            //this includes the ABOUT_TO_SCHEDULE state, during which it is still
+            //valid to set the progress monitor
+            if (job.getState_package() !is Job.NONE)
+                return null;
+            IProgressMonitor monitor = null;
+            if (progressProvider !is null)
+                monitor = progressProvider.createMonitor(cast(Job) job, group, ticks);
+            if (monitor is null)
+                monitor = new NullProgressMonitor();
+            return monitor;
+        }
+    }
+    package IProgressMonitor createMonitor_package(InternalJob job, IProgressMonitor group, int ticks) {
+        return createMonitor(job, group, ticks);
+    }
+
+    /**
+     * Returns a new progress monitor for this job.  Never returns null.
+     */
+    private IProgressMonitor createMonitor(Job job) {
+        IProgressMonitor monitor = null;
+        if (progressProvider !is null)
+            monitor = progressProvider.createMonitor(job);
+        if (monitor is null)
+            monitor = new NullProgressMonitor();
+        return monitor;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.IJobManager#createProgressGroup()
+     */
+    public IProgressMonitor createProgressGroup() {
+        if (progressProvider !is null)
+            return progressProvider.createProgressGroup();
+        return new NullProgressMonitor();
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.IJobManager#currentJob()
+     */
+    public Job currentJob() {
+        Thread current = Thread.getThis();
+        if (cast(Worker)current )
+            return (cast(Worker) current).currentJob();
+        synchronized (lock) {
+            for (Iterator it = running.iterator(); it.hasNext();) {
+                Job job = cast(Job) it.next();
+                if (job.getThread() is current)
+                    return job;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the delay in milliseconds that a job with a given priority can
+     * tolerate waiting.
+     */
+    private long delayFor(int priority) {
+        //these values may need to be tweaked based on machine speed
+        switch (priority) {
+            case Job.INTERACTIVE :
+                return 0L;
+            case Job.SHORT :
+                return 50L;
+            case Job.LONG :
+                return 100L;
+            case Job.BUILD :
+                return 500L;
+            case Job.DECORATE :
+                return 1000L;
+            default :
+                Assert.isTrue(false, Format("Job has invalid priority: {}", priority)); //$NON-NLS-1$
+                return 0;
+        }
+    }
+
+    /**
+     * Performs the scheduling of a job.  Does not perform any notifications.
+     */
+    private void doSchedule(InternalJob job, long delay) {
+        synchronized (lock) {
+            //if it's a decoration job with no rule, don't run it right now if the system is busy
+            if (job.getPriority() is Job.DECORATE && job.getRule() is null) {
+                long minDelay = running.size() * 100;
+                delay = Math.max(delay, minDelay);
+            }
+            if (delay > 0) {
+                job.setStartTime(System.currentTimeMillis() + delay);
+                changeState(job, Job.SLEEPING);
+            } else {
+                job.setStartTime(System.currentTimeMillis() + delayFor(job.getPriority()));
+                job.setWaitQueueStamp(waitQueueCounter++);
+                changeState(job, Job.WAITING);
+            }
+        }
+    }
+
+    /**
+     * Shuts down the job manager.  Currently running jobs will be told
+     * to stop, but worker threads may still continue processing.
+     * (note: This implemented IJobManager.shutdown which was removed
+     * due to problems caused by premature shutdown)
+     */
+    private void doShutdown() {
+        Job[] toCancel = null;
+        synchronized (lock) {
+            if (active) {
+                active = false;
+                //cancel all running jobs
+                toCancel = arraycast!(Job)( running.toArray());
+                //clean up
+                sleeping.clear();
+                waiting.clear();
+                running.clear();
+            }
+        }
+
+        // Give running jobs a chance to finish. Wait 0.1 seconds for up to 3 times.
+        if (toCancel !is null && toCancel.length > 0) {
+            for (int i = 0; i < toCancel.length; i++) {
+                cancel(cast(InternalJob)toCancel[i]); // cancel jobs outside sync block to avoid deadlock
+            }
+
+            for (int waitAttempts = 0; waitAttempts < 3; waitAttempts++) {
+                Thread.yield();
+                synchronized (lock) {
+                    if (running.isEmpty())
+                        break;
+                }
+                if (DEBUG_SHUTDOWN) {
+                    JobManager.debug_(Format("Shutdown - job wait cycle #{}", (waitAttempts + 1))); //$NON-NLS-1$
+                    Job[] stillRunning = null;
+                    synchronized (lock) {
+                        stillRunning = arraycast!(Job)( running.toArray());
+                    }
+                    if (stillRunning !is null) {
+                        for (int j = 0; j < stillRunning.length; j++) {
+                            JobManager.debug_(Format("\tJob: {}", printJobName(stillRunning[j]))); //$NON-NLS-1$
+                        }
+                    }
+                }
+                try {
+                    Thread.sleep(0.100);
+                } catch (InterruptedException e) {
+                    //ignore
+                }
+                Thread.yield();
+            }
+
+            synchronized (lock) { // retrieve list of the jobs that are still running
+                toCancel = arraycast!(Job)( running.toArray());
+            }
+        }
+
+        if (toCancel !is null) {
+            for (int i = 0; i < toCancel.length; i++) {
+                String jobName = printJobName(toCancel[i]);
+                //this doesn't need to be translated because it's just being logged
+                String msg = "Job found still running after platform shutdown.  Jobs should be canceled by the plugin that scheduled them during shutdown: " ~ jobName; //$NON-NLS-1$
+                RuntimeLog.log(new Status(IStatus.WARNING, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, msg, null));
+
+                // TODO the RuntimeLog.log in its current implementation won't produce a log
+                // during this stage of shutdown. For now add a standard error output.
+                // One the logging story is improved, the System.err output below can be removed:
+                Stderr.formatln("{}", msg);
+            }
+        }
+
+        pool.shutdown_package();
+    }
+
+    /**
+     * Indicates that a job was running, and has now finished.  Note that this method
+     * can be called under OutOfMemoryError conditions and thus must be paranoid
+     * about allocating objects.
+     */
+    protected void endJob(InternalJob job, IStatus result, bool notify) {
+        long rescheduleDelay = InternalJob.T_NONE;
+        synchronized (lock) {
+            //if the job is finishing asynchronously, there is nothing more to do for now
+            if (result is Job.ASYNC_FINISH)
+                return;
+            //if job is not known then it cannot be done
+            if (job.getState_package() is Job.NONE)
+                return;
+            if (JobManager.DEBUG && notify)
+                JobManager.debug_(Format("Ending job: {}", job)); //$NON-NLS-1$
+            job.setResult(result);
+            job.setProgressMonitor(null);
+            job.setThread_package(null);
+            rescheduleDelay = job.getStartTime();
+            changeState(job, Job.NONE);
+        }
+        //notify listeners outside sync block
+        final bool reschedule = active && rescheduleDelay > InternalJob.T_NONE && job.shouldSchedule_package();
+        if (notify)
+            jobListeners.done(cast(Job) job, result, reschedule);
+        //reschedule the job if requested and we are still active
+        if (reschedule)
+            schedule(job, rescheduleDelay, reschedule);
+    }
+    package void endJob_package(InternalJob job, IStatus result, bool notify) {
+        endJob(job, result, notify);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.IJobManager#endRule(dwtx.core.runtime.jobs.ISchedulingRule)
+     */
+    public void endRule(ISchedulingRule rule) {
+        implicitJobs.end(rule, false);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.IJobManager#find(java.lang.String)
+     */
+    public Job[] find(Object family) {
+        List members = select(family);
+        return arraycast!(Job)( members.toArray());
+    }
+
+    /**
+     * Returns a running or blocked job whose scheduling rule conflicts with the
+     * scheduling rule of the given waiting job.  Returns null if there are no
+     * conflicting jobs.  A job can only run if there are no running jobs and no blocked
+     * jobs whose scheduling rule conflicts with its rule.
+     */
+    protected InternalJob findBlockingJob(InternalJob waitingJob) {
+        if (waitingJob.getRule() is null)
+            return null;
+        synchronized (lock) {
+            if (running.isEmpty())
+                return null;
+            //check the running jobs
+            bool hasBlockedJobs = false;
+            for (Iterator it = running.iterator(); it.hasNext();) {
+                InternalJob job = cast(InternalJob) it.next();
+                if (waitingJob.isConflicting(job))
+                    return job;
+                if (!hasBlockedJobs)
+                    hasBlockedJobs = job.previous() !is null;
+            }
+            //there are no blocked jobs, so we are done
+            if (!hasBlockedJobs)
+                return null;
+            //check all jobs blocked by running jobs
+            for (Iterator it = running.iterator(); it.hasNext();) {
+                InternalJob job = cast(InternalJob) it.next();
+                while (true) {
+                    job = job.previous();
+                    if (job is null)
+                        break;
+                    if (waitingJob.isConflicting(job))
+                        return job;
+                }
+            }
+        }
+        return null;
+    }
+    package InternalJob findBlockingJob_package(InternalJob waitingJob) {
+        return findBlockingJob(waitingJob);
+    }
+
+    public LockManager getLockManager() {
+        return lockManager;
+    }
+
+    private void initDebugOptions() {
+        DEBUG = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_DEBUG_JOBS, false);
+        DEBUG_BEGIN_END = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_DEBUG_BEGIN_END, false);
+        DEBUG_DEADLOCK = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_DEADLOCK_ERROR, false);
+        DEBUG_LOCKS = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_LOCKS, false);
+        DEBUG_TIMING = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_DEBUG_JOBS_TIMING, false);
+        DEBUG_SHUTDOWN = JobOSGiUtils.getDefault().getBooleanDebugOption(OPTION_SHUTDOWN, false);
+    }
+
+    /**
+     * Returns whether the job manager is active (has not been shutdown).
+     */
+    protected bool isActive() {
+        return active;
+    }
+    package bool isActive_package() {
+        return isActive();
+    }
+
+    /**
+     * Returns true if the given job is blocking the execution of a non-system
+     * job.
+     */
+    protected bool isBlocking(InternalJob runningJob) {
+        synchronized (lock) {
+            // if this job isn't running, it can't be blocking anyone
+            if (runningJob.getState_package() !is Job.RUNNING)
+                return false;
+            // if any job is queued behind this one, it is blocked by it
+            InternalJob previous = runningJob.previous();
+            while (previous !is null) {
+                // ignore jobs of lower priority (higher priority value means lower priority)
+                if (previous.getPriority() < runningJob.getPriority()) {
+                    if (!previous.isSystem_package())
+                        return true;
+                    // implicit jobs should interrupt unless they act on behalf of system jobs
+                    if (cast(ThreadJob)previous  && (cast(ThreadJob) previous).shouldInterrupt())
+                        return true;
+                }
+                previous = previous.previous();
+            }
+            // none found
+            return false;
+        }
+    }
+    package bool isBlocking_package(InternalJob runningJob) {
+        return isBlocking(runningJob);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.IJobManager#isIdle()
+     */
+    public bool isIdle() {
+        synchronized (lock) {
+            return running.isEmpty() && waiting.isEmpty();
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.IJobManager#isSuspended()
+     */
+    public bool isSuspended() {
+        synchronized (lock) {
+            return suspended;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.Job#job(dwtx.core.runtime.jobs.Job)
+     */
+    protected void join(InternalJob job) {
+        IJobChangeListener listener;
+        Semaphore barrier;
+        synchronized (lock) {
+            int state = job.getState_package();
+            if (state is Job.NONE)
+                return;
+            //don't join a waiting or sleeping job when suspended (deadlock risk)
+            if (suspended && state !is Job.RUNNING)
+                return;
+            //it's an error for a job to join itself
+            if (state is Job.RUNNING && job.getThread_package() is Thread.getThis())
+                throw new IllegalStateException("Job attempted to join itself"); //$NON-NLS-1$
+            //the semaphore will be released when the job is done
+            barrier = new Semaphore(null);
+            listener = new class(barrier) JobChangeAdapter {
+                Semaphore barrier_;
+                this( Semaphore a ){
+                    barrier_ = a;
+                }
+                public void done(IJobChangeEvent event) {
+                    barrier_.release();
+                }
+            };
+            job.addJobChangeListener_package(listener);
+            //compute set of all jobs that must run before this one
+            //add a listener that removes jobs from the blocking set when they finish
+        }
+        //wait until listener notifies this thread.
+        try {
+            while (true) {
+                //notify hook to service pending syncExecs before falling asleep
+                lockManager.aboutToWait(job.getThread_package());
+                try {
+                    if (barrier.acquire(Long.MAX_VALUE))
+                        break;
+                } catch (InterruptedException e) {
+                    //loop and keep trying
+                }
+            }
+        } finally {
+            lockManager.aboutToRelease();
+            job.removeJobChangeListener_package(listener);
+        }
+    }
+    package void join_package(InternalJob job) {
+        join(job);
+    }
+
+    /* (non-Javadoc)
+     * @see IJobManager#join(String, IProgressMonitor)
+     */
+    public void join(Object family_, IProgressMonitor monitor) {
+        monitor = monitorFor(monitor);
+        IJobChangeListener listener = null;
+        Set jobs_;
+        int jobCount;
+        Job blocking = null;
+        synchronized (lock) {
+            //don't join a waiting or sleeping job when suspended (deadlock risk)
+            int states = suspended ? Job.RUNNING : Job.RUNNING | Job.WAITING | Job.SLEEPING;
+            jobs_ = Collections.synchronizedSet(new HashSet(select(family_, states)));
+            jobCount = jobs_.size();
+            if (jobCount > 0) {
+                //if there is only one blocking job, use it in the blockage callback below
+                if (jobCount is 1)
+                    blocking = cast(Job) jobs_.iterator().next();
+                listener = new class(family_, jobs_ )JobChangeAdapter {
+                    Object family;
+                    Set jobs;
+                    this(Object a, Set b){
+                        family = a;
+                        jobs = b;
+                    }
+                    public void done(IJobChangeEvent event) {
+                        //don't remove from list if job is being rescheduled
+                        if (!(cast(JobChangeEvent) event).reschedule)
+                            jobs.remove(event.getJob());
+                    }
+
+                    //update the list of jobs if new ones are added during the join
+                    public void scheduled(IJobChangeEvent event) {
+                        //don't add to list if job is being rescheduled
+                        if ((cast(JobChangeEvent) event).reschedule)
+                            return;
+                        Job job = event.getJob();
+                        if (job.belongsTo(family))
+                            jobs.add(job);
+                    }
+                };
+                addJobChangeListener(listener);
+            }
+        }
+        if (jobCount is 0) {
+            //use up the monitor outside synchronized block because monitors call untrusted code
+            monitor.beginTask(JobMessages.jobs_blocked0, 1);
+            monitor.done();
+            return;
+        }
+        //spin until all jobs are completed
+        try {
+            monitor.beginTask(JobMessages.jobs_blocked0, jobCount);
+            monitor.subTask(NLS.bind(JobMessages.jobs_waitFamSub, Integer.toString(jobCount)));
+            reportBlocked(monitor, blocking);
+            int jobsLeft;
+            int reportedWorkDone = 0;
+            while ((jobsLeft = jobs_.size()) > 0) {
+                //don't let there be negative work done if new jobs have
+                //been added since the join began
+                int actualWorkDone = Math.max(0, jobCount - jobsLeft);
+                if (reportedWorkDone < actualWorkDone) {
+                    monitor.worked(actualWorkDone - reportedWorkDone);
+                    reportedWorkDone = actualWorkDone;
+                    monitor.subTask(NLS.bind(JobMessages.jobs_waitFamSub, Integer.toString(jobsLeft)));
+                }
+                implMissing(__FILE__, __LINE__ );
+// DWT
+//                 if (Thread.interrupted())
+//                     throw new InterruptedException();
+                if (monitor.isCanceled())
+                    throw new OperationCanceledException();
+                //notify hook to service pending syncExecs before falling asleep
+                lockManager.aboutToWait(null);
+                Thread.sleep(0.100);
+            }
+        } finally {
+            lockManager.aboutToRelease();
+            removeJobChangeListener(listener);
+            reportUnblocked(monitor);
+            monitor.done();
+        }
+    }
+
+    /**
+     * Returns a non-null progress monitor instance.  If the monitor is null,
+     * returns the default monitor supplied by the progress provider, or a
+     * NullProgressMonitor if no default monitor is available.
+     */
+    private IProgressMonitor monitorFor(IProgressMonitor monitor) {
+        if (monitor is null || (cast(NullProgressMonitor)monitor )) {
+            if (progressProvider !is null) {
+                try {
+                    monitor = progressProvider.getDefaultMonitor();
+                } catch (Exception e) {
+                    String msg = NLS.bind(JobMessages.meta_pluginProblems, JobManager.PI_JOBS);
+                    RuntimeLog.log(new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, msg, e));
+                }
+            }
+        }
+
+        if (monitor is null)
+            return new NullProgressMonitor();
+        return monitor;
+    }
+
+    /* (non-Javadoc)
+     * @see IJobManager#newLock(java.lang.String)
+     */
+    public ILock newLock() {
+        return lockManager.newLock();
+    }
+
+    /**
+     * Removes and returns the first waiting job in the queue. Returns null if there
+     * are no items waiting in the queue.  If an item is removed from the queue,
+     * it is moved to the running jobs list.
+     */
+    private Job nextJob() {
+        synchronized (lock) {
+            //do nothing if the job manager is suspended
+            if (suspended)
+                return null;
+            //tickle the sleep queue to see if anyone wakes up
+            long now = System.currentTimeMillis();
+            InternalJob job = sleeping.peek();
+            while (job !is null && job.getStartTime() < now) {
+                job.setStartTime(now + delayFor(job.getPriority()));
+                job.setWaitQueueStamp(waitQueueCounter++);
+                changeState(job, Job.WAITING);
+                job = sleeping.peek();
+            }
+            //process the wait queue until we find a job whose rules are satisfied.
+            while ((job = waiting.peek()) !is null) {
+                InternalJob blocker = findBlockingJob(job);
+                if (blocker is null)
+                    break;
+                //queue this job after the job that's blocking it
+                changeState(job, InternalJob.BLOCKED);
+                //assert job does not already belong to some other data structure
+                Assert.isTrue(job.next() is null);
+                Assert.isTrue(job.previous() is null);
+                blocker.addLast(job);
+            }
+            //the job to run must be in the running list before we exit
+            //the sync block, otherwise two jobs with conflicting rules could start at once
+            if (job !is null) {
+                changeState(job, InternalJob.ABOUT_TO_RUN);
+                if (JobManager.DEBUG)
+                    JobManager.debug_(Format("Starting job: {}", job)); //$NON-NLS-1$
+            }
+            return cast(Job) job;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.IJobManager#removeJobListener(dwtx.core.runtime.jobs.IJobChangeListener)
+     */
+    public void removeJobChangeListener(IJobChangeListener listener) {
+        jobListeners.remove(listener);
+    }
+
+    /**
+     * Report to the progress monitor that this thread is blocked, supplying
+     * an information message, and if possible the job that is causing the blockage.
+     * Important: An invocation of this method MUST be followed eventually be
+     * an invocation of reportUnblocked.
+     * @param monitor The monitor to report blocking to
+     * @param blockingJob The job that is blocking this thread, or <code>null</code>
+     * @see #reportUnblocked
+     */
+    final void reportBlocked(IProgressMonitor monitor, InternalJob blockingJob) {
+        if (!(cast(IProgressMonitorWithBlocking)monitor ))
+            return;
+        IStatus reason;
+        if (blockingJob is null || cast(ThreadJob)blockingJob || blockingJob.isSystem_package()) {
+            reason = new Status(IStatus.INFO, JobManager.PI_JOBS, 1, JobMessages.jobs_blocked0, null);
+        } else {
+            String msg = NLS.bind(JobMessages.jobs_blocked1, blockingJob.getName_package());
+            reason = new JobStatus(IStatus.INFO, cast(Job) blockingJob, msg);
+        }
+        (cast(IProgressMonitorWithBlocking) monitor).setBlocked(reason);
+    }
+
+    /**
+     * Reports that this thread was blocked, but is no longer blocked and is able
+     * to proceed.
+     * @param monitor The monitor to report unblocking to.
+     * @see #reportBlocked
+     */
+    final void reportUnblocked(IProgressMonitor monitor) {
+        if (cast(IProgressMonitorWithBlocking)monitor )
+            (cast(IProgressMonitorWithBlocking) monitor).clearBlocked();
+    }
+
+    /*(non-Javadoc)
+     * @see dwtx.core.runtime.jobs.IJobManager#resume()
+     */
+    public final void resume() {
+        synchronized (lock) {
+            suspended = false;
+            //poke the job pool
+            pool.jobQueued();
+        }
+    }
+
+    /** (non-Javadoc)
+     * @deprecated this method should not be used
+     * @see dwtx.core.runtime.jobs.IJobManager#resume(dwtx.core.runtime.jobs.ISchedulingRule)
+     */
+    public final void resume(ISchedulingRule rule) {
+        implicitJobs.resume(rule);
+    }
+
+    /**
+     * Attempts to immediately start a given job.  Returns true if the job was
+     * successfully started, and false if it could not be started immediately
+     * due to a currently running job with a conflicting rule.  Listeners will never
+     * be notified of jobs that are run in this way.
+     */
+    protected bool runNow(InternalJob job) {
+        synchronized (lock) {
+            //cannot start if there is a conflicting job
+            if (findBlockingJob(job) !is null)
+                return false;
+            changeState(job, Job.RUNNING);
+            job.setProgressMonitor(new NullProgressMonitor());
+            job.run_package(null);
+        }
+        return true;
+    }
+    package bool runNow_package(InternalJob job) {
+        return runNow(job);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.Job#schedule(long)
+     */
+    protected void schedule(InternalJob job, long delay, bool reschedule) {
+        if (!active)
+            throw new IllegalStateException("Job manager has been shut down."); //$NON-NLS-1$
+        Assert.isNotNull(job, "Job is null"); //$NON-NLS-1$
+        Assert.isLegal(delay >= 0, "Scheduling delay is negative"); //$NON-NLS-1$
+        synchronized (lock) {
+            //if the job is already running, set it to be rescheduled when done
+            if (job.getState_package() is Job.RUNNING) {
+                job.setStartTime(delay);
+                return;
+            }
+            //can't schedule a job that is waiting or sleeping
+            if (job.internalGetState() !is Job.NONE)
+                return;
+            if (JobManager.DEBUG)
+                JobManager.debug_(Format("Scheduling job: {}", job)); //$NON-NLS-1$
+            //remember that we are about to schedule the job
+            //to prevent multiple schedule attempts from succeeding (bug 68452)
+            changeState(job, InternalJob.ABOUT_TO_SCHEDULE);
+        }
+        //notify listeners outside sync block
+        jobListeners.scheduled(cast(Job) job, delay, reschedule);
+        //schedule the job
+        doSchedule(job, delay);
+        //call the pool outside sync block to avoid deadlock
+        pool.jobQueued();
+    }
+    package void schedule_package(InternalJob job, long delay, bool reschedule) {
+        schedule(job, delay, reschedule);
+    }
+
+    /**
+     * Adds all family members in the list of jobs to the collection
+     */
+    private void select(List members, Object family, InternalJob firstJob, int stateMask) {
+        if (firstJob is null)
+            return;
+        InternalJob job = firstJob;
+        do {
+            //note that job state cannot be NONE at this point
+            if ((family is null || job.belongsTo_package(family)) && ((job.getState_package() & stateMask) !is 0))
+                members.add(job);
+            job = job.previous();
+        } while (job !is null && job !is firstJob);
+    }
+
+    /**
+     * Returns a list of all jobs known to the job manager that belong to the given family.
+     */
+    private List select(Object family) {
+        return select(family, Job.WAITING | Job.SLEEPING | Job.RUNNING);
+    }
+
+    /**
+     * Returns a list of all jobs known to the job manager that belong to the given
+     * family and are in one of the provided states.
+     */
+    private List select(Object family, int stateMask) {
+        List members = new ArrayList();
+        synchronized (lock) {
+            if ((stateMask & Job.RUNNING) !is 0) {
+                for (Iterator it = running.iterator(); it.hasNext();) {
+                    select(members, family, cast(InternalJob) it.next(), stateMask);
+                }
+            }
+            if ((stateMask & Job.WAITING) !is 0)
+                select(members, family, waiting.peek(), stateMask);
+            if ((stateMask & Job.SLEEPING) !is 0)
+                select(members, family, sleeping.peek(), stateMask);
+        }
+        return members;
+    }
+
+    /* (non-Javadoc)
+     * @see IJobManager#setLockListener(LockListener)
+     */
+    public void setLockListener(LockListener listener) {
+        lockManager.setLockListener(listener);
+    }
+
+    /**
+     * Changes a job priority.
+     */
+    protected void setPriority(InternalJob job, int newPriority) {
+        synchronized (lock) {
+            int oldPriority = job.getPriority();
+            if (oldPriority is newPriority)
+                return;
+            job.internalSetPriority(newPriority);
+            //if the job is waiting to run, re-shuffle the queue
+            if (job.getState_package() is Job.WAITING) {
+                long oldStart = job.getStartTime();
+                job.setStartTime(oldStart + (delayFor(newPriority) - delayFor(oldPriority)));
+                waiting.resort(job);
+            }
+        }
+    }
+    package void setPriority_package(InternalJob job, int newPriority) {
+        setPriority(job, newPriority);
+    }
+
+    /* (non-Javadoc)
+     * @see IJobManager#setProgressProvider(IProgressProvider)
+     */
+    public void setProgressProvider(ProgressProvider provider) {
+        progressProvider = provider;
+    }
+
+    /* (non-Javadoc)
+     * @see Job#setRule
+     */
+    public void setRule(InternalJob job, ISchedulingRule rule) {
+        synchronized (lock) {
+            //cannot change the rule of a job that is already running
+            Assert.isLegal(job.getState_package() is Job.NONE);
+            validateRule(rule);
+            job.internalSetRule(rule);
+        }
+    }
+
+    /**
+     * Puts a job to sleep. Returns true if the job was successfully put to sleep.
+     */
+    protected bool sleep(InternalJob job) {
+        synchronized (lock) {
+            switch (job.getState_package()) {
+                case Job.RUNNING :
+                    //cannot be paused if it is already running (as opposed to ABOUT_TO_RUN)
+                    if (job.internalGetState() is Job.RUNNING)
+                        return false;
+                    //job hasn't started running yet (aboutToRun listener)
+                    break;
+                case Job.SLEEPING :
+                    //update the job wake time
+                    job.setStartTime(InternalJob.T_INFINITE);
+                    //change state again to re-shuffle the sleep queue
+                    changeState(job, Job.SLEEPING);
+                    return true;
+                case Job.NONE :
+                    return true;
+                case Job.WAITING :
+                    //put the job to sleep
+                    break;
+            }
+            job.setStartTime(InternalJob.T_INFINITE);
+            changeState(job, Job.SLEEPING);
+        }
+        jobListeners.sleeping(cast(Job) job);
+        return true;
+    }
+    package bool sleep_package(InternalJob job) {
+        return sleep(job);
+    }
+
+    /* (non-Javadoc)
+     * @see IJobManager#sleep(String)
+     */
+    public void sleep(Object family) {
+        //don't synchronize because sleep calls listeners
+        for (Iterator it = select(family).iterator(); it.hasNext();) {
+            sleep(cast(InternalJob) it.next());
+        }
+    }
+
+    /**
+     * Returns the estimated time in milliseconds before the next job is scheduled
+     * to wake up. The result may be negative.  Returns InternalJob.T_INFINITE if
+     * there are no sleeping or waiting jobs.
+     */
+    protected long sleepHint() {
+        synchronized (lock) {
+            //wait forever if job manager is suspended
+            if (suspended)
+                return InternalJob.T_INFINITE;
+            if (!waiting.isEmpty())
+                return 0L;
+            //return the anticipated time that the next sleeping job will wake
+            InternalJob next = sleeping.peek();
+            if (next is null)
+                return InternalJob.T_INFINITE;
+            return next.getStartTime() - System.currentTimeMillis();
+        }
+    }
+    package long sleepHint_package() {
+        return sleepHint();
+    }
+    /**
+     * Returns the next job to be run, or null if no jobs are waiting to run.
+     * The worker must call endJob when the job is finished running.
+     */
+    protected Job startJob() {
+        Job job = null;
+        while (true) {
+            job = nextJob();
+            if (job is null)
+                return null;
+            //must perform this outside sync block because it is third party code
+            if (job.shouldRun()) {
+                //check for listener veto
+                jobListeners.aboutToRun(job);
+                //listeners may have canceled or put the job to sleep
+                synchronized (lock) {
+                    if (job.getState() is Job.RUNNING) {
+                        InternalJob internal = job;
+                        if (internal.isAboutToRunCanceled()) {
+                            internal.setAboutToRunCanceled(false);
+                            //fall through and end the job below
+                        } else {
+                            internal.setProgressMonitor(createMonitor(job));
+                            //change from ABOUT_TO_RUN to RUNNING
+                            internal.internalSetState(Job.RUNNING);
+                            break;
+                        }
+                    }
+                }
+            }
+            if (job.getState() !is Job.SLEEPING) {
+                //job has been vetoed or canceled, so mark it as done
+                endJob(job, Status.CANCEL_STATUS, true);
+                continue;
+            }
+        }
+        jobListeners.running(job);
+        return job;
+
+    }
+    package Job startJob_package() {
+        return startJob();
+    }
+
+    /* non-Javadoc)
+     * @see dwtx.core.runtime.jobs.IJobManager#suspend()
+     */
+    public final void suspend() {
+        synchronized (lock) {
+            suspended = true;
+        }
+    }
+
+    /** (non-Javadoc)
+     * @deprecated this method should not be used
+     * @see dwtx.core.runtime.jobs.IJobManager#suspend(dwtx.core.runtime.jobs.ISchedulingRule, dwtx.core.runtime.IProgressMonitor)
+     */
+    public final void suspend(ISchedulingRule rule, IProgressMonitor monitor) {
+        Assert.isNotNull(cast(Object)rule);
+        implicitJobs.suspend(rule, monitorFor(monitor));
+    }
+
+    /* non-Javadoc)
+     * @see dwtx.core.runtime.jobs.IJobManager#transferRule()
+     */
+    public void transferRule(ISchedulingRule rule, Thread destinationThread) {
+        implicitJobs.transfer(rule, destinationThread);
+    }
+
+    /**
+     * Validates that the given scheduling rule obeys the constraints of
+     * scheduling rules as described in the <code>ISchedulingRule</code>
+     * javadoc specification.
+     */
+    private void validateRule(ISchedulingRule rule) {
+        //null rule always valid
+        if (rule is null)
+            return;
+        initNullRule();
+        //contains method must be reflexive
+        Assert.isLegal(rule.contains(rule));
+        //contains method must return false when given an unknown rule
+        Assert.isLegal(!rule.contains(nullRule));
+        //isConflicting method must be reflexive
+        Assert.isLegal(rule.isConflicting(rule));
+        //isConflicting method must return false when given an unknown rule
+        Assert.isLegal(!rule.isConflicting(nullRule));
+    }
+
+    /* (non-Javadoc)
+     * @see Job#wakeUp(long)
+     */
+    protected void wakeUp(InternalJob job, long delay) {
+        Assert.isLegal(delay >= 0, "Scheduling delay is negative"); //$NON-NLS-1$
+        synchronized (lock) {
+            //cannot wake up if it is not sleeping
+            if (job.getState_package() !is Job.SLEEPING)
+                return;
+            doSchedule(job, delay);
+        }
+        //call the pool outside sync block to avoid deadlock
+        pool.jobQueued();
+
+        //only notify of wake up if immediate
+        if (delay is 0)
+            jobListeners.awake(cast(Job) job);
+    }
+    package void wakeUp_package(InternalJob job, long delay) {
+        wakeUp(job, delay);
+    }
+
+    /* (non-Javadoc)
+     * @see IJobFamily#wakeUp(String)
+     */
+    public void wakeUp(Object family) {
+        //don't synchronize because wakeUp calls listeners
+        for (Iterator it = select(family).iterator(); it.hasNext();) {
+            wakeUp(cast(InternalJob) it.next(), 0L);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/JobMessages.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,61 @@
+/**********************************************************************
+ * Copyright (c) 2005, 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.JobMessages;
+
+import tango.core.Thread;
+import tango.io.Stdout;
+import tango.time.WallClock;
+import tango.text.convert.TimeStamp;
+
+import dwt.dwthelper.utils;
+
+import dwtx.osgi.util.NLS;
+
+/**
+ * Job plugin message catalog
+ */
+public class JobMessages : NLS {
+    private static const String BUNDLE_NAME = "dwtx.core.internal.jobs.messages"; //$NON-NLS-1$
+
+    // Job Manager and Locks
+    public static String jobs_blocked0 = "The user operation is waiting for background work to complete.";
+    public static String jobs_blocked1 = "The user operation is waiting for \"{0}\" to complete.";
+    public static String jobs_internalError = "An internal error occured during: \"{0}\".";
+    public static String jobs_waitFamSub =  "{0} work item(s) left.";
+
+    // metadata
+    public static String meta_pluginProblems = "Problems occured when invoking code from plug-in: \"{0}\".";
+
+//     static this() {
+//         // load message values from bundle file
+//         reloadMessages();
+//     }
+
+    public static void reloadMessages() {
+//         NLS.initializeMessages(BUNDLE_NAME, import(BUNDLE_NAME~".properties"));
+    }
+
+    /**
+     * Print a debug message to the console.
+     * Pre-pend the message with the current date and the name of the current thread.
+     */
+    public static void message(String message) {
+        StringBuffer buffer = new StringBuffer();
+        char[30] buf;
+        buffer.append(tango.text.convert.TimeStamp.format( buf, WallClock.now()));
+        buffer.append(" - ["); //$NON-NLS-1$
+        buffer.append(Thread.getThis().name());
+        buffer.append("] "); //$NON-NLS-1$
+        buffer.append(message);
+        Stdout.formatln(buffer.toString());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/JobOSGiUtils.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 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 Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.internal.jobs.JobOSGiUtils;
+
+import dwt.dwthelper.utils;
+
+// import org.osgi.framework.Bundle;
+// import org.osgi.framework.BundleContext;
+// import org.osgi.service.packageadmin.PackageAdmin;
+// import org.osgi.util.tracker.ServiceTracker;
+
+import dwtx.core.runtime.jobs.IJobManager;
+// import dwtx.osgi.service.debug.DebugOptions;
+
+/**
+ * The class contains a set of helper methods for the runtime Jobs plugin.
+ * The following utility methods are supplied:
+ * - provides access to debug options
+ * - provides some bundle discovery functionality
+ *
+ * The closeServices() method should be called before the plugin is stopped.
+ *
+ * @since dwtx.core.jobs 3.2
+ */
+class JobOSGiUtils {
+//     private ServiceTracker debugTracker = null;
+//     private ServiceTracker bundleTracker = null;
+
+    private static /+final+/ JobOSGiUtils singleton;
+
+    /**
+     * Accessor for the singleton instance
+     * @return The JobOSGiUtils instance
+     */
+    public static synchronized JobOSGiUtils getDefault() {
+        if( singleton is null ){
+            singleton = new JobOSGiUtils();
+        }
+        return singleton;
+    }
+
+    /**
+     * Private constructor to block instance creation.
+     */
+    private this() {
+//         super();
+    }
+
+    void openServices() {
+        implMissing(__FILE__,__LINE__);
+//         BundleContext context = JobActivator.getContext();
+//         if (context is null) {
+//             if (JobManager.DEBUG)
+//                 JobMessages.message("JobsOSGiUtils called before plugin started"); //$NON-NLS-1$
+//             return;
+//         }
+//
+//         debugTracker = new ServiceTracker(context, DebugOptions.class.getName(), null);
+//         debugTracker.open();
+//
+//         bundleTracker = new ServiceTracker(context, PackageAdmin.class.getName(), null);
+//         bundleTracker.open();
+    }
+
+    void closeServices() {
+        implMissing(__FILE__,__LINE__);
+//         if (debugTracker !is null) {
+//             debugTracker.close();
+//             debugTracker = null;
+//         }
+//         if (bundleTracker !is null) {
+//             bundleTracker.close();
+//             bundleTracker = null;
+//         }
+    }
+
+    public bool getBooleanDebugOption(String option, bool defaultValue) {
+        implMissing(__FILE__,__LINE__);
+        return false;
+//         if (debugTracker is null) {
+//             if (JobManager.DEBUG)
+//                 JobMessages.message("Debug tracker is not set"); //$NON-NLS-1$
+//             return defaultValue;
+//         }
+//         DebugOptions options = (DebugOptions) debugTracker.getService();
+//         if (options !is null) {
+//             String value = options.getOption(option);
+//             if (value !is null)
+//                 return value.equalsIgnoreCase("true"); //$NON-NLS-1$
+//         }
+//         return defaultValue;
+    }
+
+    /**
+     * Returns the bundle id of the bundle that contains the provided object, or
+     * <code>null</code> if the bundle could not be determined.
+     */
+    public String getBundleId(Object object) {
+        implMissing(__FILE__,__LINE__);
+//         if (bundleTracker is null) {
+//             if (JobManager.DEBUG)
+//                 JobMessages.message("Bundle tracker is not set"); //$NON-NLS-1$
+//             return null;
+//         }
+//         PackageAdmin packageAdmin = (PackageAdmin) bundleTracker.getService();
+//         if (object is null)
+//             return null;
+//         if (packageAdmin is null)
+//             return null;
+//         Bundle source = packageAdmin.getBundle(object.getClass());
+//         if (source !is null && source.getSymbolicName() !is null)
+//             return source.getSymbolicName();
+        return null;
+    }
+
+    /**
+     * Calculates whether the job plugin should set worker threads to be daemon
+     * threads.  When workers are daemon threads, the job plugin does not need
+     * to be explicitly shut down because the VM can exit while workers are still
+     * alive.
+     * @return <code>true</code> if all worker threads should be daemon threads,
+     * and <code>false</code> otherwise.
+     */
+    bool useDaemonThreads() {
+        implMissing(__FILE__,__LINE__);
+        return false;
+//         BundleContext context = JobActivator.getContext();
+//         if (context is null) {
+//             //we are running stand-alone, so consult global system property
+//             String value = System.getProperty(IJobManager.PROP_USE_DAEMON_THREADS);
+//             //default to use daemon threads if property is absent
+//             if (value is null)
+//                 return true;
+//             return "true".equalsIgnoreCase(value); //$NON-NLS-1$
+//         }
+//         //only use daemon threads if the property is defined
+//         final String value = context.getProperty(IJobManager.PROP_USE_DAEMON_THREADS);
+//         //if value is absent, don't use daemon threads to maintain legacy behaviour
+//         if (value is null)
+//             return false;
+//         return "true".equalsIgnoreCase(value); //$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/JobQueue.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,146 @@
+/*******************************************************************************
+ * 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.JobQueue;
+
+import dwt.dwthelper.utils;
+
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.Status;
+
+import dwtx.core.internal.jobs.InternalJob;
+
+/**
+ * A linked list based priority queue. Either the elements in the queue must
+ * implement Comparable, or a Comparator must be provided.
+ */
+public class JobQueue {
+    /**
+     * The dummy entry sits between the head and the tail of the queue.
+     * dummy.previous() is the head, and dummy.next() is the tail.
+     */
+    private final InternalJob dummy;
+
+    /**
+     * If true, conflicting jobs will be allowed to overtake others in the
+     * queue that have lower priority. If false, higher priority jumps can only
+     * move up the queue by overtaking jobs that they don't conflict with.
+     */
+    private bool allowConflictOvertaking;
+
+    /**
+     * Create a new job queue.
+     */
+    public this(bool allowConflictOvertaking) {
+        //compareTo on dummy is never called
+        dummy = new class("Queue-Head") InternalJob {//$NON-NLS-1$
+            this( String name ){
+                super(name);
+            }
+            public IStatus run(IProgressMonitor m) {
+                return Status.OK_STATUS;
+            }
+        };
+        dummy.setNext(dummy);
+        dummy.setPrevious(dummy);
+        this.allowConflictOvertaking = allowConflictOvertaking;
+    }
+
+    /**
+     * remove all elements
+     */
+    public void clear() {
+        dummy.setNext(dummy);
+        dummy.setPrevious(dummy);
+    }
+
+    /**
+     * Return and remove the element with highest priority, or null if empty.
+     */
+    public InternalJob dequeue() {
+        InternalJob toRemove = dummy.previous();
+        if (toRemove is dummy)
+            return null;
+        return toRemove.remove();
+    }
+
+    /**
+     * Adds an item to the queue
+     */
+    public void enqueue(InternalJob newEntry) {
+        //assert new entry is does not already belong to some other data structure
+        Assert.isTrue(newEntry.next() is null);
+        Assert.isTrue(newEntry.previous() is null);
+        InternalJob tail = dummy.next();
+        //overtake lower priority jobs. Only overtake conflicting jobs if allowed to
+        while (canOvertake(newEntry, tail))
+            tail = tail.next();
+        //new entry is smaller than tail
+        final InternalJob tailPrevious = tail.previous();
+        newEntry.setNext(tail);
+        newEntry.setPrevious(tailPrevious);
+        tailPrevious.setNext(newEntry);
+        tail.setPrevious(newEntry);
+    }
+
+    /**
+     * Returns whether the new entry to overtake the existing queue entry.
+     * @param newEntry The entry to be added to the queue
+     * @param queueEntry The existing queue entry
+     */
+    private bool canOvertake(InternalJob newEntry, InternalJob queueEntry) {
+        //can never go past the end of the queue
+        if (queueEntry is dummy)
+            return false;
+        //if the new entry was already in the wait queue, ensure it is re-inserted in correct position (bug 211799)
+        if (newEntry.getWaitQueueStamp() > 0 && newEntry.getWaitQueueStamp() < queueEntry.getWaitQueueStamp())
+            return true;
+        //if the new entry has lower priority, there is no need to overtake the existing entry
+        if (queueEntry.compareTo(newEntry) >= 0)
+            return false;
+        //the new entry has higher priority, but only overtake the existing entry if the queue allows it
+        return allowConflictOvertaking || !newEntry.isConflicting(queueEntry);
+    }
+
+    /**
+     * Removes the given element from the queue.
+     */
+    public void remove(InternalJob toRemove) {
+        toRemove.remove();
+        //previous of toRemove might now bubble up
+    }
+
+    /**
+     * The given object has changed priority. Reshuffle the heap until it is
+     * valid.
+     */
+    public void resort(InternalJob entry) {
+        remove(entry);
+        enqueue(entry);
+    }
+
+    /**
+     * Returns true if the queue is empty, and false otherwise.
+     */
+    public bool isEmpty() {
+        return dummy.next() is dummy;
+    }
+
+    /**
+     * Return greatest element without removing it, or null if empty
+     */
+    public InternalJob peek() {
+        return dummy.previous() is dummy ? null : dummy.previous();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/JobStatus.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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.JobStatus;
+
+import dwt.dwthelper.utils;
+
+import dwtx.core.runtime.Status;
+import dwtx.core.runtime.jobs.IJobStatus;
+import dwtx.core.runtime.jobs.Job;
+import dwtx.core.internal.jobs.JobManager;
+
+/**
+ * Standard implementation of the IJobStatus interface.
+ */
+public class JobStatus : Status, IJobStatus {
+    private Job job;
+
+    /**
+     * Creates a new job status with no interesting error code or exception.
+     * @param severity
+     * @param job
+     * @param message
+     */
+    public this(int severity, Job job, String message) {
+        super(severity, JobManager.PI_JOBS, 1, message, null);
+        this.job = job;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.IJobStatus#getJob()
+     */
+    public Job getJob() {
+        return job;
+    }
+}
--- /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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/ObjectMap.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,354 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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 Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.internal.jobs.ObjectMap;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+import dwtx.core.internal.jobs.StringPool;
+
+/**
+ * A specialized map implementation that is optimized for a small set of object
+ * keys.
+ *
+ * Implemented as a single array that alternates keys and values.
+ *
+ * Note: This class is copied from dwtx.core.resources
+ */
+public class ObjectMap : Map {
+    // 8 attribute keys, 8 attribute values
+    protected static const int DEFAULT_SIZE = 16;
+    protected static const int GROW_SIZE = 10;
+    protected int count = 0;
+    protected Object[] elements = null;
+
+    /**
+     * Creates a new object map.
+     *
+     * @param initialCapacity
+     *                  The initial number of elements that will fit in the map.
+     */
+    public this(int initialCapacity) {
+        if (initialCapacity > 0)
+            elements = new Object[Math.max(initialCapacity * 2, 0)];
+    }
+
+    /**
+     * Creates a new object map of the same size as the given map and populate
+     * it with the key/attribute pairs found in the map.
+     *
+     * @param map
+     *                  The entries in the given map will be added to the new map.
+     */
+    public this(Map map) {
+        this(map.size());
+        putAll(map);
+    }
+
+    /**
+     * @see Map#clear()
+     */
+    public void clear() {
+        elements = null;
+        count = 0;
+    }
+
+    /**
+     * @see java.lang.Object#clone()
+     */
+    public Object clone() {
+        return new ObjectMap(this);
+    }
+
+    /**
+     * @see Map#containsKey(java.lang.Object)
+     */
+    public bool containsKey(Object key) {
+        if (elements is null || count is 0)
+            return false;
+        for (int i = 0; i < elements.length; i = i + 2)
+            if (elements[i] !is null && elements[i].opEquals(key))
+                return true;
+        return false;
+    }
+    public bool containsKey(String key) {
+        return containsKey(stringcast(key));
+    }
+    /**
+     * @see Map#containsValue(java.lang.Object)
+     */
+    public bool containsValue(Object value) {
+        if (elements is null || count is 0)
+            return false;
+        for (int i = 1; i < elements.length; i = i + 2)
+            if (elements[i] !is null && elements[i].opEquals(value))
+                return true;
+        return false;
+    }
+
+    /**
+     * @see Map#entrySet()
+     *
+     * Note: This implementation does not conform properly to the
+     * specification in the Map interface. The returned collection will not
+     * be bound to this map and will not remain in sync with this map.
+     */
+    public Set entrySet() {
+        return count is 0 ? Collections.EMPTY_SET : toHashMap().entrySet();
+    }
+
+    /**
+     * @see Object#equals(java.lang.Object)
+     */
+    public override int opEquals(Object o) {
+        if (!(cast(Map)o ))
+            return false;
+        Map other = cast(Map) o;
+        //must be same size
+        if (count !is other.size())
+            return false;
+        //keysets must be equal
+        if (!(cast(Object)keySet()).opEquals(cast(Object)other.keySet()))
+            return false;
+        //values for each key must be equal
+        for (int i = 0; i < elements.length; i = i + 2) {
+            if (elements[i] !is null && (!elements[i + 1].opEquals(other.get(elements[i]))))
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * @see Map#get(java.lang.Object)
+     */
+    public Object get(Object key) {
+        if (elements is null || count is 0)
+            return null;
+        for (int i = 0; i < elements.length; i = i + 2)
+            if (elements[i] !is null && elements[i].opEquals(key))
+                return elements[i + 1];
+        return null;
+    }
+    public Object get(String key) {
+        return get(stringcast(key));
+    }
+
+    /**
+     * The capacity of the map has been exceeded, grow the array by GROW_SIZE to
+     * accomodate more entries.
+     */
+    protected void grow() {
+        Object[] expanded = new Object[elements.length + GROW_SIZE];
+        System.arraycopy(elements, 0, expanded, 0, elements.length);
+        elements = expanded;
+    }
+
+    /**
+     * @see Object#hashCode()
+     */
+    public override hash_t toHash() {
+        int hash = 0;
+        for (int i = 0; i < elements.length; i = i + 2) {
+            if (elements[i] !is null) {
+                hash += elements[i].toHash();
+            }
+        }
+        return hash;
+    }
+
+    /**
+     * @see Map#isEmpty()
+     */
+    public bool isEmpty() {
+        return count is 0;
+    }
+
+    /**
+     * Returns all keys in this table as an array.
+     */
+    public String[] keys() {
+        String[] result = new String[count];
+        int next = 0;
+        for (int i = 0; i < elements.length; i = i + 2)
+            if (elements[i] !is null)
+                result[next++] = stringcast( elements[i] );
+        return result;
+    }
+
+    /**
+     * @see Map#keySet()
+     *
+     * Note: This implementation does not conform properly to the
+     * specification in the Map interface. The returned collection will not
+     * be bound to this map and will not remain in sync with this map.
+     */
+    public Set keySet() {
+        Set result = new HashSet(size());
+        for (int i = 0; i < elements.length; i = i + 2) {
+            if (elements[i] !is null) {
+                result.add(elements[i]);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @see Map#put(java.lang.Object, java.lang.Object)
+     */
+    public Object put(Object key, Object value) {
+        if (key is null)
+            throw new NullPointerException();
+        if (value is null)
+            return remove(key);
+
+        // handle the case where we don't have any attributes yet
+        if (elements is null)
+            elements = new Object[DEFAULT_SIZE];
+        if (count is 0) {
+            elements[0] = key;
+            elements[1] = value;
+            count++;
+            return null;
+        }
+
+        int emptyIndex = -1;
+        // replace existing value if it exists
+        for (int i = 0; i < elements.length; i += 2) {
+            if (elements[i] !is null) {
+                if (elements[i].opEquals(key)) {
+                    Object oldValue = elements[i + 1];
+                    elements[i + 1] = value;
+                    return oldValue;
+                }
+            } else if (emptyIndex is -1) {
+                // keep track of the first empty index
+                emptyIndex = i;
+            }
+        }
+        // this will put the emptyIndex greater than the size but
+        // that's ok because we will grow first.
+        if (emptyIndex is -1)
+            emptyIndex = count * 2;
+
+        // otherwise add it to the list of elements.
+        // grow if necessary
+        if (elements.length <= (count * 2))
+            grow();
+        elements[emptyIndex] = key;
+        elements[emptyIndex + 1] = value;
+        count++;
+        return null;
+    }
+    public Object put(String key, Object value) {
+        return put( stringcast(key), value );
+    }
+    public Object put(Object key, String value) {
+        return put( key, stringcast(value) );
+    }
+    public Object put(String key, String value) {
+        return put( stringcast(key), stringcast(value) );
+    }
+
+    /**
+     * @see Map#putAll(java.util.Map)
+     */
+    public void putAll(Map map) {
+        for (Iterator i = map.keySet().iterator(); i.hasNext();) {
+            Object key = i.next();
+            Object value = map.get(key);
+            put(key, value);
+        }
+    }
+
+    /**
+     * @see Map#remove(java.lang.Object)
+     */
+    public Object remove(Object key) {
+        if (elements is null || count is 0)
+            return null;
+        for (int i = 0; i < elements.length; i = i + 2) {
+            if (elements[i] !is null && elements[i].opEquals(key)) {
+                elements[i] = null;
+                Object result = elements[i + 1];
+                elements[i + 1] = null;
+                count--;
+                return result;
+            }
+        }
+        return null;
+    }
+    public Object remove(String key) {
+        return remove( stringcast(key));
+    }
+
+    /* (non-Javadoc
+     * Method declared on IStringPoolParticipant
+     */
+    public void shareStrings(StringPool set) {
+        //copy elements for thread safety
+        Object[] array = elements;
+        if (array is null)
+            return;
+        for (int i = 0; i < array.length; i++) {
+            Object o = array[i];
+            if (cast(ArrayWrapperString)o )
+                array[i] = stringcast(set.add(stringcast( o)));
+        }
+    }
+
+    /**
+     * @see Map#size()
+     */
+    public int size() {
+        return count;
+    }
+
+    /**
+     * Creates a new hash map with the same contents as this map.
+     */
+    private HashMap toHashMap() {
+        HashMap result = new HashMap(size());
+        for (int i = 0; i < elements.length; i = i + 2) {
+            if (elements[i] !is null) {
+                result.put(elements[i], elements[i + 1]);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @see Map#values()
+     *
+     * Note: This implementation does not conform properly to the
+     * specification in the Map interface. The returned collection will not
+     * be bound to this map and will not remain in sync with this map.
+     */
+    public Collection values() {
+        Set result = new HashSet(size());
+        for (int i = 1; i < elements.length; i = i + 2) {
+            if (elements[i] !is null) {
+                result.add(elements[i]);
+            }
+        }
+        return result;
+    }
+
+    public int opApply (int delegate(ref Object value) dg){
+        implMissing(__FILE__, __LINE__ );
+        return 0;
+    }
+    public int opApply (int delegate(ref Object key, ref Object value) dg){
+        implMissing(__FILE__, __LINE__ );
+        return 0;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/OrderedLock.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,318 @@
+/*******************************************************************************
+ * 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.OrderedLock;
+
+import tango.text.convert.Format;
+import tango.core.Thread;
+import tango.io.Stdout;
+import dwt.dwthelper.utils;
+
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.jobs.ILock;
+import dwtx.core.runtime.jobs.ISchedulingRule;
+
+import dwtx.core.internal.jobs.LockManager;
+import dwtx.core.internal.jobs.Queue;
+import dwtx.core.internal.jobs.Semaphore;
+
+/**
+ * A lock used to control write access to an exclusive resource.
+ *
+ * The lock avoids circular waiting deadlocks by detecting the deadlocks
+ * and resolving them through the suspension of all locks owned by one
+ * of the threads involved in the deadlock. This makes it impossible for n such
+ * locks to deadlock while waiting for each other.  The downside is that this means
+ * that during an interval when a process owns a lock, it can be forced
+ * to give the lock up and wait until all locks it requires become
+ * available.  This removes the feature of exclusive access to the
+ * resource in contention for the duration between acquire() and
+ * release() calls.
+ *
+ * The lock implementation prevents starvation by granting the
+ * lock in the same order in which acquire() requests arrive. In
+ * this scheme, starvation is only possible if a thread retains
+ * a lock indefinitely.
+ */
+public class OrderedLock : ILock, ISchedulingRule {
+
+    private static const bool DEBUG = false;
+    /**
+     * Locks are sequentially ordered for debugging purposes.
+     */
+    private static int nextLockNumber = 0;
+    /**
+     * The thread of the operation that currently owns the lock.
+     */
+    private /+volatile+/ Thread currentOperationThread;
+    /**
+     * Records the number of successive acquires in the same
+     * thread. The lock is released only when the depth
+     * reaches zero.
+     */
+    private int depth;
+    /**
+     * The manager that implements the deadlock detection and resolution protocol.
+     */
+    private const LockManager manager;
+    private const int number;
+
+    /**
+     * Queue of semaphores for threads currently waiting
+     * on the lock. This queue is not thread-safe, so access
+     * to this queue must be synchronized on the lock instance.
+     */
+    private const Queue operations;
+
+    /**
+     * Creates a new workspace lock.
+     */
+    this(LockManager manager) {
+
+        operations = new Queue();
+
+        this.manager = manager;
+        this.number = nextLockNumber++;
+    }
+
+    /* (non-Javadoc)
+     * @see Locks.ILock#acquire()
+     */
+    public void acquire() {
+        //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 (acquire(Long.MAX_VALUE))
+                    return;
+            } catch (InterruptedException e) {
+                //ignore and loop
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see Locks.ILock#acquire(long)
+     */
+    public bool acquire(long delay) {
+        implMissing(__FILE__, __LINE__ );
+//         if (Thread.interrupted())
+//             throw new InterruptedException();
+
+        bool success = false;
+        if (delay <= 0)
+            return attempt();
+        Semaphore semaphore = createSemaphore();
+        if (semaphore is null)
+            return true;
+        if (DEBUG)
+            Stdout.formatln("[{}] Operation waiting to be executed... ", Thread.getThis(), this); //$NON-NLS-1$ //$NON-NLS-2$
+        success = doAcquire(semaphore, delay);
+        manager.resumeSuspendedLocks(Thread.getThis());
+        if (DEBUG && success)
+            Stdout.formatln("[{}] Operation started... ", Thread.getThis(), this); //$NON-NLS-1$ //$NON-NLS-2$
+        else if (DEBUG)
+            Stdout.formatln("[{}] Operation timed out... ", Thread.getThis(), this); //$NON-NLS-1$ //$NON-NLS-2$
+        return success;
+    }
+
+    /**
+     * Attempts to acquire the lock.  Returns false if the lock is not available and
+     * true if the lock has been successfully acquired.
+     */
+    private synchronized bool attempt() {
+        //return true if we already own the lock
+        //also, if nobody is waiting, grant the lock immediately
+        if ((currentOperationThread is Thread.getThis()) || (currentOperationThread is null && operations.isEmpty())) {
+            depth++;
+            setCurrentOperationThread(Thread.getThis());
+            return true;
+        }
+        return false;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.ISchedulingRule#contains(dwtx.core.runtime.jobs.ISchedulingRule)
+     */
+    public bool contains(ISchedulingRule rule) {
+        return false;
+    }
+
+    /**
+     * Returns null if acquired and a Semaphore object otherwise. If a
+     * waiting semaphore already exists for this thread, it will be returned,
+     * otherwise a new semaphore will be created, enqueued, and returned.
+     */
+    private synchronized Semaphore createSemaphore() {
+        return attempt() ? null : enqueue(new Semaphore(Thread.getThis()));
+    }
+
+    /**
+     * Attempts to acquire this lock.  Callers will block  until this lock comes available to
+     * them, or until the specified delay has elapsed.
+     */
+    private bool doAcquire(Semaphore semaphore, long delay) {
+        bool success = false;
+        //notify hook to service pending syncExecs before falling asleep
+        if (manager.aboutToWait(this.currentOperationThread)) {
+            //hook granted immediate access
+            //remove semaphore for the lock request from the queue
+            //do not log in graph because this thread did not really get the lock
+            removeFromQueue(semaphore);
+            depth++;
+            manager.addLockThread(currentOperationThread, this);
+            return true;
+        }
+        //Make sure the semaphore is in the queue before we start waiting
+        //It might have been removed from the queue while servicing syncExecs
+        //This is will return our existing semaphore if it is still in the queue
+        semaphore = createSemaphore();
+        if (semaphore is null)
+            return true;
+        manager.addLockWaitThread(Thread.getThis(), this);
+        try {
+            success = semaphore.acquire(delay);
+        } catch (InterruptedException e) {
+            if (DEBUG)
+                Stdout.formatln(Format("[{}] Operation interrupted while waiting... :-|", Thread.getThis())); //$NON-NLS-1$ //$NON-NLS-2$
+            throw e;
+        }
+        if (success) {
+            depth++;
+            updateCurrentOperation();
+        } else {
+            removeFromQueue(semaphore);
+            manager.removeLockWaitThread(Thread.getThis(), this);
+        }
+        return success;
+    }
+
+    /**
+     * Releases this lock from the thread that used to own it.
+     * Grants this lock to the next thread in the queue.
+     */
+    private synchronized void doRelease() {
+        //notify hook
+        manager.aboutToRelease();
+        depth = 0;
+        Semaphore next = cast(Semaphore) operations.peek();
+        setCurrentOperationThread(null);
+        if (next !is null)
+            next.release();
+    }
+
+    /**
+     * If there is another semaphore with the same runnable in the
+     * queue, the other is returned and the new one is not added.
+     */
+    private synchronized Semaphore enqueue(Semaphore newSemaphore) {
+        Semaphore semaphore = cast(Semaphore) operations.get(newSemaphore);
+        if (semaphore is null) {
+            operations.enqueue(newSemaphore);
+            return newSemaphore;
+        }
+        return semaphore;
+    }
+
+    /**
+     * Suspend this lock by granting the lock to the next lock in the queue.
+     * Return the depth of the suspended lock.
+     */
+    protected int forceRelease() {
+        int oldDepth = depth;
+        doRelease();
+        return oldDepth;
+    }
+    package int forceRelease_package() {
+        return forceRelease();
+    }
+
+    /* (non-Javadoc)
+     * @see Locks.ILock#getDepth()
+     */
+    public int getDepth() {
+        return depth;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.ISchedulingRule#isConflicting(dwtx.core.runtime.jobs.ISchedulingRule)
+     */
+    public bool isConflicting(ISchedulingRule rule) {
+        return rule is this;
+    }
+
+    /* (non-Javadoc)
+     * @see Locks.ILock#release()
+     */
+    public void release() {
+        if (depth is 0)
+            return;
+        //only release the lock when the depth reaches zero
+        Assert.isTrue(depth >= 0, "Lock released too many times"); //$NON-NLS-1$
+        if (--depth is 0)
+            doRelease();
+        else
+            manager.removeLockThread(currentOperationThread, this);
+    }
+
+    /**
+     * Removes a semaphore from the queue of waiting operations.
+     *
+     * @param semaphore The semaphore to remove
+     */
+    private synchronized void removeFromQueue(Semaphore semaphore) {
+        operations.remove(semaphore);
+    }
+
+    /**
+     * If newThread is null, release this lock from its previous owner.
+     * If newThread is not null, grant this lock to newThread.
+     */
+    private void setCurrentOperationThread(Thread newThread) {
+        if ((currentOperationThread !is null) && (newThread is null))
+            manager.removeLockThread(currentOperationThread, this);
+        this.currentOperationThread = newThread;
+        if (currentOperationThread !is null)
+            manager.addLockThread(currentOperationThread, this);
+    }
+
+    /**
+     * Forces the lock to be at the given depth.
+     * Used when re-acquiring a suspended lock.
+     */
+    protected void setDepth(int newDepth) {
+        for (int i = depth; i < newDepth; i++) {
+            manager.addLockThread(currentOperationThread, this);
+        }
+        this.depth = newDepth;
+    }
+    package void setDepth_package(int newDepth) {
+        return setDepth(newDepth);
+    }
+
+    /**
+     * For debugging purposes only.
+     */
+    public String toString() {
+        return Format("OrderedLock ({})", number ); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    /**
+     * This lock has just been granted to a new thread (the thread waited for it).
+     * Remove the request from the queue and update both the graph and the lock.
+     */
+    private synchronized void updateCurrentOperation() {
+        operations.dequeue();
+        setCurrentOperationThread(Thread.getThis());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/Queue.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,190 @@
+/*******************************************************************************
+ * 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.Queue;
+
+import tango.text.convert.Format;
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+/**
+ * A Queue of objects.
+ */
+public class Queue {
+    protected Object[] elements_;
+    protected int head;
+    protected bool reuse;
+    protected int tail;
+
+    public this() {
+        this(20, false);
+    }
+
+    /**
+     * The parameter reuse indicates what do you want to happen with
+     * the object reference when you remove it from the queue. If
+     * reuse is false the queue no longer holds a reference to the
+     * object when it is removed. If reuse is true you can use the
+     * method getNextAvailableObject to get an used object, set its
+     * new values and add it again to the queue.
+     */
+    public this(int size, bool reuse) {
+        elements_ = new Object[size];
+        head = tail = 0;
+        this.reuse = reuse;
+    }
+
+    /**
+     * Adds an object to the tail of the queue.
+     */
+    public void enqueue(Object element) {
+        int newTail = increment(tail);
+        if (newTail is head) {
+            grow();
+            newTail = tail + 1;
+        }
+        elements_[tail] = element;
+        tail = newTail;
+    }
+
+    /**
+     * This method does not affect the queue itself. It is only a
+     * helper to decrement an index in the queue.
+     */
+    public int decrement(int index) {
+        return (index is 0) ? (elements_.length - 1) : index - 1;
+    }
+
+    public Iterator elements() {
+        /**/
+        if (isEmpty())
+            return (new ArrayList(0)).iterator();
+
+        /* if head < tail we can use the same array */
+        if (head <= tail)
+            return Arrays.asList(elements_).iterator();
+
+        /* otherwise we need to create a new array */
+        Object[] newElements = new Object[size()];
+        int end = (elements_.length - head);
+        System.arraycopy(elements_, head, newElements, 0, end);
+        System.arraycopy(elements_, 0, newElements, end, tail);
+        return Arrays.asList(newElements).iterator();
+    }
+
+    public Object get(Object o) {
+        int index = head;
+        while (index !is tail) {
+            if (elements_[index].opEquals(o))
+                return elements_[index];
+            index = increment(index);
+        }
+        return null;
+    }
+
+    /**
+     * Removes the given object from the queue. Shifts the underlying array.
+     */
+    public bool remove(Object o) {
+        int index = head;
+        //find the object to remove
+        while (index !is tail) {
+            if (elements_[index].opEquals(o))
+                break;
+            index = increment(index);
+        }
+        //if element wasn't found, return
+        if (index is tail)
+            return false;
+        //store a reference to it (needed for reuse of objects)
+        Object toRemove = elements_[index];
+        int nextIndex = -1;
+        while (index !is tail) {
+            nextIndex = increment(index);
+            if (nextIndex !is tail)
+                elements_[index] = elements_[nextIndex];
+
+            index = nextIndex;
+        }
+        //decrement tail
+        tail = decrement(tail);
+
+        //if objects are reused, transfer the reference that is removed to the end of the queue
+        //otherwise set the element after the last one to null (to avoid duplicate references)
+        elements_[tail] = reuse ? toRemove : null;
+        return true;
+    }
+
+    protected void grow() {
+        int newSize = cast(int) (elements_.length * 1.5);
+        Object[] newElements = new Object[newSize];
+        if (tail >= head)
+            System.arraycopy(elements_, head, newElements, head, size());
+        else {
+            int newHead = newSize - (elements_.length - head);
+            System.arraycopy(elements_, 0, newElements, 0, tail + 1);
+            System.arraycopy(elements_, head, newElements, newHead, (newSize - newHead));
+            head = newHead;
+        }
+        elements_ = newElements;
+    }
+
+    /**
+     * This method does not affect the queue itself. It is only a
+     * helper to increment an index in the queue.
+     */
+    public int increment(int index) {
+        return (index is (elements_.length - 1)) ? 0 : index + 1;
+    }
+
+    public bool isEmpty() {
+        return tail is head;
+    }
+
+    public Object peek() {
+        return elements_[head];
+    }
+
+    /**
+     * Removes an returns the item at the head of the queue.
+     */
+    public Object dequeue() {
+        if (isEmpty())
+            return null;
+        Object result = peek();
+        if (!reuse)
+            elements_[head] = null;
+        head = increment(head);
+        return result;
+    }
+
+    public int size() {
+        return tail > head ? (tail - head) : ((elements_.length - head) + tail);
+    }
+
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("["); //$NON-NLS-1$
+        if (!isEmpty()) {
+            Iterator it = elements();
+            while (true) {
+                sb.append(Format("{}",it.next()));
+                if (it.hasNext())
+                    sb.append(", "); //$NON-NLS-1$
+                else
+                    break;
+            }
+        }
+        sb.append("]"); //$NON-NLS-1$
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/Semaphore.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * 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 Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.internal.jobs.Semaphore;
+
+import tango.core.Thread;
+import tango.core.sync.Mutex;
+import tango.core.sync.Condition;
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+import tango.text.convert.Format;
+
+public class Semaphore {
+    protected long notifications;
+    protected Thread runnable;
+
+    private Mutex mutex;
+    private Condition condition;
+
+    public this(Thread runnable) {
+        mutex = new Mutex;
+        condition = new Condition(mutex);
+        this.runnable = runnable;
+        notifications = 0;
+    }
+
+    /**
+     * Attempts to acquire this semaphore.  Returns true if it was successfully acquired,
+     * and false otherwise.
+     */
+    public bool acquire(long delay) {
+        synchronized(mutex){
+            implMissing( __FILE__, __LINE__ );
+// DWT
+//         if (Thread.interrupted())
+//             throw new InterruptedException();
+            long start = System.currentTimeMillis();
+            long timeLeft = delay;
+            while (true) {
+                if (notifications > 0) {
+                    notifications--;
+                    return true;
+                }
+                if (timeLeft <= 0)
+                    return false;
+                condition.wait(timeLeft/1000.0f);
+                timeLeft = start + delay - System.currentTimeMillis();
+            }
+        }
+    }
+
+    public override int opEquals(Object obj) {
+        return (runnable is (cast(Semaphore) obj).runnable);
+    }
+
+    public override hash_t toHash() {
+        return runnable is null ? 0 : (cast(Object)runnable).toHash();
+    }
+
+    public void release() {
+        synchronized( mutex ){
+            notifications++;
+            condition.notifyAll();
+        }
+    }
+
+    // for debug only
+    public String toString() {
+        return Format("Semaphore({})", cast(Object) runnable ); //$NON-NLS-1$ //$NON-NLS-2$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/StringPool.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2005 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.StringPool;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+/**
+ * A string pool is used for sharing strings in a way that eliminates duplicate
+ * equal strings.  A string pool instance can be maintained over a long period
+ * of time, or used as a temporary structure during a string sharing pass over
+ * a data structure.
+ * <p>
+ * This class is not intended to be subclassed by clients.
+ * </p>
+ *
+ * Note: This class is copied from dwtx.core.resources
+ *
+ * @since 3.1
+ */
+public final class StringPool {
+    private int savings;
+    private const HashMap map;
+
+    public this(){
+        map = new HashMap();
+    }
+
+    /**
+     * Adds a <code>String</code> to the pool.  Returns a <code>String</code>
+     * that is equal to the argument but that is unique within this pool.
+     * @param string The string to add to the pool
+     * @return A string that is equal to the argument.
+     */
+    public String add(String string) {
+        if (string is null)
+            return string;
+        Object result = map.get(string);
+        if (result !is null) {
+            if (stringcast(result) !is string)
+                savings += 44 + 2 * string.length;
+            return stringcast( result );
+        }
+        map.put(string, string);
+        return string;
+    }
+
+    /**
+     * Returns an estimate of the size in bytes that was saved by sharing strings in
+     * the pool.  In particular, this returns the size of all strings that were added to the
+     * pool after an equal string had already been added.  This value can be used
+     * to estimate the effectiveness of a string sharing operation, in order to
+     * determine if or when it should be performed again.
+     *
+     * In some cases this does not precisely represent the number of bytes that
+     * were saved.  For example, say the pool already contains string S1.  Now
+     * string S2, which is equal to S1 but not identical, is added to the pool five
+     * times. This method will return the size of string S2 multiplied by the
+     * number of times it was added, even though the actual savings in this case
+     * is only the size of a single copy of S2.
+     */
+    public int getSavedStringCount() {
+        return savings;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/ThreadJob.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,347 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2007 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.ThreadJob;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+import tango.io.Stdout;
+import tango.core.Thread;
+import tango.core.sync.Mutex;
+import tango.core.sync.Condition;
+
+import dwtx.core.internal.runtime.RuntimeLog;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.OperationCanceledException;
+import dwtx.core.runtime.Status;
+import dwtx.core.runtime.jobs.ISchedulingRule;
+import dwtx.core.runtime.jobs.Job;
+import dwtx.core.internal.jobs.JobManager;
+import dwtx.core.internal.jobs.InternalJob;
+import dwtx.core.internal.jobs.LockManager;
+
+/**
+ * Captures the implicit job state for a given thread.
+ */
+class ThreadJob : Job {
+    /**
+     * The notifier is a shared object used to wake up waiting thread jobs
+     * when another job completes that is releasing a scheduling rule.
+     */
+
+    static const Mutex mutex;
+    static const Condition condition;
+    static this(){
+        mutex = new Mutex();
+        condition = new Condition(mutex);
+    }
+
+    private const JobManager manager;
+    /**
+     * Set to true if this thread job is running in a thread that did
+     * not own a rule already.  This means it needs to acquire the
+     * rule during beginRule, and must release the rule during endRule.
+     */
+    /+protected+/ bool acquireRule = false;
+
+    /**
+     * Indicates that this thread job did report to the progress manager
+     * that it will be blocked, and therefore when it begins it must
+     * be reported to the job manager when it is no longer blocked.
+     */
+    bool isBlocked = false;
+
+    /**
+     * True if this ThreadJob has begun execution
+     */
+    /+protected+/ bool isRunning_ = false;
+
+    /**
+     * Used for diagnosing mismatched begin/end pairs. This field
+     * is only used when in debug mode, to capture the stack trace
+     * of the last call to beginRule.
+     */
+    private RuntimeException lastPush = null;
+    /**
+     * The actual job that is running in the thread that this
+     * ThreadJob represents.  This will be null if this thread
+     * job is capturing a rule acquired outside of a job.
+     */
+    protected package Job realJob;
+    /**
+     * The stack of rules that have been begun in this thread, but not yet ended.
+     */
+    private ISchedulingRule[] ruleStack;
+    /**
+     * Rule stack pointer.
+     */
+    private int top;
+
+    this(JobManager manager, ISchedulingRule rule) {
+        super("Implicit Job"); //$NON-NLS-1$
+        this.manager = manager;
+        setSystem(true);
+        setPriority(Job.INTERACTIVE);
+        ruleStack = new ISchedulingRule[2];
+        top = -1;
+        setRule(rule);
+    }
+
+    /**
+     * An endRule was called that did not match the last beginRule in
+     * the stack.  Report and log a detailed informational message.
+     * @param rule The rule that was popped
+     */
+    private void illegalPop(ISchedulingRule rule) {
+        StringBuffer buf = new StringBuffer("Attempted to endRule: "); //$NON-NLS-1$
+        buf.append(Format("{}",rule));
+        if (top >= 0 && top < ruleStack.length) {
+            buf.append(", does not match most recent begin: "); //$NON-NLS-1$
+            buf.append(Format("{}",ruleStack[top]));
+        } else {
+            if (top < 0)
+                buf.append(", but there was no matching beginRule"); //$NON-NLS-1$
+            else
+                buf.append( Format(", but the rule stack was out of bounds: {}", top)); //$NON-NLS-1$
+        }
+        buf.append(".  See log for trace information if rule tracing is enabled."); //$NON-NLS-1$
+        String msg = buf.toString();
+        if (JobManager.DEBUG || JobManager.DEBUG_BEGIN_END) {
+            Stdout.formatln("{}",msg);
+            Exception t = lastPush is null ? new IllegalArgumentException("") : lastPush;
+            IStatus error = new Status(IStatus.ERROR, JobManager.PI_JOBS, 1, msg, t);
+            RuntimeLog.log(error);
+        }
+        Assert.isLegal(false, msg);
+    }
+
+    /**
+     * Client has attempted to begin a rule that is not contained within
+     * the outer rule.
+     */
+    private void illegalPush(ISchedulingRule pushRule, ISchedulingRule baseRule) {
+        StringBuffer buf = new StringBuffer("Attempted to beginRule: "); //$NON-NLS-1$
+        buf.append(Format("{}",pushRule));
+        buf.append(", does not match outer scope rule: "); //$NON-NLS-1$
+        buf.append(Format("{}",baseRule));
+        String msg = buf.toString();
+        if (JobManager.DEBUG) {
+            Stdout.formatln("{}",msg);
+            IStatus error = new Status(IStatus.ERROR, JobManager.PI_JOBS, 1, msg, new IllegalArgumentException(""));
+            RuntimeLog.log(error);
+        }
+        Assert.isLegal(false, msg);
+
+    }
+
+    /**
+     * Returns true if the monitor is canceled, and false otherwise.
+     * Protects the caller from exception in the monitor implementation.
+     */
+    private bool isCanceled(IProgressMonitor monitor) {
+        try {
+            return monitor.isCanceled();
+        } catch (RuntimeException e) {
+            //logged message should not be translated
+            IStatus status = new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, "ThreadJob.isCanceled", e); //$NON-NLS-1$
+            RuntimeLog.log(status);
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if this thread job was scheduled and actually started running.
+     */
+    bool isRunning() {
+        synchronized(mutex){
+            return isRunning_;
+        }
+    }
+
+    /**
+     * Schedule the job and block the calling thread until the job starts running.
+     * Returns the ThreadJob instance that was started.
+     */
+    ThreadJob joinRun(IProgressMonitor monitor) {
+        if (isCanceled(monitor))
+            throw new OperationCanceledException();
+        //check if there is a blocking thread before waiting
+        InternalJob blockingJob = manager.findBlockingJob_package(this);
+        Thread blocker = blockingJob is null ? null : blockingJob.getThread_package();
+        ThreadJob result = this;
+        try {
+            //just return if lock listener decided to grant immediate access
+            if (manager.getLockManager().aboutToWait(blocker))
+                return this;
+            try {
+                waitStart(monitor, blockingJob);
+                final Thread getThis = Thread.getThis();
+                while (true) {
+                    if (isCanceled(monitor))
+                        throw new OperationCanceledException();
+                    //try to run the job
+                    if (manager.runNow_package(this))
+                        return this;
+                    //update blocking job
+                    blockingJob = manager.findBlockingJob_package(this);
+                    //the rule could have been transferred to this thread while we were waiting
+                    blocker = blockingJob is null ? null : blockingJob.getThread_package();
+                    if (blocker is getThis && cast(ThreadJob)blockingJob ) {
+                        //now we are just the nested acquire case
+                        result = cast(ThreadJob) blockingJob;
+                        result.push(getRule());
+                        result.isBlocked = this.isBlocked;
+                        return result;
+                    }
+                    //just return if lock listener decided to grant immediate access
+                    if (manager.getLockManager().aboutToWait(blocker))
+                        return this;
+                    //must lock instance before calling wait
+                    synchronized (mutex) {
+                        try {
+                            condition.wait(0.250);
+                        } catch (InterruptedException e) {
+                            //ignore
+                        }
+                    }
+                }
+            } finally {
+                if (this is result)
+                    waitEnd(monitor);
+            }
+        } finally {
+            manager.getLockManager().aboutToRelease();
+        }
+    }
+
+    /**
+     * Pops a rule. Returns true if it was the last rule for this thread
+     * job, and false otherwise.
+     */
+    bool pop(ISchedulingRule rule) {
+        if (top < 0 || ruleStack[top] !is rule)
+            illegalPop(rule);
+        ruleStack[top--] = null;
+        return top < 0;
+    }
+
+    /**
+     * Adds a new scheduling rule to the stack of rules for this thread. Throws
+     * a runtime exception if the new rule is not compatible with the base
+     * scheduling rule for this thread.
+     */
+    void push(ISchedulingRule rule) {
+        ISchedulingRule baseRule = getRule();
+        if (++top >= ruleStack.length) {
+            ISchedulingRule[] newStack = new ISchedulingRule[ruleStack.length * 2];
+            SimpleType!(ISchedulingRule).arraycopy(ruleStack, 0, newStack, 0, ruleStack.length);
+            ruleStack = newStack;
+        }
+        ruleStack[top] = rule;
+        if (JobManager.DEBUG_BEGIN_END)
+            lastPush = new RuntimeException()/+).fillInStackTrace()+/;
+        //check for containment last because we don't want to fail again on endRule
+        if (baseRule !is null && rule !is null && !baseRule.contains(rule))
+            illegalPush(rule, baseRule);
+    }
+
+    /**
+     * Reset all of this job's fields so it can be reused.  Returns false if
+     * reuse is not possible
+     */
+    bool recycle() {
+        //don't recycle if still running for any reason
+        if (getState() !is Job.NONE)
+            return false;
+        //clear and reset all fields
+        acquireRule = isRunning_ = isBlocked = false;
+        realJob = null;
+        setRule(null);
+        setThread(null);
+        if (ruleStack.length !is 2)
+            ruleStack = new ISchedulingRule[2];
+        else
+            ruleStack[0] = ruleStack[1] = null;
+        top = -1;
+        return true;
+    }
+
+    /** (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.Job#run(dwtx.core.runtime.IProgressMonitor)
+     */
+    public IStatus run(IProgressMonitor monitor) {
+        synchronized (this) {
+            isRunning_ = true;
+        }
+        return ASYNC_FINISH;
+    }
+
+    /**
+     * Records the job that is actually running in this thread, if any
+     * @param realJob The running job
+     */
+    void setRealJob(Job realJob) {
+        this.realJob = realJob;
+    }
+
+    /**
+     * Returns true if this job should cause a self-canceling job
+     * to cancel itself, and false otherwise.
+     */
+    bool shouldInterrupt() {
+        return realJob is null ? true : !realJob.isSystem();
+    }
+
+    /* (non-javadoc)
+     * For debugging purposes only
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer("ThreadJob"); //$NON-NLS-1$
+        buf.append('(').append(Format("{}",realJob)).append(',').append('[');
+        for (int i = 0; i <= top && i < ruleStack.length; i++)
+            buf.append(Format("{}",ruleStack[i])).append(',');
+        buf.append(']').append(')');
+        return buf.toString();
+    }
+
+    /**
+     * Reports that this thread was blocked, but is no longer blocked and is able
+     * to proceed.
+     * @param monitor The monitor to report unblocking to.
+     */
+    private void waitEnd(IProgressMonitor monitor) {
+        final LockManager lockManager = manager.getLockManager();
+        final Thread getThis = Thread.getThis();
+        if (isRunning()) {
+            lockManager.addLockThread(getThis, getRule());
+            //need to re-acquire any locks that were suspended while this thread was blocked on the rule
+            lockManager.resumeSuspendedLocks(getThis);
+        } else {
+            //tell lock manager that this thread gave up waiting
+            lockManager.removeLockWaitThread(getThis, getRule());
+        }
+    }
+
+    /**
+     * Indicates the start of a wait on a scheduling rule. Report the
+     * blockage to the progress manager and update the lock manager.
+     * @param monitor The monitor to report blocking to
+     * @param blockingJob The job that is blocking this thread, or <code>null</code>
+     */
+    private void waitStart(IProgressMonitor monitor, InternalJob blockingJob) {
+        manager.getLockManager().addLockWaitThread(Thread.getThis(), getRule());
+        isBlocked = true;
+        manager.reportBlocked(monitor, blockingJob);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/Worker.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * 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 Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.internal.jobs.Worker;
+
+import tango.core.Thread;
+import tango.text.convert.Format;
+import dwt.dwthelper.utils;
+
+import dwtx.core.internal.runtime.RuntimeLog;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.OperationCanceledException;
+import dwtx.core.runtime.Status;
+import dwtx.core.runtime.jobs.Job;
+import dwtx.osgi.util.NLS;
+
+import dwtx.core.internal.jobs.InternalJob;
+import dwtx.core.internal.jobs.WorkerPool;
+import dwtx.core.internal.jobs.JobMessages;
+import dwtx.core.internal.jobs.JobManager;
+
+/**
+ * A worker thread processes jobs supplied to it by the worker pool.  When
+ * the worker pool gives it a null job, the worker dies.
+ */
+public class Worker : Thread {
+    //worker number used for debugging purposes only
+    private static int nextWorkerNumber = 0;
+    private /+volatile+/ InternalJob currentJob_;
+    private final WorkerPool pool;
+
+    public this(WorkerPool pool) {
+        super(&run);
+        this.name = Format("Worker-{}", nextWorkerNumber++); //$NON-NLS-1$
+        this.pool = pool;
+        //set the context loader to avoid leaking the current context loader
+        //for the thread that spawns this worker (bug 98376)
+// DWT
+//         setContextClassLoader(pool.defaultContextLoader);
+    }
+
+    /**
+     * Returns the currently running job, or null if none.
+     */
+    public Job currentJob() {
+        return cast(Job) currentJob_;
+    }
+
+    private IStatus handleException(InternalJob job, Exception t) {
+        String message = NLS.bind(JobMessages.jobs_internalError, job.getName_package());
+        return new Status(IStatus.ERROR, JobManager.PI_JOBS, JobManager.PLUGIN_ERROR, message, t);
+    }
+
+    public void run() {
+        this.priority((PRIORITY_MAX-PRIORITY_MIN)/2); // DWT normal priority
+        try {
+            while ((currentJob_ = pool.startJob_package(this)) !is null) {
+                currentJob_.setThread_package(this);
+                IStatus result = Status.OK_STATUS;
+                try {
+                    result = currentJob_.run_package(currentJob_.getProgressMonitor());
+                } catch (OperationCanceledException e) {
+                    result = Status.CANCEL_STATUS;
+                } catch (Exception e) {
+                    result = handleException(currentJob_, e);
+//                 } catch (ThreadDeath e) {
+//                     //must not consume thread death
+//                     result = handleException(currentJob_, e);
+//                     throw e;
+//                 } catch (Error e) {
+//                     result = handleException(currentJob_, e);
+                } finally {
+                    //clear interrupted state for this thread
+                    implMissing( __FILE__, __LINE__ );
+// DWT
+//                     Thread.interrupted();
+
+
+                    //result must not be null
+                    if (result is null)
+                        result = handleException(currentJob_, new NullPointerException());
+                    pool.endJob_package(currentJob_, result);
+                    if ((result.getSeverity() & (IStatus.ERROR | IStatus.WARNING)) !is 0)
+                        RuntimeLog.log(result);
+                    currentJob_ = null;
+                }
+            }
+        } catch (Exception t) {
+            ExceptionPrintStackTrace(t);
+        } finally {
+            currentJob_ = null;
+            pool.endWorker_package(this);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/jobs/WorkerPool.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,296 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2007 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.WorkerPool;
+
+import tango.core.Thread;
+import tango.core.sync.Mutex;
+import tango.core.sync.Condition;
+import tango.text.convert.Format;
+import dwt.dwthelper.utils;
+
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.jobs.Job;
+import dwtx.core.internal.jobs.JobManager;
+import dwtx.core.internal.jobs.Worker;
+
+import dwtx.core.internal.jobs.InternalJob;
+import dwtx.core.internal.jobs.ThreadJob;
+
+/**
+ * Maintains a pool of worker threads. Threads are constructed lazily as
+ * required, and are eventually discarded if not in use for awhile. This class
+ * maintains the thread creation/destruction policies for the job manager.
+ *
+ * Implementation note: all the data structures of this class are protected
+ * by the instance's object monitor.  To avoid deadlock with third party code,
+ * this lock is never held when calling methods outside this class that may in
+ * turn use locks.
+ */
+class WorkerPool {
+
+    protected Mutex mutex;
+    protected Condition condition;
+
+
+    /**
+     * Threads not used by their best before timestamp are destroyed.
+     */
+    private static const int BEST_BEFORE = 60000;
+    /**
+     * There will always be at least MIN_THREADS workers in the pool.
+     */
+    private static const int MIN_THREADS = 1;
+    /**
+     * Use the busy thread count to avoid starting new threads when a living
+     * thread is just doing house cleaning (notifying listeners, etc).
+     */
+    private int busyThreads = 0;
+
+    /**
+     * The default context class loader to use when creating worker threads.
+     */
+//     protected const ClassLoader defaultContextLoader;
+
+    /**
+     * Records whether new worker threads should be daemon threads.
+     */
+    private bool isDaemon = false;
+
+    private JobManager manager;
+    /**
+     * The number of workers in the threads array
+     */
+    private int numThreads = 0;
+    /**
+     * The number of threads that are currently sleeping
+     */
+    private int sleepingThreads = 0;
+    /**
+     * The living set of workers in this pool.
+     */
+    private Worker[] threads;
+
+    protected package this(JobManager manager) {
+        threads = new Worker[10];
+        this.manager = manager;
+        mutex = new Mutex;
+        condition = new Condition(mutex);
+//         this.defaultContextLoader = Thread.currentThread().getContextClassLoader();
+    }
+
+    /**
+     * Adds a worker to the list of workers.
+     */
+    private void add(Worker worker) {
+        synchronized(mutex){
+            int size = threads.length;
+            if (numThreads + 1 > size) {
+                Worker[] newThreads = new Worker[2 * size];
+                System.arraycopy(threads, 0, newThreads, 0, size);
+                threads = newThreads;
+            }
+            threads[numThreads++] = worker;
+        }
+    }
+
+    private void decrementBusyThreads() {
+        synchronized(mutex){
+            //impossible to have less than zero busy threads
+            if (--busyThreads < 0) {
+                if (JobManager.DEBUG)
+                    Assert.isTrue(false, Integer.toString(busyThreads));
+                busyThreads = 0;
+            }
+        }
+    }
+
+    /**
+     * Signals the end of a job.  Note that this method can be called under
+     * OutOfMemoryError conditions and thus must be paranoid about allocating objects.
+     */
+    protected void endJob(InternalJob job, IStatus result) {
+        decrementBusyThreads();
+        //need to end rule in graph before ending job so that 2 threads
+        //do not become the owners of the same rule in the graph
+        if ((job.getRule_package() !is null) && !(cast(ThreadJob)job )) {
+            //remove any locks this thread may be owning on that rule
+            manager.getLockManager().removeLockCompletely(Thread.getThis(), job.getRule_package());
+        }
+        manager.endJob_package(job, result, true);
+        //ensure this thread no longer owns any scheduling rules
+        manager.implicitJobs.endJob(job);
+    }
+    package void endJob_package(InternalJob job, IStatus result) {
+        endJob(job, result);
+    }
+
+    /**
+     * Signals the death of a worker thread.  Note that this method can be called under
+     * OutOfMemoryError conditions and thus must be paranoid about allocating objects.
+     */
+    protected void endWorker(Worker worker) {
+        synchronized(mutex){
+            if (remove(worker) && JobManager.DEBUG)
+                JobManager.debug_(Format("worker removed from pool: {}", worker)); //$NON-NLS-1$
+        }
+    }
+    package void endWorker_package(Worker worker) {
+        endWorker(worker);
+    }
+
+    private void incrementBusyThreads() {
+        synchronized(mutex){
+            //impossible to have more busy threads than there are threads
+            if (++busyThreads > numThreads) {
+                if (JobManager.DEBUG)
+                    Assert.isTrue(false, Format( "{},{}", busyThreads, numThreads));
+                busyThreads = numThreads;
+            }
+        }
+    }
+
+    /**
+     * Notification that a job has been added to the queue. Wake a worker,
+     * creating a new worker if necessary. The provided job may be null.
+     */
+    protected package void jobQueued() {
+        synchronized(mutex){
+            //if there is a sleeping thread, wake it up
+            if (sleepingThreads > 0) {
+                condition.notify();
+                return;
+            }
+            //create a thread if all threads are busy
+            if (busyThreads >= numThreads) {
+                Worker worker = new Worker(this);
+                worker.isDaemon(isDaemon);
+                add(worker);
+                if (JobManager.DEBUG)
+                    JobManager.debug_(Format("worker added to pool: {}", worker)); //$NON-NLS-1$
+                worker.start();
+                return;
+            }
+        }
+    }
+
+    /**
+     * Remove a worker thread from our list.
+     * @return true if a worker was removed, and false otherwise.
+     */
+    private bool remove(Worker worker) {
+        synchronized(mutex){
+            for (int i = 0; i < threads.length; i++) {
+                if (threads[i] is worker) {
+                    System.arraycopy(threads, i + 1, threads, i, numThreads - i - 1);
+                    threads[--numThreads] = null;
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Sets whether threads created in the worker pool should be daemon threads.
+     */
+    void setDaemon(bool value) {
+        this.isDaemon = value;
+    }
+
+    protected void shutdown() {
+        synchronized(mutex){
+            condition.notifyAll();
+        }
+    }
+    package void shutdown_package() {
+        shutdown();
+    }
+
+    /**
+     * Sleep for the given duration or until woken.
+     */
+    private void sleep(long duration) {
+        synchronized(mutex){
+            sleepingThreads++;
+            busyThreads--;
+            if (JobManager.DEBUG)
+                JobManager.debug_(Format("worker sleeping for: {}ms", duration)); //$NON-NLS-1$ //$NON-NLS-2$
+            try {
+                condition.wait(duration/1000.0f);
+            } catch (InterruptedException e) {
+                if (JobManager.DEBUG)
+                    JobManager.debug_("worker interrupted while waiting... :-|"); //$NON-NLS-1$
+            } finally {
+                sleepingThreads--;
+                busyThreads++;
+            }
+        }
+    }
+
+    /**
+     * Returns a new job to run. Returns null if the thread should die.
+     */
+    protected InternalJob startJob(Worker worker) {
+        //if we're above capacity, kill the thread
+        synchronized (mutex) {
+            if (!manager.isActive_package()) {
+                //must remove the worker immediately to prevent all threads from expiring
+                endWorker(worker);
+                return null;
+            }
+            //set the thread to be busy now in case of reentrant scheduling
+            incrementBusyThreads();
+        }
+        Job job = null;
+        try {
+            job = manager.startJob_package();
+            //spin until a job is found or until we have been idle for too long
+            long idleStart = System.currentTimeMillis();
+            while (manager.isActive_package() && job is null) {
+                long hint = manager.sleepHint_package();
+                if (hint > 0)
+                    sleep(Math.min(hint, BEST_BEFORE));
+                job = manager.startJob_package();
+                //if we were already idle, and there are still no new jobs, then
+                // the thread can expire
+                synchronized (mutex) {
+                    if (job is null && (System.currentTimeMillis() - idleStart > BEST_BEFORE) && (numThreads - busyThreads) > MIN_THREADS) {
+                        //must remove the worker immediately to prevent all threads from expiring
+                        endWorker(worker);
+                        return null;
+                    }
+                }
+            }
+            if (job !is null) {
+                //if this job has a rule, then we are essentially acquiring a lock
+                if ((job.getRule() !is null) && !(cast(ThreadJob)job )) {
+                    //don't need to re-aquire locks because it was not recorded in the graph
+                    //that this thread waited to get this rule
+                    manager.getLockManager().addLockThread(Thread.getThis(), job.getRule());
+                }
+                //see if we need to wake another worker
+                if (manager.sleepHint_package() <= 0)
+                    jobQueued();
+            }
+        } finally {
+            //decrement busy thread count if we're not running a job
+            if (job is null)
+                decrementBusyThreads();
+        }
+        return job;
+    }
+    package InternalJob startJob_package(Worker worker) {
+        return startJob(worker);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/internal/runtime/RuntimeLog.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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 Corporation - initial API and implementation
+ *     Julian Chen - fix for bug #92572, jclRM
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.internal.runtime.RuntimeLog;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.core.runtime.ILogListener;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.OperationCanceledException;
+
+/**
+ * NOT API!!!  This log infrastructure was split from the InternalPlatform.
+ *
+ * @since dwtx.equinox.common 3.2
+ */
+// XXX this must be removed and replaced with something more reasonable
+public final class RuntimeLog {
+
+    private static ArrayList logListeners = new ArrayList(5);
+
+    /**
+     * Keep the messages until the first log listener is registered.
+     * Once first log listeners is registred, it is going to receive
+     * all status messages accumulated during the period when no log
+     * listener was available.
+     */
+    private static ArrayList queuedMessages = new ArrayList(5);
+
+    /**
+     * See dwtx.core.runtime.Platform#addLogListener(ILogListener)
+     */
+    public static void addLogListener(ILogListener listener) {
+        synchronized (logListeners) {
+            bool firstListener = (logListeners.size() is 0);
+            // replace if already exists (Set behaviour but we use an array
+            // since we want to retain order)
+            logListeners.remove(listener);
+            logListeners.add(listener);
+            if (firstListener) {
+                for (Iterator i = queuedMessages.iterator(); i.hasNext();) {
+                    try {
+                        IStatus recordedMessage = cast(IStatus) i.next();
+                        listener.logging(recordedMessage, IRuntimeConstants.PI_RUNTIME);
+                    } catch (Exception e) {
+                        handleException(e);
+                    } catch (LinkageError e) {
+                        handleException(e);
+                    }
+                }
+                queuedMessages.clear();
+            }
+        }
+    }
+
+    /**
+     * See dwtx.core.runtime.Platform#removeLogListener(ILogListener)
+     */
+    public static void removeLogListener(ILogListener listener) {
+        synchronized (logListeners) {
+            logListeners.remove(listener);
+        }
+    }
+
+    /**
+     * Checks if the given listener is present
+     */
+    public static bool contains(ILogListener listener) {
+        synchronized (logListeners) {
+            return logListeners.contains(listener);
+        }
+    }
+
+    /**
+     * Notifies all listeners of the platform log.
+     */
+    public static void log(IStatus status) {
+        // create array to avoid concurrent access
+        ILogListener[] listeners;
+        synchronized (logListeners) {
+            listeners = arraycast!(ILogListener)( logListeners.toArray());
+            if (listeners.length is 0) {
+                queuedMessages.add(status);
+                return;
+            }
+        }
+        for (int i = 0; i < listeners.length; i++) {
+            try {
+                listeners[i].logging(status, IRuntimeConstants.PI_RUNTIME);
+            } catch (Exception e) {
+                handleException(e);
+            } catch (LinkageError e) {
+                handleException(e);
+            }
+        }
+    }
+
+    private static void handleException(Exception e) {
+        if (!(cast(OperationCanceledException)e )) {
+            // Got a error while logging. Don't try to log again, just put it into stderr
+            ExceptionPrintStackTrace(e);
+        }
+    }
+
+    /**
+     * Helps determine if any listeners are registered with the logging mechanism.
+     * @return true if no listeners are registered
+     */
+    public static bool isEmpty() {
+        synchronized (logListeners) {
+            return (logListeners.size() is 0);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/PlatformObject.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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 Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.PlatformObject;
+
+import dwt.dwthelper.utils;
+
+import dwtx.core.runtime.IAdaptable;
+// import dwtx.core.internal.runtime.AdapterManager;
+
+/**
+ * An abstract superclass implementing the <code>IAdaptable</code>
+ * interface. <code>getAdapter</code> invocations are directed
+ * to the platform's adapter manager.
+ * <p>
+ * Note: In situations where it would be awkward to subclass this
+ * class, the same affect can be achieved simply by implementing
+ * the {@link IAdaptable} interface and explicitly forwarding
+ * the <code>getAdapter</code> request to an implementation
+ * of the {@link IAdapterManager} service. The method would look like:
+ * <pre>
+ *     public Object getAdapter(Class adapter) {
+ *         IAdapterManager manager = ...;//lookup the IAdapterManager service
+ *         return manager.getAdapter(this, adapter);
+ *     }
+ * </pre>
+ * </p><p>
+ * This class can be used without OSGi running.
+ * </p><p>
+ * Clients may subclass.
+ * </p>
+ *
+ * @see IAdapterManager
+ * @see IAdaptable
+ */
+public abstract class PlatformObject : IAdaptable {
+    /**
+     * Constructs a new platform object.
+     */
+    public this() {
+        super();
+    }
+
+    /**
+     * Returns an object which is an instance of the given class
+     * associated with this object. Returns <code>null</code> if
+     * no such object can be found.
+     * <p>
+     * This implementation of the method declared by <code>IAdaptable</code>
+     * passes the request along to the platform's adapter manager; roughly
+     * <code>Platform.getAdapterManager().getAdapter(this, adapter)</code>.
+     * Subclasses may override this method (however, if they do so, they
+     * should invoke the method on their superclass to ensure that the
+     * Platform's adapter manager is consulted).
+     * </p>
+     *
+     * @param adapter the class to adapt to
+     * @return the adapted object or <code>null</code>
+     * @see IAdaptable#getAdapter(Class)
+     */
+    public Object getAdapter(ClassInfo adapter) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+//         return AdapterManager.getDefault().getAdapter(this, adapter);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/jobs/IJobChangeEvent.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 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.runtime.jobs.IJobChangeEvent;
+
+import dwt.dwthelper.utils;
+
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.jobs.Job;
+
+/**
+ * An event describing a change to the state of a job.
+ *
+ * @see IJobChangeListener
+ * @since 3.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IJobChangeEvent {
+    /**
+     * The amount of time in milliseconds to wait after scheduling the job before it
+     * should be run, or <code>-1</code> if not applicable for this type of event.
+     * This value is only applicable for the <code>scheduled</code> event.
+     *
+     * @return the delay time for this event
+     */
+    public long getDelay();
+
+    /**
+     * The job on which this event occurred.
+     *
+     * @return the job for this event
+     */
+    public Job getJob();
+
+    /**
+     * The result returned by the job's run method, or <code>null</code> if
+     * not applicable.  This value is only applicable for the <code>done</code> event.
+     *
+     * @return the status for this event
+     */
+    public IStatus getResult();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/jobs/IJobChangeListener.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * 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 Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.jobs.IJobChangeListener;
+
+import dwt.dwthelper.utils;
+import dwtx.core.runtime.jobs.IJobChangeEvent;
+
+/**
+ * Callback interface for clients interested in being notified when jobs change state.
+ * <p>
+ * A single job listener instance can be added either to the job manager, for notification
+ * of all scheduled jobs, or to any set of individual jobs.  A single listener instance should
+ * not be added to both the job manager, and to individual jobs (such a listener may
+ * receive duplicate notifications).
+ * </p><p>
+ * Clients should not rely on the result of the <code>Job#getState()</code>
+ * method on jobs for which notification is occurring. Listeners are notified of
+ * all job state changes, but whether the state change occurs before, during, or
+ * after listeners are notified is unspecified.
+ * </p><p>
+ * Clients may implement this interface.
+ * </p>
+ * @see JobChangeAdapter
+ * @see IJobManager#addJobChangeListener(IJobChangeListener)
+ * @see IJobManager#removeJobChangeListener(IJobChangeListener)
+ * @see Job#addJobChangeListener(IJobChangeListener)
+ * @see Job#getState()
+ * @see Job#removeJobChangeListener(IJobChangeListener)
+ * @since 3.0
+ */
+public interface IJobChangeListener {
+    /**
+     * Notification that a job is about to be run. Listeners are allowed to sleep, cancel,
+     * or change the priority of the job before it is started (and as a result may prevent
+     * the run from actually occurring).
+     *
+     * @param event the event details
+     */
+    public void aboutToRun(IJobChangeEvent event);
+
+    /**
+     * Notification that a job was previously sleeping and has now been rescheduled
+     * to run.
+     *
+     * @param event the event details
+     */
+    public void awake(IJobChangeEvent event);
+
+    /**
+     * Notification that a job has completed execution, either due to cancelation, successful
+     * completion, or failure.  The event status object indicates how the job finished,
+     * and the reason for failure, if applicable.
+     *
+     * @param event the event details
+     */
+    public void done(IJobChangeEvent event);
+
+    /**
+     * Notification that a job has started running.
+     *
+     * @param event the event details
+     */
+    public void running(IJobChangeEvent event);
+
+    /**
+     * Notification that a job is being added to the queue of scheduled jobs.
+     * The event details includes the scheduling delay before the job should start
+     * running.
+     *
+     * @param event the event details, including the job instance and the scheduling
+     * delay
+     */
+    public void scheduled(IJobChangeEvent event);
+
+    /**
+     * Notification that a job was waiting to run and has now been put in the
+     * sleeping state.
+     *
+     * @param event the event details
+     */
+    public void sleeping(IJobChangeEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/jobs/IJobManager.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,420 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 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 Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.jobs.IJobManager;
+
+import dwt.dwthelper.utils;
+
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.OperationCanceledException;
+import dwtx.core.runtime.jobs.IJobChangeListener;
+import dwtx.core.runtime.jobs.ISchedulingRule;
+import dwtx.core.runtime.jobs.Job;
+import dwtx.core.runtime.jobs.ILock;
+import dwtx.core.runtime.jobs.LockListener;
+
+import dwtx.core.runtime.jobs.ProgressProvider;
+import tango.core.Thread;
+
+/**
+ * The job manager provides facilities for scheduling, querying, and maintaining jobs
+ * and locks.  In particular, the job manager provides the following services:
+ * <ul>
+ * <li>Maintains a queue of jobs that are waiting to be run.  Items can be added to
+ * the queue using the <code>schedule</code> method.</li>
+ * <li>Allows manipulation of groups of jobs called job families.  Job families can
+ * be canceled, put to sleep, or woken up atomically.  There is also a mechanism
+ * for querying the set of known jobs in a given family.</li>
+ * <li>Allows listeners to find out about progress on running jobs, and to find out
+ * when jobs have changed states.</li>
+ * <li>Provides a factory for creating lock objects.  Lock objects are smart monitors
+ * that have strategies for avoiding deadlock.</li>
+ * <li>Provide feedback to a client that is waiting for a given job or family of jobs
+ * to complete.</li>
+ * </ul>
+ *
+ * @see Job
+ * @see ILock
+ * @since 3.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IJobManager {
+    /**
+     * A system property key indicating whether the job manager should create
+     * job threads as daemon threads.  Set to <code>true</code> to force all worker
+     * threads to be created as daemon threads. Set to <code>false</code> to force
+     * all worker threads to be created as non-daemon threads.
+     * @since 3.3
+     */
+    public static final String PROP_USE_DAEMON_THREADS = "eclipse.jobs.daemon"; //$NON-NLS-1$
+
+    /**
+     * Registers a job listener with the job manager.
+     * Has no effect if an identical listener is already registered.
+     *
+     * @param listener the listener to be added
+     * @see #removeJobChangeListener(IJobChangeListener)
+     * @see IJobChangeListener
+     */
+    public void addJobChangeListener(IJobChangeListener listener);
+
+    /**
+     * Begins applying this rule in the calling thread.  If the rule conflicts with another
+     * rule currently running in another thread, this method blocks until there are
+     * no conflicting rules.  Calls to <tt>beginRule</tt> must eventually be followed
+     * by a matching call to <tt>endRule</tt> in the same thread and with the identical
+     * rule instance.
+     * <p>
+     * Rules can be nested only if the rule for the inner <tt>beginRule</tt>
+     * is contained within the rule for the outer <tt>beginRule</tt>.  Rule containment
+     * is tested with the API method <tt>ISchedulingRule.contains</tt>.  Also, begin/end
+     * pairs must be strictly nested.  Only the rule that has most recently begun
+     * can be ended at any given time.
+     * <p>
+     * A rule of <code>null</code> can be used, but will be ignored for scheduling
+     * purposes.  The outermost non-null rule in the thread will be used for scheduling. A
+     * <code>null</code> rule that is begun must still be ended.
+     * <p>
+     * If this method is called from within a job that has a scheduling rule, the
+     * given rule must also be contained within the rule for the running job.
+     * <p>
+     * Note that <tt>endRule</tt> must be called even if <tt>beginRule</tt> fails.
+     * The recommended usage is:
+     * <pre>
+     * final ISchedulingRule rule = ...;
+     * try {
+     *  manager.beginRule(rule, monitor);
+     * } finally {
+     *  manager.endRule(rule);
+     * }
+     * </pre>
+     *
+     * @param rule the rule to begin applying in this thread, or <code>null</code>
+     * @param monitor a progress monitor, or <code>null</code> if progress
+     *    reporting and cancellation are not desired
+     * @throws IllegalArgumentException if the rule is not strictly nested within
+     *  all other rules currently active for this thread
+     * @throws OperationCanceledException if the supplied monitor reports cancelation
+     *  before the rule becomes available
+     * @see ISchedulingRule#contains(ISchedulingRule)
+     */
+    public void beginRule(ISchedulingRule rule, IProgressMonitor monitor);
+
+    /**
+     * Cancels all jobs in the given job family.  Jobs in the family that are currently waiting
+     * will be removed from the queue.  Sleeping jobs will be discarded without having
+     * a chance to wake up.  Currently executing jobs will be asked to cancel but there
+     * is no guarantee that they will do so.
+     *
+     * @param family the job family to cancel, or <code>null</code> to cancel all jobs
+     * @see Job#belongsTo(Object)
+     */
+    public void cancel(Object family);
+
+    /**
+     * Returns a progress monitor that can be used to provide
+     * aggregated progress feedback on a set of running jobs. A user
+     * interface will typically group all jobs in a progress group together,
+     * providing progress feedback for individual jobs as well as aggregated
+     * progress for the entire group.  Jobs in the group may be run sequentially,
+     * in parallel, or some combination of the two.
+     * <p>
+     * Recommended usage (this snippet runs two jobs in sequence in a
+     * single progress group):
+     * <pre>
+     *    Job parseJob, compileJob;
+     *    IProgressMonitor pm = Platform.getJobManager().createProgressGroup();
+     *    try {
+     *       pm.beginTask("Building", 10);
+     *       parseJob.setProgressGroup(pm, 5);
+     *       parseJob.schedule();
+     *       compileJob.setProgressGroup(pm, 5);
+     *       compileJob.schedule();
+     *       parseJob.join();
+     *       compileJob.join();
+     *    } finally {
+     *       pm.done();
+     *    }
+     * </pre>
+     *
+     * @see Job#setProgressGroup(IProgressMonitor, int)
+     * @see IProgressMonitor
+     * @return a progress monitor
+     */
+    public IProgressMonitor createProgressGroup();
+
+    /**
+     * Returns the job that is currently running in this thread, or <code>null</code> if there
+     * is no currently running job.
+     *
+     * @return the job or <code>null</code>
+     */
+    public Job currentJob();
+
+    /**
+     * Ends the application of a rule to the calling thread.  Calls to <tt>endRule</tt>
+     * must be preceded by a matching call to <tt>beginRule</tt> in the same thread
+     * with an identical rule instance.
+     * <p>
+     * Rules can be nested only if the rule for the inner <tt>beginRule</tt>
+     * is contained within the rule for the outer <tt>beginRule</tt>.  Also, begin/end
+     * pairs must be strictly nested.  Only the rule that has most recently begun
+     * can be ended at any given time.
+     *
+     * @param rule the rule to end applying in this thread
+     * @throws IllegalArgumentException if this method is called on a rule for which
+     * there is no matching begin, or that does not match the most recent begin.
+     * @see ISchedulingRule#contains(ISchedulingRule)
+     */
+    public void endRule(ISchedulingRule rule);
+
+    /**
+     * Returns all waiting, executing and sleeping jobs belonging
+     * to the given family. If no jobs are found, an empty array is returned.
+     *
+     * @param family the job family to find, or <code>null</code> to find all jobs
+     * @return the job array
+     * @see Job#belongsTo(Object)
+     */
+    public Job[] find(Object family);
+
+    /**
+     * Returns whether the job manager is currently idle.  The job manager is
+     * idle if no jobs are currently running or waiting to run.
+     *
+     * @return <code>true</code> if the job manager is idle, and
+     * <code>false</code> otherwise
+     * @since 3.1
+     */
+    public bool isIdle();
+
+    /**
+     * Returns whether the job manager is currently suspended.
+     *
+     * @return <code>true</code> if the job manager is suspended, and
+     * <code>false</code> otherwise
+     * @since 3.4
+     * @see #suspend()
+     * @see #resume()
+     */
+    public bool isSuspended();
+
+    /**
+     * Waits until all jobs of the given family are finished.  This method will block the
+     * calling thread until all such jobs have finished executing, or until this thread is
+     * interrupted.   If there are no jobs in
+     * the family that are currently waiting, running, or sleeping, this method returns
+     * immediately.  Feedback on how the join is progressing is provided to a  progress
+     * monitor.
+     * <p>
+     * If this method is called while the job manager is suspended, only jobs
+     * that are currently running will be joined; Once there are no jobs
+     * in the family in the {@link Job#RUNNING} state, this method returns.
+     * </p>
+     * <p>
+     * Note that there is a deadlock risk when using join.  If the calling thread owns
+     * a lock or object monitor that the joined thread is waiting for, deadlock
+     * will occur. This method can also result in starvation of the current thread if
+     * another thread continues to add jobs of the given family, or if a
+     * job in the given family reschedules itself in an infinite loop.
+     * </p>
+     *
+     * @param family the job family to join, or <code>null</code> to join all jobs.
+     * @param monitor Progress monitor for reporting progress on how the
+     * wait is progressing, or <code>null</code> if no progress monitoring is required.
+     * @exception InterruptedException if this thread is interrupted while waiting
+     * @exception OperationCanceledException if the progress monitor is canceled while waiting
+     * @see Job#belongsTo(Object)
+     * @see #suspend()
+     */
+    public void join(Object family, IProgressMonitor monitor);
+
+    /**
+     * Creates a new lock object.  All lock objects supplied by the job manager
+     * know about each other and will always avoid circular deadlock amongst
+     * themselves.
+     *
+     * @return the new lock object
+     */
+    public ILock newLock();
+
+    /**
+     * Removes a job listener from the job manager.
+     * Has no effect if an identical listener is not already registered.
+     *
+     * @param listener the listener to be removed
+     * @see #addJobChangeListener(IJobChangeListener)
+     * @see IJobChangeListener
+     */
+    public void removeJobChangeListener(IJobChangeListener listener);
+
+    /**
+     * Resumes execution of jobs after a previous <code>suspend</code>.  All
+     * jobs that were sleeping or waiting prior to the suspension, or that were
+     * scheduled while the job manager was suspended, will now be eligible
+     * for execution.
+     * <p>
+     * Calling this method on a rule that is not suspended  has no effect.  If another
+     * thread also owns the rule at the time this method is called, then the rule will
+     * not be resumed until all threads have released the rule.
+     *
+     * @deprecated This method is not safe and should not be used.
+     * Suspending a scheduling rule violates the thread safety
+     * of clients that use scheduling rules as a mutual exclusion mechanism,
+     * and can result in concurrency problems in all clients that use the suspended rule.
+     * @see #suspend(ISchedulingRule, IProgressMonitor)
+     */
+    public void resume(ISchedulingRule rule);
+
+    /**
+     * Resumes execution of jobs after a previous <code>suspend</code>.  All
+     * jobs that were sleeping or waiting prior to the suspension, or that were
+     * scheduled while the job manager was suspended, will now be eligible
+     * for execution.
+     * <p>
+     * Calling <code>resume</code> when the job manager is not suspended
+     * has no effect.
+     *
+     * @see #suspend()
+     * @see #isSuspended()
+     */
+    public void resume();
+
+    /**
+     * Provides a hook that is notified whenever a thread is about to wait on a lock,
+     * or when a thread is about to release a lock.  This hook must only be set once.
+     * <p>
+     * This method is for internal use by the platform-related plug-ins.
+     * Clients should not call this method.
+     * </p>
+     * @see LockListener
+     */
+    public void setLockListener(LockListener listener);
+
+    /**
+     * Registers a progress provider with the job manager.  If there was a
+     * provider already registered, it is replaced.
+     * <p>
+     * This method is intended for use by the currently executing Eclipse application.
+     * Plug-ins outside the currently running application should not call this method.
+     * </p>
+     *
+     * @param provider the new provider, or <code>null</code> if no progress
+     * is needed
+     */
+    public void setProgressProvider(ProgressProvider provider);
+
+    /**
+     * Suspends execution of all jobs.  Jobs that are already running
+     * when this method is invoked will complete as usual, but all sleeping and
+     * waiting jobs will not be executed until the job manager is resumed.
+     * <p>
+     * The job manager will remain suspended until a subsequent call to
+     * <code>resume</code>.  Further calls to <code>suspend</code>
+     * when the job manager is already suspended are ignored.
+     * <p>
+     * All attempts to join sleeping and waiting jobs while the job manager is
+     * suspended will return immediately.
+     * <p>
+     * Note that this very powerful function should be used with extreme caution.
+     * Suspending the job manager will prevent all jobs in the system from executing,
+     * which may have adverse affects on components that are relying on
+     * execution of jobs. The job manager should never be suspended without intent
+     * to resume execution soon afterwards.
+     *
+     * @see #resume()
+     * @see #join(Object, IProgressMonitor)
+     * @see #isSuspended()
+     */
+    public void suspend();
+
+    /**
+     * Defers execution of all jobs with scheduling rules that conflict with the
+     * given rule. The caller will be blocked until all currently executing jobs with
+     * conflicting rules are completed.  Conflicting jobs that are sleeping or waiting at
+     * the time this method is called will not be executed until the rule is resumed.
+     * <p>
+     * While a rule is suspended, all calls to <code>beginRule</code> and
+     * <code>endRule</code> on a suspended rule will not block the caller.
+     * The rule remains suspended until a subsequent call to
+     * <code>resume(ISchedulingRule)</code> with the identical rule instance.
+     * Further calls to <code>suspend</code> with an identical rule prior to calling
+     * <code>resume</code> are ignored.
+     * </p>
+     * <p>
+     * This method is long-running; progress and cancelation are provided by
+     * the given progress monitor. In the case of cancelation, the rule will
+     * not be suspended.
+     * </p>
+     * Note: this very powerful function should be used with extreme caution.
+     * Suspending rules will prevent jobs in the system from executing, which may
+     * have adverse effects on components that are relying on execution of jobs.
+     * The job manager should never be suspended without intent to resume
+     * execution soon afterwards. Deadlock will result if the thread responsible
+     * for resuming the rule attempts to join a suspended job.
+     *
+     * @deprecated This method is not safe and should not be used.
+     * Suspending a scheduling rule violates the thread safety
+     * of clients that use scheduling rules as a mutual exclusion mechanism,
+     * and can result in concurrency problems in all clients that use the suspended rule.
+     * @param rule The scheduling rule to suspend. Must not be <code>null</code>.
+     * @param monitor a progress monitor, or <code>null</code> if progress
+     * reporting is not desired
+     * @exception OperationCanceledException if the operation is canceled.
+     * Cancelation can occur even if no progress monitor is provided.
+     * @see #resume(ISchedulingRule)
+     */
+    public void suspend(ISchedulingRule rule, IProgressMonitor monitor);
+
+    /**
+     * Requests that all jobs in the given job family be suspended.  Jobs currently
+     * waiting to be run will be removed from the queue and moved into the
+     * <code>SLEEPING</code> state.  Jobs that have been put to sleep
+     * will remain in that state until either resumed or canceled.  This method has
+     * no effect on jobs that are not currently waiting to be run.
+     * <p>
+     * Sleeping jobs can be resumed using <code>wakeUp</code>.
+     *
+     * @param family the job family to sleep, or <code>null</code> to sleep all jobs.
+     * @see Job#belongsTo(Object)
+     */
+    public void sleep(Object family);
+
+    /**
+     * Transfers ownership of a scheduling rule to another thread.  The identical
+     * scheduling rule must currently be owned by the calling thread as a result of
+     * a previous call to <code>beginRule</code>.  The destination thread must
+     * not already own a scheduling rule.
+     * <p>
+     * Calling this method is equivalent to atomically calling <code>endRule</code>
+     * in the calling thread followed by an immediate <code>beginRule</code> in
+     * the destination thread.  The destination thread is responsible for subsequently
+     * calling <code>endRule</code> when it is finished using the rule.
+     * <p>
+     * This method has no effect when the destination thread is the same as the
+     * calling thread.
+     *
+     * @param rule The scheduling rule to transfer
+     * @param destinationThread The new owner for the transferred rule.
+     * @since 3.1
+     */
+    public void transferRule(ISchedulingRule rule, Thread destinationThread);
+
+    /**
+     * Resumes scheduling of all sleeping jobs in the given family.  This method
+     * has no effect on jobs in the family that are not currently sleeping.
+     *
+     * @param family the job family to wake up, or <code>null</code> to wake up all jobs
+     * @see Job#belongsTo(Object)
+     */
+    public void wakeUp(Object family);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/jobs/IJobStatus.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2008 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.runtime.jobs.IJobStatus;
+
+import dwt.dwthelper.utils;
+
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.jobs.Job;
+
+/**
+ * Represents status relating to the execution of jobs.
+ *
+ * @see dwtx.core.runtime.IStatus
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IJobStatus : IStatus {
+    /**
+     * Returns the job associated with this status.
+     *
+     * @return the job associated with this status
+     */
+    public Job getJob();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/jobs/ILock.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 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.runtime.jobs.ILock;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A lock is used to control access to an exclusive resource.
+ * <p>
+ * Locks are reentrant.  That is, they can be acquired multiple times by the same thread
+ * without releasing.  Locks are only released when the number of successful acquires
+ * equals the number of successful releases.
+ * </p><p>
+ * Locks are capable of detecting and recovering from programming errors that cause
+ * circular waiting deadlocks. When a deadlock between two or more <tt>ILock</tt>
+ * instances is detected, detailed debugging information is printed to the log file.  The
+ * locks will then automatically recover from the deadlock by employing a release
+ * and wait strategy. One thread will lose control of the locks it owns, thus breaking
+ * the deadlock and  allowing other threads to proceed.  Once that thread's locks are
+ * all available, it will be given exclusive access to all its locks and allowed to proceed.
+ * A thread can only lose locks while it is waiting on an <tt>acquire()</tt> call.
+ *
+ * </p><p>
+ * Successive acquire attempts by different threads are queued and serviced on
+ * a first come, first served basis.
+ * </p><p>
+ * It is very important that acquired locks eventually get released.  Calls to release
+ * should be done in a finally block to ensure they execute.  For example:
+ * <pre>
+ * try {
+ *  lock.acquire();
+ *  // ... do work here ...
+ * } finally {
+ *  lock.release();
+ * }
+ * </pre>
+ * Note: although <tt>lock.acquire</tt> should never fail, it is good practice to place
+ * it inside the try block anyway.  Releasing without acquiring is far less catastrophic
+ * than acquiring without releasing.
+ * </p>
+ *
+ * @see IJobManager#newLock()
+ * @since 3.0
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface ILock {
+    /**
+     * Attempts to acquire this lock.  If the lock is in use and the specified delay is
+     * greater than zero, the calling thread will block until one of the following happens:
+     * <ul>
+     * <li>This lock is available</li>
+     * <li>The thread is interrupted</li>
+     * <li>The specified delay has elapsed</li>
+     * </ul>
+     * <p>
+     * While a thread is waiting,  locks it already owns may be granted to other threads
+     * if necessary to break a deadlock.  In this situation, the calling thread may be blocked
+     * for longer than the specified delay.  On returning from this call, the calling thread
+     * will once again have exclusive access to any other locks it owned upon entering
+     * the acquire method.
+     *
+     * @param delay the number of milliseconds to delay
+     * @return <code>true</code> if the lock was successfully acquired, and
+     * <code>false</code> otherwise.
+     * @exception InterruptedException if the thread was interrupted
+     */
+    public bool acquire(long delay);
+
+    /**
+     * Acquires this lock.  If the lock is in use, the calling thread will block until the lock
+     * becomes available.  If the calling thread owns several locks, it will be blocked
+     * until all threads it requires become available, or until the thread is interrupted.
+     * While a thread is waiting, its locks may be granted to other threads if necessary
+     * to break a deadlock.  On returning from this call, the calling thread will
+     * have exclusive access to this lock, and any other locks it owned upon
+     * entering the acquire method.
+     * <p>
+     * This implementation ignores attempts to interrupt the thread.  If response to
+     * interruption is needed, use the method <code>acquire(long)</code>
+     */
+    public void acquire();
+
+    /**
+     * Returns the number of nested acquires on this lock that have not been released.
+     * This is the number of times that release() must be called before the lock is
+     * freed.
+     *
+     * @return the number of nested acquires that have not been released
+     */
+    public int getDepth();
+
+    /**
+     * Releases this lock. Locks must only be released by the thread that currently
+     * owns the lock.
+     */
+    public void release();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/jobs/ISchedulingRule.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * 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.runtime.jobs.ISchedulingRule;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Scheduling rules are used by jobs to indicate when they need exclusive access
+ * to a resource.  Scheduling rules can also be applied synchronously to a thread
+ * using <tt>IJobManager.beginRule(ISchedulingRule)</tt> and 
+ * <tt>IJobManager.endRule(ISchedulingRule)</tt>.  The job manager guarantees that 
+ * no two jobs with conflicting scheduling rules will run concurrently. 
+ * Multiple rules can be applied to a given thread only if the outer rule explicitly 
+ * allows the nesting as specified by the <code>contains</code> method.  
+ * <p>
+ * Clients may implement this interface.
+ * 
+ * @see Job#getRule()
+ * @see Job#setRule(ISchedulingRule)
+ * @see Job#schedule(long)
+ * @see IJobManager#beginRule(ISchedulingRule, dwtx.core.runtime.IProgressMonitor)
+ * @see IJobManager#endRule(ISchedulingRule)
+ * @since 3.0
+ */
+public interface ISchedulingRule {
+    /**
+     * Returns whether this scheduling rule completely contains another scheduling
+     * rule.  Rules can only be nested within a thread if the inner rule is completely 
+     * contained within the outer rule.
+     * <p>
+     * Implementations of this method must obey the rules of a partial order relation
+     * on the set of all scheduling rules.  In particular, implementations must be reflexive
+     * (a.contains(a) is always true), antisymmetric (a.contains(b) and b.contains(a) iff a.equals(b), 
+     * and transitive (if a.contains(b) and b.contains(c), then a.contains(c)).  Implementations
+     * of this method must return <code>false</code> when compared to a rule they
+     * know nothing about.
+     * 
+     * @param rule the rule to check for containment
+     * @return <code>true</code> if this rule contains the given rule, and 
+     * <code>false</code> otherwise.
+     */
+    public bool contains(ISchedulingRule rule);
+
+    /**
+     * Returns whether this scheduling rule is compatible with another scheduling rule.
+     * If <code>true</code> is returned, then no job with this rule will be run at the 
+     * same time as a job with the conflicting rule.  If <code>false</code> is returned, 
+     * then the job manager is free to run jobs with these rules at the same time.
+     * <p>
+     * Implementations of this method must be reflexive, symmetric, and consistent,
+     * and must return <code>false</code> when compared  to a rule they know 
+     * nothing about.
+     *
+     * @param rule the rule to check for conflicts
+     * @return <code>true</code> if the rule is conflicting, and <code>false</code>
+     *  otherwise.
+     */
+    public bool isConflicting(ISchedulingRule rule);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/jobs/Job.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,699 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2007 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 Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.jobs.Job;
+
+import tango.core.Thread;
+import dwt.dwthelper.utils;
+
+import dwtx.core.internal.jobs.InternalJob;
+import dwtx.core.internal.jobs.JobManager;
+import dwtx.core.runtime.IAdaptable;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.QualifiedName;
+import dwtx.core.runtime.Status;
+import dwtx.core.runtime.jobs.IJobManager;
+import dwtx.core.runtime.jobs.IJobChangeListener;
+import dwtx.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * Jobs are units of runnable work that can be scheduled to be run with the job
+ * manager.  Once a job has completed, it can be scheduled to run again (jobs are
+ * reusable).
+ * <p>
+ * Jobs have a state that indicates what they are currently doing.  When constructed,
+ * jobs start with a state value of <code>NONE</code>.  When a job is scheduled
+ * to be run, it moves into the <code>WAITING</code> state.  When a job starts
+ * running, it moves into the <code>RUNNING</code> state.  When execution finishes
+ * (either normally or through cancelation), the state changes back to
+ * <code>NONE</code>.
+ * </p><p>
+ * A job can also be in the <code>SLEEPING</code> state.  This happens if a user
+ * calls Job.sleep() on a waiting job, or if a job is scheduled to run after a specified
+ * delay.  Only jobs in the <code>WAITING</code> state can be put to sleep.
+ * Sleeping jobs can be woken at any time using Job.wakeUp(), which will put the
+ * job back into the <code>WAITING</code> state.
+ * </p><p>
+ * Jobs can be assigned a priority that is used as a hint about how the job should
+ * be scheduled.  There is no guarantee that jobs of one priority will be run before
+ * all jobs of lower priority.  The javadoc for the various priority constants provide
+ * more detail about what each priority means.  By default, jobs start in the
+ * <code>LONG</code> priority class.
+ *
+ * @see IJobManager
+ * @since 3.0
+ */
+public abstract class Job : InternalJob, IAdaptable {
+    // DWT from IAdaptable
+    public Object getAdapter(ClassInfo adapter){
+        return super.getAdapter(adapter);
+    }
+
+    /**
+     * Job status return value that is used to indicate asynchronous job completion.
+     * @see Job#run(IProgressMonitor)
+     * @see Job#done(IStatus)
+     */
+    private static IStatus ASYNC_FINISH_;
+    public static IStatus ASYNC_FINISH(){
+        if( ASYNC_FINISH_ is null ){
+            synchronized( Job.classinfo ) {
+                if( ASYNC_FINISH_ is null ){
+                    ASYNC_FINISH_ = new Status(IStatus.OK, JobManager.PI_JOBS, 1, "", null);//$NON-NLS-1$
+                }
+            }
+        }
+        return ASYNC_FINISH_;
+    }
+
+    /* Job priorities */
+    /**
+     * Job priority constant (value 10) for interactive jobs.
+     * Interactive jobs generally have priority over all other jobs.
+     * Interactive jobs should be either fast running or very low on CPU
+     * usage to avoid blocking other interactive jobs from running.
+     *
+     * @see #getPriority()
+     * @see #setPriority(int)
+     * @see #run(IProgressMonitor)
+     */
+    public static const int INTERACTIVE = 10;
+    /**
+     * Job priority constant (value 20) for short background jobs.
+     * Short background jobs are jobs that typically complete within a second,
+     * but may take longer in some cases.  Short jobs are given priority
+     * over all other jobs except interactive jobs.
+     *
+     * @see #getPriority()
+     * @see #setPriority(int)
+     * @see #run(IProgressMonitor)
+     */
+    public static const int SHORT = 20;
+    /**
+     * Job priority constant (value 30) for long-running background jobs.
+     *
+     * @see #getPriority()
+     * @see #setPriority(int)
+     * @see #run(IProgressMonitor)
+     */
+    public static const int LONG = 30;
+    /**
+     * Job priority constant (value 40) for build jobs.  Build jobs are
+     * generally run after all other background jobs complete.
+     *
+     * @see #getPriority()
+     * @see #setPriority(int)
+     * @see #run(IProgressMonitor)
+     */
+    public static const int BUILD = 40;
+
+    /**
+     * Job priority constant (value 50) for decoration jobs.
+     * Decoration jobs have lowest priority.  Decoration jobs generally
+     * compute extra information that the user may be interested in seeing
+     * but is generally not waiting for.
+     *
+     * @see #getPriority()
+     * @see #setPriority(int)
+     * @see #run(IProgressMonitor)
+     */
+    public static const int DECORATE = 50;
+    /**
+     * Job state code (value 0) indicating that a job is not
+     * currently sleeping, waiting, or running (i.e., the job manager doesn't know
+     * anything about the job).
+     *
+     * @see #getState()
+     */
+    public static const int NONE = 0;
+    /**
+     * Job state code (value 1) indicating that a job is sleeping.
+     *
+     * @see #run(IProgressMonitor)
+     * @see #getState()
+     */
+    public static const int SLEEPING = 0x01;
+    /**
+     * Job state code (value 2) indicating that a job is waiting to be run.
+     *
+     * @see #getState()
+     */
+    public static const int WAITING = 0x02;
+    /**
+     * Job state code (value 4) indicating that a job is currently running
+     *
+     * @see #getState()
+     */
+    public static const int RUNNING = 0x04;
+
+    /**
+     * Returns the job manager.
+     *
+     * @return the job manager
+     * @since dwtx.core.jobs 3.2
+     */
+    public static final IJobManager getJobManager() {
+        return manager;
+    }
+
+    /**
+     * Creates a new job with the specified name.  The job name is a human-readable
+     * value that is displayed to users.  The name does not need to be unique, but it
+     * must not be <code>null</code>.
+     *
+     * @param name the name of the job.
+     */
+    public this(String name) {
+        super(name);
+    }
+
+    /**
+     * Registers a job listener with this job
+     * Has no effect if an identical listener is already registered.
+     *
+     * @param listener the listener to be added.
+     */
+    public final void addJobChangeListener(IJobChangeListener listener) {
+        super.addJobChangeListener(listener);
+    }
+
+    /**
+     * Returns whether this job belongs to the given family.  Job families are
+     * represented as objects that are not interpreted or specified in any way
+     * by the job manager.  Thus, a job can choose to belong to any number of
+     * families.
+     * <p>
+     * Clients may override this method.  This default implementation always returns
+     * <code>false</code>.  Overriding implementations must return <code>false</code>
+     * for families they do not recognize.
+     * </p>
+     *
+     * @param family the job family identifier
+     * @return <code>true</code> if this job belongs to the given family, and
+     * <code>false</code> otherwise.
+     */
+    public bool belongsTo(Object family) {
+        return false;
+    }
+
+    /**
+     * Stops the job.  If the job is currently waiting,
+     * it will be removed from the queue.  If the job is sleeping,
+     * it will be discarded without having a chance to resume and its sleeping state
+     * will be cleared.  If the job is currently executing, it will be asked to
+     * stop but there is no guarantee that it will do so.
+     *
+     * @return <code>false</code> if the job is currently running (and thus may not
+     * respond to cancelation), and <code>true</code> in all other cases.
+     */
+    public final bool cancel() {
+        return super.cancel();
+    }
+
+    /**
+     * A hook method indicating that this job is running and {@link #cancel()}
+     * is being called for the first time.
+     * <p>
+     * Subclasses may override this method to perform additional work when
+     * a cancelation request is made.  This default implementation does nothing.
+     * @since 3.3
+     */
+    protected void canceling() {
+        //default implementation does nothing
+    }
+
+    /**
+     * Jobs that complete their execution asynchronously must indicate when they
+     * are finished by calling this method.  This method must not be called by
+     * a job that has not indicated that it is executing asynchronously.
+     * <p>
+     * This method must not be called from within the scope of a job's <code>run</code>
+     * method.  Jobs should normally indicate completion by returning an appropriate
+     * status from the <code>run</code> method.  Jobs that return a status of
+     * <code>ASYNC_FINISH</code> from their run method must later call
+     * <code>done</code> to indicate completion.
+     *
+     * @param result a status object indicating the result of the job's execution.
+     * @see #ASYNC_FINISH
+     * @see #run(IProgressMonitor)
+     */
+    public final void done(IStatus result) {
+        super.done(result);
+    }
+
+    /**
+     * Returns the human readable name of this job.  The name is never
+     * <code>null</code>.
+     *
+     * @return the name of this job
+     */
+    public final String getName() {
+        return super.getName();
+    }
+
+    /**
+     * Returns the priority of this job.  The priority is used as a hint when the job
+     * is scheduled to be run.
+     *
+     * @return the priority of the job.  One of INTERACTIVE, SHORT, LONG, BUILD,
+     *  or DECORATE.
+     */
+    public final int getPriority() {
+        return super.getPriority();
+    }
+
+    /**
+     * Returns the value of the property of this job identified by the given key,
+     * or <code>null</code> if this job has no such property.
+     *
+     * @param key the name of the property
+     * @return the value of the property,
+     *     or <code>null</code> if this job has no such property
+     * @see #setProperty(QualifiedName, Object)
+     */
+    public final Object getProperty(QualifiedName key) {
+        return super.getProperty(key);
+    }
+
+    /**
+     * Returns the result of this job's last run.
+     *
+     * @return the result of this job's last run, or <code>null</code> if this
+     * job has never finished running.
+     */
+    public final IStatus getResult() {
+        return super.getResult();
+    }
+
+    /**
+     * Returns the scheduling rule for this job.  Returns <code>null</code> if this job has no
+     * scheduling rule.
+     *
+     * @return the scheduling rule for this job, or <code>null</code>.
+     * @see ISchedulingRule
+     * @see #setRule(ISchedulingRule)
+     */
+    public final ISchedulingRule getRule() {
+        return super.getRule();
+    }
+
+    /**
+     * Returns the state of the job. Result will be one of:
+     * <ul>
+     * <li><code>Job.RUNNING</code> - if the job is currently running.</li>
+     * <li><code>Job.WAITING</code> - if the job is waiting to be run.</li>
+     * <li><code>Job.SLEEPING</code> - if the job is sleeping.</li>
+     * <li><code>Job.NONE</code> - in all other cases.</li>
+     * </ul>
+     * <p>
+     * Note that job state is inherently volatile, and in most cases clients
+     * cannot rely on the result of this method being valid by the time the
+     * result is obtained.  For example, if <tt>getState</tt> returns
+     * <tt>RUNNING</tt>,  the job may have actually completed by the
+     * time the <tt>getState</tt> method returns.  All clients can infer from
+     * invoking this method is that  the job was recently in the returned state.
+     *
+     * @return the job state
+     */
+    public final int getState() {
+        return super.getState();
+    }
+
+    /**
+     * Returns the thread that this job is currently running in.
+     *
+     * @return the thread this job is running in, or <code>null</code>
+     * if this job is not running or the thread is unknown.
+     */
+    public final Thread getThread() {
+        return super.getThread();
+    }
+
+    /**
+     * Returns whether this job is blocking a higher priority non-system job from
+     * starting due to a conflicting scheduling rule.  Returns <code>false</code>
+     * if this job is not running, or is not blocking a higher priority non-system job.
+     *
+     * @return <code>true</code> if this job is blocking a higher priority non-system
+     * job, and <code>false</code> otherwise.
+     * @see #getRule()
+     * @see #isSystem()
+     */
+    public final bool isBlocking() {
+        return super.isBlocking();
+    }
+
+    /**
+     * Returns whether this job is a system job.  System jobs are typically not
+     * revealed to users in any UI presentation of jobs.  Other than their UI presentation,
+     * system jobs act exactly like other jobs.  If this value is not explicitly set, jobs
+     * are treated as non-system jobs.  The default value is <code>false</code>.
+     *
+     * @return <code>true</code> if this job is a system job, and
+     * <code>false</code> otherwise.
+     * @see #setSystem(bool)
+     */
+    public final bool isSystem() {
+        return super.isSystem();
+    }
+
+    /**
+     * Returns whether this job has been directly initiated by a UI end user.
+     * These jobs may be presented differently in the UI.  The default value
+     * is <code>false</code>.
+     *
+     * @return <code>true</code> if this job is a user-initiated job, and
+     * <code>false</code> otherwise.
+     * @see #setUser(bool)
+     */
+    public final bool isUser() {
+        return super.isUser();
+    }
+
+    /**
+     * Waits until this job is finished.  This method will block the calling thread until the
+     * job has finished executing, or until this thread has been interrupted.  If the job
+     * has not been scheduled, this method returns immediately.  A job must not
+     * be joined from within the scope of its run method.
+     * <p>
+     * If this method is called on a job that reschedules itself from within the
+     * <tt>run</tt> method, the join will return at the end of the first execution.
+     * In other words, join will return the first time this job exits the
+     * {@link #RUNNING} state, or as soon as this job enters the {@link #NONE} state.
+     * </p>
+     * <p>
+     * If this method is called while the job manager is suspended, this job
+     * will only be joined if it is already running; if this job is waiting or sleeping,
+     * this method returns immediately.
+     * </p>
+     * <p>
+     * Note that there is a deadlock risk when using join.  If the calling thread owns
+     * a lock or object monitor that the joined thread is waiting for, deadlock
+     * will occur.
+     * </p>
+     *
+     * @exception InterruptedException if this thread is interrupted while waiting
+     * @see ILock
+     * @see IJobManager#suspend()
+     */
+    public final void join() {
+        super.join();
+    }
+
+    /**
+     * Removes a job listener from this job.
+     * Has no effect if an identical listener is not already registered.
+     *
+     * @param listener the listener to be removed
+     */
+    public final void removeJobChangeListener(IJobChangeListener listener) {
+        super.removeJobChangeListener(listener);
+    }
+
+    /**
+     * Executes this job.  Returns the result of the execution.
+     * <p>
+     * The provided monitor can be used to report progress and respond to
+     * cancellation.  If the progress monitor has been canceled, the job
+     * should finish its execution at the earliest convenience and return a result
+     * status of severity {@link IStatus#CANCEL}.  The singleton
+     * cancel status {@link Status#CANCEL_STATUS} can be used for
+     * this purpose.  The monitor is only valid for the duration of the invocation
+     * of this method.
+     * <p>
+     * This method must not be called directly by clients.  Clients should call
+     * <code>schedule</code>, which will in turn cause this method to be called.
+     * <p>
+     * Jobs can optionally finish their execution asynchronously (in another thread) by
+     * returning a result status of {@link #ASYNC_FINISH}.  Jobs that finish
+     * asynchronously <b>must</b> specify the execution thread by calling
+     * <code>setThread</code>, and must indicate when they are finished by calling
+     * the method <code>done</code>.
+     *
+     * @param monitor the monitor to be used for reporting progress and
+     * responding to cancelation. The monitor is never <code>null</code>
+     * @return resulting status of the run. The result must not be <code>null</code>
+     * @see #ASYNC_FINISH
+     * @see #done(IStatus)
+     */
+    protected abstract IStatus run(IProgressMonitor monitor);
+
+    /**
+     * Schedules this job to be run.  The job is added to a queue of waiting
+     * jobs, and will be run when it arrives at the beginning of the queue.
+     * <p>
+     * This is a convenience method, fully equivalent to
+     * <code>schedule(0L)</code>.
+     * </p>
+     * @see #schedule(long)
+     */
+    public final void schedule() {
+        super.schedule(0L);
+    }
+
+    /**
+     * Schedules this job to be run after a specified delay.  The job is put in the
+     * {@link #SLEEPING} state until the specified delay has elapsed, after which
+     * the job is added to a queue of {@link #WAITING} jobs. Once the job arrives
+     * at the beginning of the queue, it will be run at the first available opportunity.
+     * </p><p>
+     * Jobs of equal priority and <code>delay</code> with conflicting scheduling
+     * rules are guaranteed to run in the order they are scheduled. No guarantees
+     * are made about the relative execution order of jobs with unrelated or
+     * <code>null</code> scheduling rules, or different priorities.
+     * <p>
+     * If this job is currently running, it will be rescheduled with the specified
+     * delay as soon as it finishes.  If this method is called multiple times
+     * while the job is running, the job will still only be rescheduled once,
+     * with the most recent delay value that was provided.
+     * </p><p>
+     * Scheduling a job that is waiting or sleeping has no effect.
+     * </p>
+     *
+     * @param delay a time delay in milliseconds before the job should run
+     * @see ISchedulingRule
+     */
+    public final void schedule(long delay) {
+        super.schedule(delay);
+    }
+
+    /**
+     * Changes the name of this job.  If the job is currently running, waiting,
+     * or sleeping, the new job name may not take effect until the next time the
+     * job is scheduled.
+     * <p>
+     * The job name is a human-readable value that is displayed to users.  The name
+     * does not need to be unique, but it must not be <code>null</code>.
+     *
+     * @param name the name of the job.
+     */
+    public final void setName(String name) {
+        super.setName(name);
+    }
+
+    /**
+     * Sets the priority of the job.  This will not affect the execution of
+     * a running job, but it will affect how the job is scheduled while
+     * it is waiting to be run.
+     *
+     * @param priority the new job priority.  One of
+     * INTERACTIVE, SHORT, LONG, BUILD, or DECORATE.
+     */
+    public final void setPriority(int priority) {
+        super.setPriority(priority);
+    }
+
+    /**
+     * Associates this job with a progress group.  Progress feedback
+     * on this job's next execution will be displayed together with other
+     * jobs in that group. The provided monitor must be a monitor
+     * created by the method <tt>IJobManager.createProgressGroup</tt>
+     * and must have at least <code>ticks</code> units of available work.
+     * <p>
+     * The progress group must be set before the job is scheduled.
+     * The group will be used only for a single invocation of the job's
+     * <tt>run</tt> method, after which any association of this job to the
+     * group will be lost.
+     *
+     * @see IJobManager#createProgressGroup()
+     * @param group The progress group to use for this job
+     * @param ticks the number of work ticks allocated from the
+     *    parent monitor, or {@link IProgressMonitor#UNKNOWN}
+     */
+    public final void setProgressGroup(IProgressMonitor group, int ticks) {
+        super.setProgressGroup(group, ticks);
+    }
+
+    /**
+     * Sets the value of the property of this job identified
+     * by the given key. If the supplied value is <code>null</code>,
+     * the property is removed from this resource.
+     * <p>
+     * Properties are intended to be used as a caching mechanism
+     * by ISV plug-ins. They allow key-object associations to be stored with
+     * a job instance.  These key-value associations are maintained in
+     * memory (at all times), and the information is never discarded automatically.
+     * </p><p>
+     * The qualifier part of the property name must be the unique identifier
+     * of the declaring plug-in (e.g. <code>"com.example.plugin"</code>).
+     * </p>
+     *
+     * @param key the qualified name of the property
+     * @param value the value of the property,
+     *     or <code>null</code> if the property is to be removed
+     * @see #getProperty(QualifiedName)
+     */
+    public void setProperty(QualifiedName key, Object value) {
+        super.setProperty(key, value);
+    }
+
+    /**
+     * Sets the scheduling rule to be used when scheduling this job.  This method
+     * must be called before the job is scheduled.
+     *
+     * @param rule the new scheduling rule, or <code>null</code> if the job
+     * should have no scheduling rule
+     * @see #getRule()
+     */
+    public final void setRule(ISchedulingRule rule) {
+        super.setRule(rule);
+    }
+
+    /**
+     * Sets whether or not this job is a system job.  System jobs are typically not
+     * revealed to users in any UI presentation of jobs.  Other than their UI presentation,
+     * system jobs act exactly like other jobs.  If this value is not explicitly set, jobs
+     * are treated as non-system jobs. This method must be called before the job
+     * is scheduled.
+     *
+     * @param value <code>true</code> if this job should be a system job, and
+     * <code>false</code> otherwise.
+     * @see #isSystem()
+     */
+    public final void setSystem(bool value) {
+        super.setSystem(value);
+    }
+
+    /**
+     * Sets whether or not this job has been directly initiated by a UI end user.
+     * These jobs may be presented differently in the UI. This method must be
+     * called before the job is scheduled.
+     *
+     * @param value <code>true</code> if this job is a user-initiated job, and
+     * <code>false</code> otherwise.
+     * @see #isUser()
+     */
+    public final void setUser(bool value) {
+        super.setUser(value);
+    }
+
+    /**
+     * Sets the thread that this job is currently running in, or <code>null</code>
+     * if this job is not running or the thread is unknown.
+     * <p>
+     * Jobs that use the {@link #ASYNC_FINISH} return code should tell
+     * the job what thread it is running in.  This is used to prevent deadlocks.
+     *
+     * @param thread the thread that this job is running in.
+     *
+     * @see #ASYNC_FINISH
+     * @see #run(IProgressMonitor)
+     */
+    public final void setThread(Thread thread) {
+        super.setThread(thread);
+    }
+
+    /**
+     * Returns whether this job should be run.
+     * If <code>false</code> is returned, this job will be discarded by the job manager
+     * without running.
+     * <p>
+     * This method is called immediately prior to calling the job's
+     * run method, so it can be used for last minute pre-condition checking before
+     * a job is run. This method must not attempt to schedule or change the
+     * state of any other job.
+     * </p><p>
+     * Clients may override this method.  This default implementation always returns
+     * <code>true</code>.
+     * </p>
+     *
+     * @return <code>true</code> if this job should be run
+     *   and <code>false</code> otherwise
+     */
+    public bool shouldRun() {
+        return true;
+    }
+
+    /**
+     * Returns whether this job should be scheduled.
+     * If <code>false</code> is returned, this job will be discarded by the job manager
+     * without being added to the queue.
+     * <p>
+     * This method is called immediately prior to adding the job to the waiting job
+     * queue.,so it can be used for last minute pre-condition checking before
+     * a job is scheduled.
+     * </p><p>
+     * Clients may override this method.  This default implementation always returns
+     * <code>true</code>.
+     * </p>
+     *
+     * @return <code>true</code> if the job manager should schedule this job
+     *   and <code>false</code> otherwise
+     */
+    public bool shouldSchedule() {
+        return true;
+    }
+
+    /**
+     * Requests that this job be suspended.  If the job is currently waiting to be run, it
+     * will be removed from the queue move into the {@link #SLEEPING} state.
+     * The job will remain asleep until either resumed or canceled.  If this job is not
+     * currently waiting to be run, this method has no effect.
+     * <p>
+     * Sleeping jobs can be resumed using <code>wakeUp</code>.
+     *
+     * @return <code>false</code> if the job is currently running (and thus cannot
+     * be put to sleep), and <code>true</code> in all other cases
+     * @see #wakeUp()
+     */
+    public final bool sleep() {
+        return super.sleep();
+    }
+
+    /**
+     * Puts this job immediately into the {@link #WAITING} state so that it is
+     * eligible for immediate execution. If this job is not currently sleeping,
+     * the request is ignored.
+     * <p>
+     * This is a convenience method, fully equivalent to
+     * <code>wakeUp(0L)</code>.
+     * </p>
+     * @see #sleep()
+     */
+    public final void wakeUp() {
+        super.wakeUp(0L);
+    }
+
+    /**
+     * Puts this job back into the {@link #WAITING} state after
+     * the specified delay. This is equivalent to canceling the sleeping job and
+     * rescheduling with the given delay.  If this job is not currently sleeping,
+     * the request  is ignored.
+     *
+     * @param delay the number of milliseconds to delay
+     * @see #sleep()
+     */
+    public final void wakeUp(long delay) {
+        super.wakeUp(delay);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/jobs/JobChangeAdapter.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * 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.runtime.jobs.JobChangeAdapter;
+
+import dwt.dwthelper.utils;
+import dwtx.core.runtime.jobs.IJobChangeListener;
+import dwtx.core.runtime.jobs.IJobChangeEvent;
+
+/**
+ * This adapter class provides default implementations for the
+ * methods described by the <code>IJobChangeListener</code> interface.
+ * <p>
+ * Classes that wish to listen to the progress of scheduled jobs can
+ * extend this class and override only the methods which they are
+ * interested in.
+ * </p>
+ *
+ * @see IJobChangeListener
+ * @since 3.0
+ */
+public class JobChangeAdapter : IJobChangeListener {
+    /* (non-Javadoc)
+     * @see IJobChangeListener#aboutToRun(IJobChangeEvent)
+     * This default implementation does nothing
+     */
+    public void aboutToRun(IJobChangeEvent event) {
+        // do nothing
+    }
+
+    /* (non-Javadoc)
+     * @see IJobChangeListener#awake(IJobChangeEvent)
+     * This default implementation does nothing
+     */
+    public void awake(IJobChangeEvent event) {
+        // do nothing
+    }
+
+    /* (non-Javadoc)
+     * @see IJobChangeListener#done(IJobChangeEvent)
+     * This default implementation does nothing
+     */
+    public void done(IJobChangeEvent event) {
+        // do nothing
+    }
+
+    /* (non-Javadoc)
+     * @see IJobChangeListener#running(IJobChangeEvent)
+     * This default implementation does nothing
+     */
+    public void running(IJobChangeEvent event) {
+        // do nothing
+    }
+
+    /* (non-Javadoc)
+     * @see IJobChangeListener#scheduled(IJobChangeEvent)
+     * This default implementation does nothing
+     */
+    public void scheduled(IJobChangeEvent event) {
+        // do nothing
+    }
+
+    /* (non-Javadoc)
+     * @see IJobChangeListener#sleeping(IJobChangeEvent)
+     * This default implementation does nothing
+     */
+    public void sleeping(IJobChangeEvent event) {
+        // do nothing
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/jobs/LockListener.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 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.runtime.jobs.LockListener;
+
+import tango.core.Thread;
+import dwt.dwthelper.utils;
+
+import dwtx.core.internal.jobs.JobManager;
+import dwtx.core.internal.jobs.LockManager;
+import dwtx.core.runtime.jobs.Job;
+
+/**
+ * A lock listener is notified whenever a thread is about to wait
+ * on a lock, and when a thread is about to release a lock.
+ * <p>
+ * This class is for internal use by the platform-related plug-ins.
+ * Clients outside of the base platform should not reference or subclass this class.
+ * </p>
+ *
+ * @see IJobManager#setLockListener(LockListener)
+ * @since 3.0
+ */
+public class LockListener {
+    private const LockManager manager;
+
+    public this(){
+        manager = (cast(JobManager)Job.getJobManager()).getLockManager();
+    }
+
+    /**
+     * Notification that a thread is about to block on an attempt to acquire a lock.
+     * Returns whether the thread should be granted immediate access to the lock.
+     * <p>
+     * This default implementation always returns <code>false</code>.
+     * Subclasses may override.
+     *
+     * @param lockOwner the thread that currently owns the lock this thread is
+     * waiting for, or <code>null</code> if unknown.
+     * @return <code>true</code> if the thread should be granted immediate access,
+     * and <code>false</code> if it should wait for the lock to be available
+     */
+    public bool aboutToWait(Thread lockOwner) {
+        return false;
+    }
+
+    /**
+     * Notification that a thread is about to release a lock.
+     * <p>
+     * This default implementation does nothing. Subclasses may override.
+     */
+    public void aboutToRelease() {
+        //do nothing
+    }
+
+    /**
+     * Returns whether this thread currently owns any locks
+     * @return <code>true</code> if this thread owns any locks, and
+     * <code>false</code> otherwise.
+     */
+    protected final bool isLockOwnerThread() {
+        return manager.isLockOwner();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/jobs/MultiRule.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2008 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.runtime.jobs.MultiRule;
+
+import dwt.dwthelper.utils;
+import dwtx.dwtxhelper.Collection;
+
+import dwtx.core.runtime.jobs.ISchedulingRule;
+
+/**
+ * A MultiRule is a compound scheduling rule that represents a fixed group of child
+ * scheduling rules.  A MultiRule conflicts with another rule if any of its children conflict
+ * with that rule.  More formally, a compound rule represents a logical intersection
+ * of its child rules with respect to the <code>isConflicting</code> equivalence
+ * relation.
+ * <p>
+ * A MultiRule will never contain other MultiRules as children.  If a MultiRule is provided
+ * as a child, its children will be added instead.
+ * </p>
+ *
+ * @since 3.0
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class MultiRule : ISchedulingRule {
+    private ISchedulingRule[] rules;
+
+    /**
+     * Returns a scheduling rule that encompasses all provided rules.  The resulting
+     * rule may or may not be an instance of <code>MultiRule</code>.  If all
+     * provided rules are <code>null</code> then the result will be
+     * <code>null</code>.
+     *
+     * @param ruleArray An array of scheduling rules, some of which may be <code>null</code>
+     * @return a combined scheduling rule, or <code>null</code>
+     * @since 3.1
+     */
+    public static ISchedulingRule combine(ISchedulingRule[] ruleArray) {
+        ISchedulingRule result = null;
+        for (int i = 0; i < ruleArray.length; i++) {
+            if (ruleArray[i] is null)
+                continue;
+            if (result is null) {
+                result = ruleArray[i];
+                continue;
+            }
+            result = combine(result, ruleArray[i]);
+        }
+        return result;
+    }
+
+    /**
+     * Returns a scheduling rule that encompasses both provided rules.  The resulting
+     * rule may or may not be an instance of <code>MultiRule</code>.  If both
+     * provided rules are <code>null</code> then the result will be
+     * <code>null</code>.
+     *
+     * @param rule1 a scheduling rule, or <code>null</code>
+     * @param rule2 another scheduling rule, or <code>null</code>
+     * @return a combined scheduling rule, or <code>null</code>
+     */
+    public static ISchedulingRule combine(ISchedulingRule rule1, ISchedulingRule rule2) {
+        if (rule1 is rule2)
+            return rule1;
+        if (rule1 is null)
+            return rule2;
+        if (rule2 is null)
+            return rule1;
+        if (rule1.contains(rule2))
+            return rule1;
+        if (rule2.contains(rule1))
+            return rule2;
+        MultiRule result = new MultiRule();
+        result.rules = [rule1, rule2];
+        //make sure we don't end up with nested multi-rules
+        if (cast(MultiRule)rule1  || cast(MultiRule)rule2 )
+            result.rules = flatten(result.rules);
+        return result;
+    }
+
+    /*
+     * Collapses an array of rules that may contain MultiRules into an
+     * array in which no rules are MultiRules.
+     */
+    private static ISchedulingRule[] flatten(ISchedulingRule[] nestedRules) {
+        ArrayList myRules = new ArrayList(nestedRules.length);
+        for (int i = 0; i < nestedRules.length; i++) {
+            if (cast(MultiRule)nestedRules[i] ) {
+                ISchedulingRule[] children = (cast(MultiRule) nestedRules[i]).getChildren();
+                for (int j = 0; j < children.length; j++)
+                    myRules.add(cast(Object)children[j]);
+            } else {
+                myRules.add(cast(Object)nestedRules[i]);
+            }
+        }
+        return arraycast!(ISchedulingRule)( myRules.toArray() );
+    }
+
+    /**
+     * Creates a new scheduling rule that composes a set of nested rules.
+     *
+     * @param nestedRules the nested rules for this compound rule.
+     */
+    public this(ISchedulingRule[] nestedRules) {
+        this.rules = flatten(nestedRules);
+    }
+
+    /**
+     * Creates a new scheduling rule with no nested rules. For
+     * internal use only.
+     */
+    private this() {
+        //to be invoked only by factory methods
+    }
+
+    /**
+     * Returns the child rules within this rule.
+     * @return the child rules
+     */
+    public ISchedulingRule[] getChildren() {
+        return rules.dup;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.ISchedulingRule#contains(dwtx.core.runtime.jobs.ISchedulingRule)
+     */
+    public bool contains(ISchedulingRule rule) {
+        if (this is rule)
+            return true;
+        if (cast(MultiRule)rule ) {
+            ISchedulingRule[] otherRules = (cast(MultiRule) rule).getChildren();
+            //for each child of the target, there must be some child in this rule that contains it.
+            for (int other = 0; other < otherRules.length; other++) {
+                bool found = false;
+                for (int mine = 0; !found && mine < rules.length; mine++)
+                    found = rules[mine].contains(otherRules[other]);
+                if (!found)
+                    return false;
+            }
+            return true;
+        }
+        for (int i = 0; i < rules.length; i++)
+            if (rules[i].contains(rule))
+                return true;
+        return false;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.core.runtime.jobs.ISchedulingRule#isConflicting(dwtx.core.runtime.jobs.ISchedulingRule)
+     */
+    public bool isConflicting(ISchedulingRule rule) {
+        if (this is rule)
+            return true;
+        if (cast(MultiRule)rule ) {
+            ISchedulingRule[] otherRules = (cast(MultiRule) rule).getChildren();
+            for (int j = 0; j < otherRules.length; j++)
+                for (int i = 0; i < rules.length; i++)
+                    if (rules[i].isConflicting(otherRules[j]))
+                        return true;
+        } else {
+            for (int i = 0; i < rules.length; i++)
+                if (rules[i].isConflicting(rule))
+                    return true;
+        }
+        return false;
+    }
+
+    /*
+     * For debugging purposes only.
+     */
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append("MultiRule["); //$NON-NLS-1$
+        int last = rules.length - 1;
+        for (int i = 0; i < rules.length; i++) {
+            buffer.append(rules[i] ? (cast(Object)rules[i]).toString() : "null" );
+            if (i !is last)
+                buffer.append(',');
+        }
+        buffer.append(']');
+        return buffer.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/jobs/ProgressProvider.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * 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 Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.core.runtime.jobs.ProgressProvider;
+
+import dwt.dwthelper.utils;
+
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.NullProgressMonitor;
+import dwtx.core.runtime.SubProgressMonitor;
+
+import dwtx.core.runtime.jobs.Job;
+
+/**
+ * The progress provider supplies the job manager with progress monitors for
+ * running jobs.  There can only be one progress provider at any given time.
+ * <p>
+ * This class is intended for use by the currently executing Eclipse application.
+ * Plug-ins outside the currently running application should not reference or
+ * subclass this class.
+ * </p>
+ *
+ * @see IJobManager#setProgressProvider(ProgressProvider)
+ * @since 3.0
+ */
+public abstract class ProgressProvider {
+    /**
+     * Provides a new progress monitor instance to be used by the given job.
+     * This method is called prior to running any job that does not belong to a
+     * progress group. The returned monitor will be supplied to the job's
+     * <code>run</code> method.
+     *
+     * @see #createProgressGroup()
+     * @see Job#setProgressGroup(IProgressMonitor, int)
+     * @param job the job to create a progress monitor for
+     * @return a progress monitor, or <code>null</code> if no progress monitoring
+     * is needed.
+     */
+    public abstract IProgressMonitor createMonitor(Job job);
+
+    /**
+     * Returns a progress monitor that can be used to provide
+     * aggregated progress feedback on a set of running jobs.
+     * This method implements <code>IJobManager.createProgressGroup</code>,
+     * and must obey all rules specified in that contract.
+     * <p>
+     * This default implementation returns a new
+     * <code>NullProgressMonitor</code>  Subclasses may override.
+     *
+     * @see IJobManager#createProgressGroup()
+     * @return a progress monitor
+     */
+    public IProgressMonitor createProgressGroup() {
+        return new NullProgressMonitor();
+    }
+
+    /**
+     * Returns a progress monitor that can be used by a running job
+     * to report progress in the context of a progress group. This method
+     * implements <code>Job.setProgressGroup</code>.  One of the
+     * two <code>createMonitor</code> methods will be invoked
+     * prior to each execution of a job, depending on whether a progress
+     * group was specified for the job.
+     * <p>
+     * The provided monitor must be a monitor returned by the method
+     * <code>createProgressGroup</code>.  This method is responsible
+     * for asserting this and throwing an appropriate runtime exception
+     * if an invalid monitor is provided.
+     * <p>
+     * This default implementation returns a new
+     * <code>SubProgressMonitor</code>.  Subclasses may override.
+     *
+     * @see IJobManager#createProgressGroup()
+     * @see Job#setProgressGroup(IProgressMonitor, int)
+     * @param job the job to create a progress monitor for
+     * @param group the progress monitor group that this job belongs to
+     * @param ticks the number of ticks of work for the progress monitor
+     * @return a progress monitor, or <code>null</code> if no progress monitoring
+     * is needed.
+     */
+    public IProgressMonitor createMonitor(Job job, IProgressMonitor group, int ticks) {
+        return new SubProgressMonitor(group, ticks);
+    }
+
+    /**
+     * Returns a progress monitor to use when none has been provided
+     * by the client running the job.
+     * <p>
+     * This default implementation returns a new
+     * <code>NullProgressMonitor</code>  Subclasses may override.
+     *
+     * @return a progress monitor
+     */
+    public IProgressMonitor getDefaultMonitor() {
+        return new NullProgressMonitor();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/core/runtime/jobs/package.html	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides core support for scheduling and interacting with background activity.
+<h2>
+Package Specification</h2>
+<p>
+This package specifies API for scheduling background tasks, or jobs.  Jobs can be
+scheduled for immediate execution, or for execution after a specified delay.  Once
+scheduled, jobs can be queried, canceled, or suspended.  Rules can be attached to
+jobs to indicate when they can run, and whether they can run simultaneously with other
+jobs.  This package also includes a generic locking facility that includes support for 
+detecting and responding to deadlock.
+<p>
+@since 3.0
+<p>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/osgi/util/NLS.d	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,446 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2008 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.osgi.util.NLS;
+
+import dwt.dwthelper.utils;
+
+// import java.io.IOException;
+// import java.io.InputStream;
+// import java.lang.reflect.Field;
+// import java.lang.reflect.Modifier;
+// import java.security.AccessController;
+// import java.security.PrivilegedAction;
+// import java.util.ArrayList;
+// import java.util.HashMap;
+// import java.util.Locale;
+// import java.util.Map;
+// import java.util.Properties;
+//
+// import dwtx.osgi.framework.debug.Debug;
+// import dwtx.osgi.framework.log.FrameworkLog;
+// import dwtx.osgi.framework.log.FrameworkLogEntry;
+
+/**
+ * Common superclass for all message bundle classes.  Provides convenience
+ * methods for manipulating messages.
+ * <p>
+ * The <code>#bind</code> methods perform string substitution and should be considered a
+ * convenience and <em>not</em> a full substitute replacement for <code>MessageFormat#format</code>
+ * method calls.
+ * </p>
+ * <p>
+ * Text appearing within curly braces in the given message, will be interpreted
+ * as a numeric index to the corresponding substitution object in the given array. Calling
+ * the <code>#bind</code> methods with text that does not map to an integer will result in an
+ * {@link IllegalArgumentException}.
+ * </p>
+ * <p>
+ * Text appearing within single quotes is treated as a literal. A single quote is escaped by
+ * a preceeding single quote.
+ * </p>
+ * <p>
+ * Clients who wish to use the full substitution power of the <code>MessageFormat</code> class should
+ * call that class directly and not use these <code>#bind</code> methods.
+ * </p>
+ * <p>
+ * Clients may subclass this type.
+ * </p>
+ *
+ * @since 3.1
+ */
+public abstract class NLS {
+
+//     private static final Object[] EMPTY_ARGS = new Object[0];
+//     private static final String EXTENSION = ".properties"; //$NON-NLS-1$
+//     private static String[] nlSuffixes;
+//     /*
+//      * NOTE do not change the name of this field; it is set by the Framework using reflection
+//      */
+//     private static FrameworkLog frameworkLog;
+//
+//     static final int SEVERITY_ERROR = 0x04;
+//     static final int SEVERITY_WARNING = 0x02;
+//     /*
+//      * This object is assigned to the value of a field map to indicate
+//      * that a translated message has already been assigned to that field.
+//      */
+//     static final Object ASSIGNED = new Object();
+//
+//     /**
+//      * Creates a new NLS instance.
+//      */
+//     protected NLS() {
+//         super();
+//     }
+
+    /**
+     * Bind the given message's substitution locations with the given string value.
+     *
+     * @param message the message to be manipulated
+     * @param binding the object to be inserted into the message
+     * @return the manipulated String
+     * @throws IllegalArgumentException if the text appearing within curly braces in the given message does not map to an integer
+     */
+    public static String bind(String message, Object binding) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+//         return internalBind(message, null, String.valueOf(binding), null);
+    }
+    public static String bind(String message, String binding) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+//         return internalBind(message, null, String.valueOf(binding), null);
+    }
+
+    /**
+     * Bind the given message's substitution locations with the given string values.
+     *
+     * @param message the message to be manipulated
+     * @param binding1 An object to be inserted into the message
+     * @param binding2 A second object to be inserted into the message
+     * @return the manipulated String
+     * @throws IllegalArgumentException if the text appearing within curly braces in the given message does not map to an integer
+     */
+    public static String bind(String message, Object binding1, Object binding2) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+//         return internalBind(message, null, String.valueOf(binding1), String.valueOf(binding2));
+    }
+    public static String bind(String message, String binding1, String binding2) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+//         return internalBind(message, null, String.valueOf(binding1), String.valueOf(binding2));
+    }
+
+    /**
+     * Bind the given message's substitution locations with the given string values.
+     *
+     * @param message the message to be manipulated
+     * @param bindings An array of objects to be inserted into the message
+     * @return the manipulated String
+     * @throws IllegalArgumentException if the text appearing within curly braces in the given message does not map to an integer
+     */
+    public static String bind(String message, Object[] bindings) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+//         return internalBind(message, bindings, null, null);
+    }
+    public static String bind(String message, String[] bindings) {
+        implMissing( __FILE__, __LINE__ );
+        return null;
+//         return internalBind(message, bindings, null, null);
+    }
+
+//     /**
+//      * Initialize the given class with the values from the specified message bundle.
+//      *
+//      * @param bundleName fully qualified path of the class name
+//      * @param clazz the class where the constants will exist
+//      */
+//     public static void initializeMessages(final String bundleName, final Class clazz) {
+//         if (System.getSecurityManager() is null) {
+//             load(bundleName, clazz);
+//             return;
+//         }
+//         AccessController.doPrivileged(new PrivilegedAction() {
+//             public Object run() {
+//                 load(bundleName, clazz);
+//                 return null;
+//             }
+//         });
+//     }
+//
+//     /*
+//      * Perform the string substitution on the given message with the specified args.
+//      * See the class comment for exact details.
+//      */
+//     private static String internalBind(String message, Object[] args, String argZero, String argOne) {
+//         if (message is null)
+//             return "No message available."; //$NON-NLS-1$
+//         if (args is null || args.length is 0)
+//             args = EMPTY_ARGS;
+//
+//         int length = message.length();
+//         //estimate correct size of string buffer to avoid growth
+//         int bufLen = length + (args.length * 5);
+//         if (argZero !is null)
+//             bufLen += argZero.length() - 3;
+//         if (argOne !is null)
+//             bufLen += argOne.length() - 3;
+//         StringBuffer buffer = new StringBuffer(bufLen < 0 ? 0 : bufLen);
+//         for (int i = 0; i < length; i++) {
+//             char c = message.charAt(i);
+//             switch (c) {
+//                 case '{' :
+//                     int index = message.indexOf('}', i);
+//                     // if we don't have a matching closing brace then...
+//                     if (index is -1) {
+//                         buffer.append(c);
+//                         break;
+//                     }
+//                     i++;
+//                     if (i >= length) {
+//                         buffer.append(c);
+//                         break;
+//                     }
+//                     // look for a substitution
+//                     int number = -1;
+//                     try {
+//                         number = Integer.parseInt(message.substring(i, index));
+//                     } catch (NumberFormatException e) {
+//                         throw new IllegalArgumentException();
+//                     }
+//                     if (number is 0 && argZero !is null)
+//                         buffer.append(argZero);
+//                     else if (number is 1 && argOne !is null)
+//                         buffer.append(argOne);
+//                     else {
+//                         if (number >= args.length || number < 0) {
+//                             buffer.append("<missing argument>"); //$NON-NLS-1$
+//                             i = index;
+//                             break;
+//                         }
+//                         buffer.append(args[number]);
+//                     }
+//                     i = index;
+//                     break;
+//                 case '\'' :
+//                     // if a single quote is the last char on the line then skip it
+//                     int nextIndex = i + 1;
+//                     if (nextIndex >= length) {
+//                         buffer.append(c);
+//                         break;
+//                     }
+//                     char next = message.charAt(nextIndex);
+//                     // if the next char is another single quote then write out one
+//                     if (next is '\'') {
+//                         i++;
+//                         buffer.append(c);
+//                         break;
+//                     }
+//                     // otherwise we want to read until we get to the next single quote
+//                     index = message.indexOf('\'', nextIndex);
+//                     // if there are no more in the string, then skip it
+//                     if (index is -1) {
+//                         buffer.append(c);
+//                         break;
+//                     }
+//                     // otherwise write out the chars inside the quotes
+//                     buffer.append(message.substring(nextIndex, index));
+//                     i = index;
+//                     break;
+//                 default :
+//                     buffer.append(c);
+//             }
+//         }
+//         return buffer.toString();
+//     }
+//
+//     /*
+//      * Build an array of property files to search.  The returned array contains
+//      * the property fields in order from most specific to most generic.
+//      * So, in the FR_fr locale, it will return file_fr_FR.properties, then
+//      * file_fr.properties, and finally file.properties.
+//      */
+//     private static String[] buildVariants(String root) {
+//         if (nlSuffixes is null) {
+//             //build list of suffixes for loading resource bundles
+//             String nl = Locale.getDefault().toString();
+//             ArrayList result = new ArrayList(4);
+//             int lastSeparator;
+//             while (true) {
+//                 result.add('_' + nl + EXTENSION);
+//                 lastSeparator = nl.lastIndexOf('_');
+//                 if (lastSeparator is -1)
+//                     break;
+//                 nl = nl.substring(0, lastSeparator);
+//             }
+//             //add the empty suffix last (most general)
+//             result.add(EXTENSION);
+//             nlSuffixes = (String[]) result.toArray(new String[result.size()]);
+//         }
+//         root = root.replace('.', '/');
+//         String[] variants = new String[nlSuffixes.length];
+//         for (int i = 0; i < variants.length; i++)
+//             variants[i] = root + nlSuffixes[i];
+//         return variants;
+//     }
+//
+//     private static void computeMissingMessages(String bundleName, Class clazz, Map fieldMap, Field[] fieldArray, bool isAccessible) {
+//         // iterate over the fields in the class to make sure that there aren't any empty ones
+//         final int MOD_EXPECTED = Modifier.PUBLIC | Modifier.STATIC;
+//         final int MOD_MASK = MOD_EXPECTED | Modifier.FINAL;
+//         final int numFields = fieldArray.length;
+//         for (int i = 0; i < numFields; i++) {
+//             Field field = fieldArray[i];
+//             if ((field.getModifiers() & MOD_MASK) !is MOD_EXPECTED)
+//                 continue;
+//             //if the field has a a value assigned, there is nothing to do
+//             if (fieldMap.get(field.getName()) is ASSIGNED)
+//                 continue;
+//             try {
+//                 // Set a value for this empty field. We should never get an exception here because
+//                 // we know we have a public static non-final field. If we do get an exception, silently
+//                 // log it and continue. This means that the field will (most likely) be un-initialized and
+//                 // will fail later in the code and if so then we will see both the NPE and this error.
+//                 String value = "NLS missing message: " + field.getName() + " in: " + bundleName; //$NON-NLS-1$ //$NON-NLS-2$
+//                 if (Debug.DEBUG_MESSAGE_BUNDLES)
+//                     System.out.println(value);
+//                 log(SEVERITY_WARNING, value, null);
+//                 if (!isAccessible)
+//                     field.setAccessible(true);
+//                 field.set(null, value);
+//             } catch (Exception e) {
+//                 log(SEVERITY_ERROR, "Error setting the missing message value for: " + field.getName(), e); //$NON-NLS-1$
+//             }
+//         }
+//     }
+//
+//     /*
+//      * Load the given resource bundle using the specified class loader.
+//      */
+//     static void load(final String bundleName, Class clazz) {
+//         long start = System.currentTimeMillis();
+//         final Field[] fieldArray = clazz.getDeclaredFields();
+//         ClassLoader loader = clazz.getClassLoader();
+//
+//         bool isAccessible = (clazz.getModifiers() & Modifier.PUBLIC) !is 0;
+//
+//         //build a map of field names to Field objects
+//         final int len = fieldArray.length;
+//         Map fields = new HashMap(len * 2);
+//         for (int i = 0; i < len; i++)
+//             fields.put(fieldArray[i].getName(), fieldArray[i]);
+//
+//         // search the variants from most specific to most general, since
+//         // the MessagesProperties.put method will mark assigned fields
+//         // to prevent them from being assigned twice
+//         final String[] variants = buildVariants(bundleName);
+//         for (int i = 0; i < variants.length; i++) {
+//             // loader is null if we're launched off the Java boot classpath
+//             final InputStream input = loader is null ? ClassLoader.getSystemResourceAsStream(variants[i]) : loader.getResourceAsStream(variants[i]);
+//             if (input is null)
+//                 continue;
+//             try {
+//                 final MessagesProperties properties = new MessagesProperties(fields, bundleName, isAccessible);
+//                 properties.load(input);
+//             } catch (IOException e) {
+//                 log(SEVERITY_ERROR, "Error loading " + variants[i], e); //$NON-NLS-1$
+//             } finally {
+//                 if (input !is null)
+//                     try {
+//                         input.close();
+//                     } catch (IOException e) {
+//                         // ignore
+//                     }
+//             }
+//         }
+//         computeMissingMessages(bundleName, clazz, fields, fieldArray, isAccessible);
+//         if (Debug.DEBUG_MESSAGE_BUNDLES)
+//             System.out.println("Time to load message bundle: " + bundleName + " was " + (System.currentTimeMillis() - start) + "ms."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+//     }
+//
+//     /*
+//      * The method adds a log entry based on the error message and exception.
+//      * The output is written to the System.err.
+//      *
+//      * This method is only expected to be called if there is a problem in
+//      * the NLS mechanism. As a result, translation facility is not available
+//      * here and messages coming out of this log are generally not translated.
+//      *
+//      * @param severity - severity of the message (SEVERITY_ERROR or SEVERITY_WARNING)
+//      * @param message - message to log
+//      * @param e - exception to log
+//      */
+//     static void log(int severity, String message, Exception e) {
+//         if (frameworkLog !is null) {
+//             frameworkLog.log(new FrameworkLogEntry("dwtx.osgi", severity, 1, message, 0, e, null)); //$NON-NLS-1$
+//             return;
+//         }
+//         String statusMsg;
+//         switch (severity) {
+//             case SEVERITY_ERROR :
+//                 statusMsg = "Error: "; //$NON-NLS-1$
+//                 break;
+//             case SEVERITY_WARNING :
+//                 // intentionally fall through:
+//             default :
+//                 statusMsg = "Warning: "; //$NON-NLS-1$
+//         }
+//         if (message !is null)
+//             statusMsg += message;
+//         if (e !is null)
+//             statusMsg += ": " + e.getMessage(); //$NON-NLS-1$
+//         System.err.println(statusMsg);
+//         if (e !is null)
+//             e.printStackTrace();
+//     }
+//
+//     /*
+//      * Class which sub-classes java.util.Properties and uses the #put method
+//      * to set field values rather than storing the values in the table.
+//      */
+//     private static class MessagesProperties extends Properties {
+//
+//         private static final int MOD_EXPECTED = Modifier.PUBLIC | Modifier.STATIC;
+//         private static final int MOD_MASK = MOD_EXPECTED | Modifier.FINAL;
+//         private static final long serialVersionUID = 1L;
+//
+//         private final String bundleName;
+//         private final Map fields;
+//         private final bool isAccessible;
+//
+//         public MessagesProperties(Map fieldMap, String bundleName, bool isAccessible) {
+//             super();
+//             this.fields = fieldMap;
+//             this.bundleName = bundleName;
+//             this.isAccessible = isAccessible;
+//         }
+//
+//         /* (non-Javadoc)
+//          * @see java.util.Hashtable#put(java.lang.Object, java.lang.Object)
+//          */
+//         public synchronized Object put(Object key, Object value) {
+//             Object fieldObject = fields.put(key, ASSIGNED);
+//             // if already assigned, there is nothing to do
+//             if (fieldObject is ASSIGNED)
+//                 return null;
+//             if (fieldObject is null) {
+//                 final String msg = "NLS unused message: " + key + " in: " + bundleName;//$NON-NLS-1$ //$NON-NLS-2$
+//                 if (Debug.DEBUG_MESSAGE_BUNDLES)
+//                     System.out.println(msg);
+//                 log(SEVERITY_WARNING, msg, null);
+//                 return null;
+//             }
+//             final Field field = (Field) fieldObject;
+//             //can only set value of public static non-final fields
+//             if ((field.getModifiers() & MOD_MASK) !is MOD_EXPECTED)
+//                 return null;
+//             try {
+//                 // Check to see if we are allowed to modify the field. If we aren't (for instance
+//                 // if the class is not public) then change the accessible attribute of the field
+//                 // before trying to set the value.
+//                 if (!isAccessible)
+//                     field.setAccessible(true);
+//                 // Set the value into the field. We should never get an exception here because
+//                 // we know we have a public static non-final field. If we do get an exception, silently
+//                 // log it and continue. This means that the field will (most likely) be un-initialized and
+//                 // will fail later in the code and if so then we will see both the NPE and this error.
+//                 field.set(null, value);
+//             } catch (Exception e) {
+//                 log(SEVERITY_ERROR, "Exception setting field value.", e); //$NON-NLS-1$
+//             }
+//             return null;
+//         }
+//     }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/dwtx.core.internal.jobs.messages.properties	Tue Aug 12 02:34:21 2008 +0200
@@ -0,0 +1,20 @@
+###############################################################################
+# Copyright (c) 2000, 2005 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 Corporation - initial API and implementation
+###############################################################################
+### Runtime jobs plugin messages
+
+### Job Manager and Locks
+jobs_blocked0=The user operation is waiting for background work to complete.
+jobs_blocked1=The user operation is waiting for \"{0}\" to complete.
+jobs_internalError=An internal error occurred during: \"{0}\".
+jobs_waitFamSub={0} work item(s) left.
+
+### metadata
+meta_pluginProblems = Problems occurred when invoking code from plug-in: \"{0}\".