diff options
author | Eike Stepper | 2016-01-15 17:56:43 +0000 |
---|---|---|
committer | Eike Stepper | 2016-01-15 17:56:43 +0000 |
commit | 02fbd2120884fc08c98b507c60278f0310ee98a4 (patch) | |
tree | fb9ddeb6d96cc8c134524fba23fcbe3ec49dae96 | |
parent | 00e61cd9bbb13a48ad936852f6c1e8de46072c10 (diff) | |
download | cdo-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
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 { |