| /******************************************************************************* |
| * Copyright (c) 2010, 2012 Broadcom 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: |
| * James Blackburn (Broadcom Corp.) - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.core.tests.runtime.jobs; |
| |
| import org.eclipse.core.runtime.jobs.ILock; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.core.tests.harness.TestBarrier; |
| |
| /** |
| * Regression test for bug 311863 |
| * Interrupting a thread, just as it acquires a lock, loses the lock and deadlocks |
| * occurs if another thread is in the queue waiting for the lock |
| */ |
| public class Bug_311863 extends AbstractJobManagerTest { |
| |
| /** Signal to the threads that we're done */ |
| volatile boolean finished = false; |
| |
| final int WAIT_ACQUIRE = 10002; |
| final int RELEASED = 10013; |
| final int DONE = 10023; |
| |
| final ILock lock = Job.getJobManager().newLock(); |
| |
| /** |
| * Test thread that tries to acquire the lock, |
| * yields and sleeps |
| */ |
| class TestThread extends Thread { |
| private final TestBarrier tb; |
| private final int yield_time; |
| |
| public TestThread(TestBarrier tb, int yield_time) { |
| this.tb = tb; |
| this.yield_time = yield_time; |
| } |
| |
| public void run() { |
| while (true) { |
| tb.waitForStatus(WAIT_ACQUIRE); |
| if (finished) |
| break; |
| for (int i = 0; i < 5; i++) { |
| lock.acquire(); |
| try { |
| Thread.sleep(yield_time); |
| Thread.yield(); |
| } catch (InterruptedException e) { |
| } |
| lock.release(); |
| Thread.interrupted(); |
| } |
| tb.setStatus(RELEASED); |
| } |
| tb.setStatus(DONE); |
| } |
| } |
| |
| /** |
| * Threads: main, t1, t2, t3 |
| * Locks: lock1 |
| * |
| * All three threads try to acquire and release lock1 while being concurrently |
| * interrupted. |
| * |
| * What goes wrong in this: |
| * Say t1 holds lock1. |
| * If t2 is interrupted *just* as t1 releases the lock. t2's released |
| * semaphore is discarded leading to deadlock as t1 gets put behind |
| * t3 in the queue to acquire the lock. |
| * |
| * Particularly insidious as the UI thread is interrupted frequently |
| * |
| * @throws Exception |
| */ |
| public void testInterruptDuringLockRelease() throws Exception { |
| final TestBarrier tb1 = new TestBarrier(-1); |
| final TestBarrier tb2 = new TestBarrier(-1); |
| final TestBarrier tb3 = new TestBarrier(-1); |
| |
| // The threads that will fight over the lock |
| Thread t1 = new TestThread(tb1, 1); |
| Thread t2 = new TestThread(tb2, 2); |
| Thread t3 = new TestThread(tb3, 3); |
| |
| // Start the threads |
| t1.start(); |
| t2.start(); |
| t3.start(); |
| |
| // iterations |
| for (int i = 0; i < 10; i++) { |
| // t1, t2, t3 fight for the lock |
| tb1.setStatus(WAIT_ACQUIRE); |
| tb2.setStatus(WAIT_ACQUIRE); |
| tb3.setStatus(WAIT_ACQUIRE); |
| |
| // Comment out makes the test pass |
| for (int j = 0; j < 10; j++) { |
| t1.interrupt(); |
| t2.interrupt(); |
| t3.interrupt(); |
| Thread.yield(); |
| } |
| |
| // Release everything and start again |
| tb1.waitForStatus(RELEASED); |
| tb2.waitForStatus(RELEASED); |
| tb3.waitForStatus(RELEASED); |
| // System.out.println("Success " + i); |
| } |
| finished = true; |
| |
| // release the threads |
| tb1.setStatus(WAIT_ACQUIRE); |
| tb2.setStatus(WAIT_ACQUIRE); |
| tb3.setStatus(WAIT_ACQUIRE); |
| |
| // wait for them to go away |
| tb1.waitForStatus(DONE); |
| tb2.waitForStatus(DONE); |
| tb3.waitForStatus(DONE); |
| } |
| } |