From c973566f543122c56b06b82fae4671dc880dc05f Mon Sep 17 00:00:00 2001 From: Dmitry Mikhaylov Date: Mon, 28 Jun 2021 17:38:28 +0300 Subject: [PATCH] test for failing synchronization in LocalVariableTable.unpack --- .../bcel/classfile/LocalVariableTable.java | 15 ++- .../LocalVariableTableConcurrencyTest.java | 94 +++++++++++++++++++ 2 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/LocalVariableTableConcurrencyTest.java diff --git a/bcel-builder/src/main/java/org/aspectj/apache/bcel/classfile/LocalVariableTable.java b/bcel-builder/src/main/java/org/aspectj/apache/bcel/classfile/LocalVariableTable.java index e6415dae6..545227050 100644 --- a/bcel-builder/src/main/java/org/aspectj/apache/bcel/classfile/LocalVariableTable.java +++ b/bcel-builder/src/main/java/org/aspectj/apache/bcel/classfile/LocalVariableTable.java @@ -63,7 +63,7 @@ import org.aspectj.apache.bcel.Constants; /** * This class represents collection of local variables in a method. This attribute is contained in the Code attribute. - * + * * @version $Id: LocalVariableTable.java,v 1.8 2009/09/15 19:40:12 aclement Exp $ * @author M. Dahm * @see Code @@ -99,7 +99,7 @@ public class LocalVariableTable extends Attribute { /** * Construct object from file stream. - * + * * @param name_index Index in constant pool * @param length Content length in bytes * @param file Input stream @@ -117,7 +117,7 @@ public class LocalVariableTable extends Attribute { /** * Called by objects that are traversing the nodes of the tree implicitely defined by the contents of a Java class. I.e., the * hierarchy of methods, fields, attributes, etc. spawns a tree of objects. - * + * * @param v Visitor object */ @Override @@ -128,7 +128,7 @@ public class LocalVariableTable extends Attribute { /** * Dump local variable table attribute to file stream in binary format. - * + * * @param file Output file stream * @throws IOException */ @@ -189,6 +189,11 @@ public class LocalVariableTable extends Attribute { return buf.toString(); } + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + /** * @return deep copy of this attribute */ @@ -223,7 +228,7 @@ public class LocalVariableTable extends Attribute { dis.close(); data = null; // throw it away now } catch (IOException e) { - throw new RuntimeException("Unpacking of LocalVariableTable attribute failed"); + throw new RuntimeException("Unpacking of LocalVariableTable attribute failed", e); } isInPackedState = false; } diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/LocalVariableTableConcurrencyTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/LocalVariableTableConcurrencyTest.java new file mode 100644 index 000000000..da9b8c296 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/LocalVariableTableConcurrencyTest.java @@ -0,0 +1,94 @@ +/* ******************************************************************* + * 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: + * Andy Clement - 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 org.aspectj.apache.bcel.classfile.tests.BcelTestCase; + +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()); + } + + private LocalVariableTable createReferenceCopy() { + try { + return (LocalVariableTable) reference.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException("Faileed to clone LocalVariableTable", e); + } + } + + public void testLocalVariableTableAttributeConcurrency() throws RuntimeException, InterruptedException { + final AtomicReference error = new AtomicReference<>(); + for (int i = 0; i < 100000; i++) { + LocalVariableTable sharedInstance = createReferenceCopy(); + 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 - one tread should enter unpack() when other is about to leave it + if (needsDelay) createReferenceCopy().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(); + } +} -- 2.39.5