Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Stepper2016-01-15 17:56:43 +0000
committerEike Stepper2016-01-15 17:56:43 +0000
commit02fbd2120884fc08c98b507c60278f0310ee98a4 (patch)
treefb9ddeb6d96cc8c134524fba23fcbe3ec49dae96
parent00e61cd9bbb13a48ad936852f6c1e8de46072c10 (diff)
downloadcdo-02fbd2120884fc08c98b507c60278f0310ee98a4.tar.gz
cdo-02fbd2120884fc08c98b507c60278f0310ee98a4.tar.xz
cdo-02fbd2120884fc08c98b507c60278f0310ee98a4.zip
[323788] [Dawn] UI freeze on remote edge deletion tests
https://bugs.eclipse.org/bugs/show_bug.cgi?id=323788
-rw-r--r--plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/NonFairReentrantLockTest.java133
-rw-r--r--plugins/org.eclipse.net4j.util.ui/plugin.xml10
-rw-r--r--plugins/org.eclipse.net4j.util.ui/src/org/eclipse/net4j/util/internal/ui/DisplayDelegateDetector.java65
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/DelegableReentrantLock.java202
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/NonFairReentrantLock.java268
-rw-r--r--plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/Worker.java8
6 files changed, 682 insertions, 4 deletions
diff --git a/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/NonFairReentrantLockTest.java b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/NonFairReentrantLockTest.java
new file mode 100644
index 0000000000..558ef617a5
--- /dev/null
+++ b/plugins/org.eclipse.net4j.tests/src/org/eclipse/net4j/util/tests/NonFairReentrantLockTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2004-2015 Eike Stepper (Berlin, Germany) 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:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.net4j.util.tests;
+
+import org.eclipse.net4j.util.concurrent.NonFairReentrantLock;
+import org.eclipse.net4j.util.concurrent.QueueRunner;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * @author Eike Stepper
+ * @since 3.6
+ */
+public class NonFairReentrantLockTest
+{
+ public static void main(String[] args) throws Exception
+ {
+ final QueueRunner eventQueue = new QueueRunner();
+ eventQueue.activate();
+
+ final Lock lock = new NonFairReentrantLock()
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected boolean isOwner(Thread thread, Thread owner)
+ {
+ if (super.isOwner(thread, owner))
+ {
+ return true;
+ }
+
+ // State state = owner.getState();
+ // if (state == State.WAITING)
+ // {
+ // StackTraceElement[] stackTrace = owner.getStackTrace();
+ //
+ // }
+
+ return thread == eventQueue.getWorkerThread();
+ }
+ };
+
+ /**
+ * @author Eike Stepper
+ */
+ class Event implements Runnable
+ {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ public void run()
+ {
+ System.out.println("event");
+ lock.lock();
+
+ try
+ {
+ System.out.println("forked");
+ }
+ finally
+ {
+ lock.unlock();
+ latch.countDown();
+ }
+ }
+ }
+
+ Thread[] threads = new Thread[20];
+ for (int i = 0; i < threads.length; i++)
+ {
+ threads[i] = new Thread()
+ {
+ @Override
+ public void run()
+ {
+ lock.lock();
+
+ try
+ {
+ nested();
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+
+ private void nested()
+ {
+ lock.lock();
+ System.out.println("nested");
+
+ try
+ {
+ Event event = new Event();
+ eventQueue.addWork(event);
+
+ try
+ {
+ event.latch.await();
+ }
+ catch (Throwable ex)
+ {
+ //$FALL-THROUGH$
+ }
+ }
+ finally
+ {
+ lock.unlock();
+ }
+ }
+ };
+
+ threads[i].start();
+ }
+
+ for (int i = 0; i < threads.length; i++)
+ {
+ threads[i].join();
+ }
+
+ eventQueue.deactivate();
+ }
+}
diff --git a/plugins/org.eclipse.net4j.util.ui/plugin.xml b/plugins/org.eclipse.net4j.util.ui/plugin.xml
index 39b0082113..0e662296b2 100644
--- a/plugins/org.eclipse.net4j.util.ui/plugin.xml
+++ b/plugins/org.eclipse.net4j.util.ui/plugin.xml
@@ -39,13 +39,15 @@
<factory
class="org.eclipse.net4j.util.internal.ui.InteractiveCredentialsProviderFactory"
productGroup="org.eclipse.net4j.util.security.credentialsProviders"
- type="interactive">
- </factory>
+ type="interactive"/>
<factory
class="org.eclipse.net4j.util.ui.confirmation.InteractiveConfirmationProvider$Factory"
productGroup="org.eclipse.net4j.util.confirmationProviders"
- type="interactive">
- </factory>
+ type="interactive"/>
+ <factory
+ class="org.eclipse.net4j.util.internal.ui.DisplayDelegateDetector$Factory"
+ productGroup="org.eclipse.net4j.util.concurrent.delegateDetectors"
+ type="display"/>
</extension>
</plugin>
diff --git a/plugins/org.eclipse.net4j.util.ui/src/org/eclipse/net4j/util/internal/ui/DisplayDelegateDetector.java b/plugins/org.eclipse.net4j.util.ui/src/org/eclipse/net4j/util/internal/ui/DisplayDelegateDetector.java
new file mode 100644
index 0000000000..35263ee424
--- /dev/null
+++ b/plugins/org.eclipse.net4j.util.ui/src/org/eclipse/net4j/util/internal/ui/DisplayDelegateDetector.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016 Eike Stepper (Berlin, Germany) 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:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.net4j.util.internal.ui;
+
+import org.eclipse.net4j.util.concurrent.DelegableReentrantLock.DelegateDetector;
+import org.eclipse.net4j.util.factory.ProductCreationException;
+import org.eclipse.net4j.util.ui.UIUtil;
+
+import org.eclipse.swt.widgets.Display;
+
+import java.lang.Thread.State;
+
+/**
+ * @author Eike Stepper
+ */
+public class DisplayDelegateDetector implements DelegateDetector
+{
+ public DisplayDelegateDetector()
+ {
+ }
+
+ public boolean isDelegate(Thread thread, Thread owner)
+ {
+ if (owner.getState() == State.WAITING)
+ {
+ Display display = UIUtil.getDisplay();
+ Thread displayThread = display.getThread();
+
+ if (thread == displayThread && display.getSyncThread() == owner)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static abstract class Factory
+ extends org.eclipse.net4j.util.concurrent.DelegableReentrantLock.DelegateDetector.Factory
+ {
+ public static final String TYPE = "display";
+
+ public Factory()
+ {
+ super(TYPE);
+ }
+
+ @Override
+ public DelegateDetector create(String description) throws ProductCreationException
+ {
+ return new DisplayDelegateDetector();
+ }
+ }
+}
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/DelegableReentrantLock.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/DelegableReentrantLock.java
new file mode 100644
index 0000000000..a0f87c9551
--- /dev/null
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/DelegableReentrantLock.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2016 Eike Stepper (Berlin, Germany) 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:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.net4j.util.concurrent;
+
+import org.eclipse.net4j.util.container.ContainerEventAdapter;
+import org.eclipse.net4j.util.container.IContainer;
+import org.eclipse.net4j.util.container.IManagedContainer;
+import org.eclipse.net4j.util.container.IPluginContainer;
+import org.eclipse.net4j.util.event.EventUtil;
+import org.eclipse.net4j.util.event.IListener;
+import org.eclipse.net4j.util.factory.ProductCreationException;
+import org.eclipse.net4j.util.lifecycle.ILifecycle;
+import org.eclipse.net4j.util.lifecycle.LifecycleException;
+import org.eclipse.net4j.util.lifecycle.LifecycleState;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * @author Eike Stepper
+ * @since 3.6
+ */
+public class DelegableReentrantLock extends NonFairReentrantLock implements ILifecycle
+{
+ private static final long serialVersionUID = 1L;
+
+ private final IManagedContainer container;
+
+ private final IListener containerListener = new ContainerEventAdapter<Object>()
+ {
+ @Override
+ protected void onAdded(IContainer<Object> container, Object element)
+ {
+ addDelegateDetector(element);
+ }
+
+ @Override
+ protected void onRemoved(IContainer<Object> container, Object element)
+ {
+ removeDelegateDetector(element);
+ }
+ };
+
+ private final List<DelegateDetector> delegateDetectors = new CopyOnWriteArrayList<DelegateDetector>();
+
+ private volatile boolean active;
+
+ public DelegableReentrantLock(IManagedContainer container)
+ {
+ this.container = container;
+ }
+
+ public DelegableReentrantLock()
+ {
+ this(IPluginContainer.INSTANCE);
+ }
+
+ public final IManagedContainer getContainer()
+ {
+ return container;
+ }
+
+ public final synchronized void activate() throws LifecycleException
+ {
+ if (!active)
+ {
+ active = true;
+
+ for (Object element : container.getElements(DelegateDetector.Factory.PRODUCT_GROUP))
+ {
+ addDelegateDetector(element);
+ }
+
+ container.addListener(containerListener);
+ }
+ }
+
+ public final synchronized Exception deactivate()
+ {
+ if (active)
+ {
+ try
+ {
+ container.removeListener(containerListener);
+ delegateDetectors.clear();
+ }
+ catch (Exception ex)
+ {
+ return ex;
+ }
+ finally
+ {
+ active = false;
+ }
+ }
+
+ return null;
+ }
+
+ public final LifecycleState getLifecycleState()
+ {
+ return active ? LifecycleState.ACTIVE : LifecycleState.INACTIVE;
+ }
+
+ public final boolean isActive()
+ {
+ return active;
+ }
+
+ public final void addListener(IListener listener)
+ {
+ // Do nothing
+ }
+
+ public final void removeListener(IListener listener)
+ {
+ // Do nothing
+ }
+
+ public final IListener[] getListeners()
+ {
+ return EventUtil.NO_LISTENERS;
+ }
+
+ public final boolean hasListeners()
+ {
+ return false;
+ }
+
+ @Override
+ protected boolean isOwner(Thread thread, Thread owner)
+ {
+ if (super.isOwner(thread, owner))
+ {
+ return true;
+ }
+
+ return isDelegate(thread, owner);
+ }
+
+ protected boolean isDelegate(Thread thread, Thread owner)
+ {
+ for (DelegateDetector delegateDetector : delegateDetectors)
+ {
+ if (delegateDetector.isDelegate(thread, owner))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void addDelegateDetector(Object element)
+ {
+ if (element instanceof DelegateDetector)
+ {
+ DelegateDetector delegateDetector = (DelegateDetector)element;
+ delegateDetectors.add(delegateDetector);
+ }
+ }
+
+ private void removeDelegateDetector(Object element)
+ {
+ if (element instanceof DelegateDetector)
+ {
+ DelegateDetector delegateDetector = (DelegateDetector)element;
+ delegateDetectors.remove(delegateDetector);
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public interface DelegateDetector
+ {
+ public boolean isDelegate(Thread thread, Thread owner);
+
+ /**
+ * @author Eike Stepper
+ */
+ public static abstract class Factory extends org.eclipse.net4j.util.factory.Factory
+ {
+ public static final String PRODUCT_GROUP = "org.eclipse.net4j.util.concurrent.delegateDetectors";
+
+ public Factory(String type)
+ {
+ super(PRODUCT_GROUP, type);
+ }
+
+ public abstract DelegateDetector create(String description) throws ProductCreationException;
+ }
+ }
+}
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/NonFairReentrantLock.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/NonFairReentrantLock.java
new file mode 100644
index 0000000000..5f408c1fa0
--- /dev/null
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/NonFairReentrantLock.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2016 Eike Stepper (Berlin, Germany) 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:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.net4j.util.concurrent;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.AbstractQueuedSynchronizer;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * @author Eike Stepper
+ * @since 3.6
+ */
+public class NonFairReentrantLock implements Lock, Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ private final Sync sync = new Sync();
+
+ public NonFairReentrantLock()
+ {
+ }
+
+ public void lock()
+ {
+ sync.lock();
+ }
+
+ public void lockInterruptibly() throws InterruptedException
+ {
+ sync.acquireInterruptibly(1);
+ }
+
+ public boolean tryLock()
+ {
+ return sync.tryAcquire(1);
+ }
+
+ public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException
+ {
+ return sync.tryAcquireNanos(1, unit.toNanos(timeout));
+ }
+
+ public void unlock()
+ {
+ sync.release(1);
+ }
+
+ public Condition newCondition()
+ {
+ return sync.newCondition();
+ }
+
+ public int getHoldCount()
+ {
+ return sync.getHoldCount();
+ }
+
+ public boolean isHeldByCurrentThread()
+ {
+ return sync.isHeldExclusively();
+ }
+
+ public boolean isLocked()
+ {
+ return sync.isLocked();
+ }
+
+ public Thread getOwner()
+ {
+ return sync.getOwner();
+ }
+
+ public final boolean hasQueuedThreads()
+ {
+ return sync.hasQueuedThreads();
+ }
+
+ public final boolean hasQueuedThread(Thread thread)
+ {
+ return sync.isQueued(thread);
+ }
+
+ public final int getQueueLength()
+ {
+ return sync.getQueueLength();
+ }
+
+ public boolean hasWaiters(Condition condition)
+ {
+ if (condition == null)
+ {
+ throw new NullPointerException();
+ }
+
+ if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
+ {
+ throw new IllegalArgumentException("not owner");
+ }
+
+ return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
+ }
+
+ public int getWaitQueueLength(Condition condition)
+ {
+ if (condition == null)
+ {
+ throw new NullPointerException();
+ }
+
+ if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
+ {
+ throw new IllegalArgumentException("not owner");
+ }
+
+ return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
+ }
+
+ @Override
+ public String toString()
+ {
+ Thread o = sync.getOwner();
+ return super.toString() + (o == null ? "[Unlocked]" : "[Locked by thread " + o.getName() + "]");
+ }
+
+ protected Collection<Thread> getQueuedThreads()
+ {
+ return sync.getQueuedThreads();
+ }
+
+ protected Collection<Thread> getWaitingThreads(Condition condition)
+ {
+ if (condition == null)
+ {
+ throw new NullPointerException();
+ }
+
+ if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
+ {
+ throw new IllegalArgumentException("not owner");
+ }
+
+ return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
+ }
+
+ protected boolean isOwner(Thread thread, Thread owner)
+ {
+ return thread == owner;
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class Sync extends AbstractQueuedSynchronizer
+ {
+ private static final long serialVersionUID = 1L;
+
+ public Thread getOwner()
+ {
+ return getState() == 0 ? null : getExclusiveOwnerThread();
+ }
+
+ public void lock()
+ {
+ if (compareAndSetState(0, 1))
+ {
+ setExclusiveOwnerThread(Thread.currentThread());
+ }
+ else
+ {
+ acquire(1);
+ }
+ }
+
+ public ConditionObject newCondition()
+ {
+ return new ConditionObject();
+ }
+
+ public int getHoldCount()
+ {
+ return isHeldExclusively() ? getState() : 0;
+ }
+
+ public boolean isLocked()
+ {
+ return getState() != 0;
+ }
+
+ @Override
+ public boolean tryAcquire(int acquires)
+ {
+ final Thread current = Thread.currentThread();
+ int c = getState();
+ if (c == 0)
+ {
+ if (compareAndSetState(0, acquires))
+ {
+ setExclusiveOwnerThread(current);
+ return true;
+ }
+ }
+ else if (isHeldExclusively(current))
+ {
+ int nextc = c + acquires;
+ if (nextc < 0)
+ {
+ throw new Error("Maximum lock count exceeded");
+ }
+
+ setState(nextc);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ protected boolean tryRelease(int releases)
+ {
+ int c = getState() - releases;
+ if (!isHeldExclusively(Thread.currentThread()))
+ {
+ throw new IllegalMonitorStateException();
+ }
+
+ boolean free = false;
+ if (c == 0)
+ {
+ free = true;
+ setExclusiveOwnerThread(null);
+ }
+
+ setState(c);
+ return free;
+ }
+
+ @Override
+ protected boolean isHeldExclusively()
+ {
+ return isHeldExclusively(Thread.currentThread());
+ }
+
+ private boolean isHeldExclusively(Thread current)
+ {
+ return isOwner(current, getExclusiveOwnerThread());
+ }
+
+ /**
+ * For {@link Serializable}.
+ */
+ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException
+ {
+ s.defaultReadObject();
+ setState(0);
+ }
+ }
+}
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/Worker.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/Worker.java
index f61a21cb60..04292bfd4d 100644
--- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/Worker.java
+++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/concurrent/Worker.java
@@ -86,6 +86,14 @@ public abstract class Worker extends Lifecycle
this.deactivationTimeout = deactivationTimeout;
}
+ /**
+ * @since 3.6
+ */
+ public WorkerThread getWorkerThread()
+ {
+ return workerThread;
+ }
+
@Override
protected void doActivate() throws Exception
{

Back to the top