|
|
@@ -0,0 +1,95 @@ |
|
|
|
/* ******************************************************************* |
|
|
|
* Copyright (c) 2004 IBM |
|
|
|
* 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: |
|
|
|
* Dmitry Mikhaylov - initial implementation |
|
|
|
* ******************************************************************/ |
|
|
|
|
|
|
|
package org.aspectj.apache.bcel.classfile.tests; |
|
|
|
|
|
|
|
import org.aspectj.apache.bcel.classfile.Code; |
|
|
|
import org.aspectj.apache.bcel.classfile.JavaClass; |
|
|
|
import org.aspectj.apache.bcel.classfile.LocalVariableTable; |
|
|
|
import org.aspectj.apache.bcel.classfile.Method; |
|
|
|
|
|
|
|
import java.util.concurrent.CountDownLatch; |
|
|
|
import java.util.concurrent.ExecutorService; |
|
|
|
import java.util.concurrent.Executors; |
|
|
|
import java.util.concurrent.Semaphore; |
|
|
|
import java.util.concurrent.atomic.AtomicReference; |
|
|
|
|
|
|
|
|
|
|
|
public class LocalVariableTableConcurrencyTest extends BcelTestCase { |
|
|
|
|
|
|
|
private final int nThreads = Runtime.getRuntime().availableProcessors(); |
|
|
|
|
|
|
|
private final ExecutorService[] workers = new ExecutorService[nThreads]; |
|
|
|
|
|
|
|
private LocalVariableTable reference; |
|
|
|
|
|
|
|
protected void setUp() throws Exception { |
|
|
|
super.setUp(); |
|
|
|
|
|
|
|
for (int i = 0; i < nThreads; i++) workers[i] = Executors.newSingleThreadExecutor(); |
|
|
|
|
|
|
|
JavaClass clazz = getClassFromJar("SimpleGenericsProgram"); |
|
|
|
Method mainMethod = getMethod(clazz,"main"); |
|
|
|
Code codeAttr = (Code) findAttribute("Code",mainMethod.getAttributes()); |
|
|
|
reference = (LocalVariableTable) findAttribute("LocalVariableTable",codeAttr.getAttributes()); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Try to hit concurrency bug in org.aspectj.apache.bcel.classfile.LocalVariableTable.unpack(). |
|
|
|
* We do so by running unpack() on same instance with multiple threads, and artificially |
|
|
|
* delaying all threads but first so that they enter unpack() the moment first thread is about to leave it. |
|
|
|
* |
|
|
|
* Since this test relies on empirically obtained access pattern and number of iterations, |
|
|
|
* it never has 100% probability of hitting the bug. If it fails - there is certainly a bug. |
|
|
|
* If it passes, it could mean anything - fully correct code or slightly changed execution order preventing |
|
|
|
* threads to collide at problematic location. |
|
|
|
* |
|
|
|
* As such, it is not really good for unit testing. |
|
|
|
*/ |
|
|
|
public void testLocalVariableTableAttributeConcurrency() throws RuntimeException, InterruptedException { |
|
|
|
final AtomicReference<RuntimeException> error = new AtomicReference<>(); |
|
|
|
for (int i = 0; i < 10000; i++) { |
|
|
|
LocalVariableTable sharedInstance = reference.copyFromPackedState(); |
|
|
|
CountDownLatch preStart = new CountDownLatch(nThreads); |
|
|
|
Semaphore start = new Semaphore(0); |
|
|
|
CountDownLatch finish = new CountDownLatch(nThreads); |
|
|
|
|
|
|
|
for (int j = 0; j < nThreads; j++) { |
|
|
|
final boolean needsDelay = j > 0; |
|
|
|
workers[j].execute(() -> { |
|
|
|
preStart.countDown(); |
|
|
|
start.acquireUninterruptibly(); |
|
|
|
// trying to trigger concurrent unpacking bug - one tread should enter unpack() when other is about to leave it |
|
|
|
if (needsDelay) reference.copyFromPackedState().getTableLength(); |
|
|
|
try { |
|
|
|
sharedInstance.getTableLength(); |
|
|
|
} |
|
|
|
catch (RuntimeException ex) { |
|
|
|
error.compareAndSet(null, ex); |
|
|
|
} |
|
|
|
finish.countDown(); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
preStart.await(); |
|
|
|
start.release(nThreads); |
|
|
|
finish.await(); |
|
|
|
|
|
|
|
if (error.get() != null) throw error.get(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
protected void tearDown() throws Exception { |
|
|
|
for (int i = 0; i < nThreads; i++) if (workers[i] != null) workers[i].shutdownNow(); |
|
|
|
super.tearDown(); |
|
|
|
} |
|
|
|
} |