* Fix ArrayIndexOutOfBoundsException in TempVars

* Rename project name in ANT script to "jMonkeyEngine3"

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7751 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
sha..rd 14 years ago
parent 1b988498bb
commit 8523bcb46c
  1. 2
      engine/build.xml
  2. 192
      engine/src/core/com/jme3/util/TempVars.java
  3. 2
      engine/src/test/jme3test/app/TestTempVars.java

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project name="jME3_ordered" default="default" basedir="."> <project name="jMonkeyEngine3" default="default" basedir=".">
<description>Builds, tests, and runs the project jME3_ordered.</description> <description>Builds, tests, and runs the project jME3_ordered.</description>
<import file="nbproject/build-impl.xml"/> <import file="nbproject/build-impl.xml"/>
<!-- <import file="nbproject/profiler-build-impl.xml"/> --> <!-- <import file="nbproject/profiler-build-impl.xml"/> -->

@ -44,133 +44,107 @@ import com.jme3.scene.Spatial;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
/** /**
* Temporary variables assigned to each thread. Engine classes may access * Temporary variables assigned to each thread. Engine classes may access
* these temp variables with TempVars.get(). * these temp variables with TempVars.get(), all retrieved TempVars
* This returns an available instance of the TempVar class ensuring this particular instance is never used elsewhere in the mean time. * instances must be returned via TempVars.release().
* This returns an available instance of the TempVar class ensuring this
* particular instance is never used elsewhere in the mean time.
*/ */
public class TempVars { public class TempVars {
private static final Logger log = Logger.getLogger(TempVars.class.getName()); /**
//default array size * Allow X instances of TempVars in a single thread.
private static final int arraySize = 30; */
//one array per thread private static final int STACK_SIZE = 5;
private static final ThreadLocal<TempVars[]> varsLocal = new ThreadLocal<TempVars[]>() {
/**
* <code>TempVarsStack</code> contains a stack of TempVars.
* Every time TempVars.get() is called, a new entry is added to the stack,
* and the index incremented.
* When TempVars.release() is called, the entry is checked against
* the current instance and then the index is decremented.
*/
private static class TempVarsStack {
int index = 0;
TempVars[] tempVars = new TempVars[STACK_SIZE];
}
/**
* ThreadLocal to store a TempVarsStack for each thread.
* This ensures each thread has a single TempVarsStack that is
* used only in method calls in that thread.
*/
private static final ThreadLocal<TempVarsStack> varsLocal = new ThreadLocal<TempVarsStack>() {
@Override @Override
public TempVars[] initialValue() { public TempVarsStack initialValue() {
TempVars[] l = new TempVars[arraySize]; return new TempVarsStack();
//the array is initialized with one instance (should be enough in most cases)
l[0] = new TempVars();
return l;
} }
}; };
//the current index in the stack
private static int stackLevel = 0;
//number of instanced TempVar, just to raise a warning if too much are instanced
private static int instanceCount = 1;
/** /**
* Returns an available instance of the TempVar class * This instance of TempVars has been retrieved but not released yet.
* Warning, you have to release the instance once used by calling the release() method */
* @return a TempVar instance private boolean isUsed = false;
private TempVars() {
}
/**
* Acquire an instance of the TempVar class.
* You have to release the instance after use by calling the
* release() method.
* If more than STACK_SIZE (currently 5) instances are requested
* in a single thread then an ArrayIndexOutOfBoundsException will be thrown.
*
* @return A TempVar instance
*/ */
public static TempVars get() { public static TempVars get() {
TempVarsStack stack = varsLocal.get();
TempVars vars = null;
TempVars[] array = varsLocal.get(); TempVars instance = stack.tempVars[stack.index];
//if the stack level is > 0 it means we have some instances in the array, we can return one of them
if (stackLevel >= 0) { if (instance == null){
//getting the lastTempVar instance // Create new
vars = array[stackLevel]; instance = new TempVars();
//removing the reference to it in the array so if it's never released it will be garbage collected.
array[stackLevel] = null;
//decreasing the stack level since we have one less instance
stackLevel--;
//In some cases (when lauching jme threads from another thread), the instance at 0 can be null // Put it in there
//so we check that to avoid NPE stack.tempVars[stack.index] = instance;
if (vars==null){
vars = new TempVars();
//increasing the count to keep track
instanceCount++;
}
} else {
//if the stack level is <0 it means the stack is empty, we have to create a new instance
//there
vars = new TempVars();
//increasing the count to keep track
instanceCount++;
//raising a warning if the instance count is to high, because it might be due to bad usage
if (instanceCount == array.length) {
log.log(Level.WARNING, "TempVars has been requested {0} times, maybe you forgot to call release()?", instanceCount);
}
} }
return vars;
} stack.index++;
private TempVars() { instance.isUsed = true;
return instance;
} }
// private boolean locked = false;
// private StackTraceElement[] lockerStack;
// public final boolean lock() {
// if (locked) {
// System.err.println("INTERNAL ERROR");
// System.err.println("Offending trace: ");
//
// StackTraceElement[] stack = new Throwable().getStackTrace();
// for (int i = 1; i < stack.length; i++) {
// System.err.println("\tat " + stack[i].toString());
// }
//
// System.err.println("Attempted to aquire TempVars lock owned by");
// for (int i = 1; i < lockerStack.length; i++) {
// System.err.println("\tat " + lockerStack[i].toString());
// }
// System.exit(1);
// return false;
// }
//
// lockerStack = new Throwable().getStackTrace();
// locked = true;
// return true;
// }
//
// public final boolean unlock() {
// if (!locked) {
// System.err.println("INTERNAL ERROR");
// System.err.println("Attempted to release non-existent lock: ");
//
// StackTraceElement[] stack = new Throwable().getStackTrace();
// for (int i = 1; i < stack.length; i++) {
// System.err.println("\tat " + stack[i].toString());
// }
//
// System.exit(1);
// return false;
// }
//
// lockerStack = null;
// locked = false;
// return true;
// }
/**
* Release this instance of TempVar, allowing it to be reused later.
*/
public final void release() {
//we only keep as much instances as we can, /**
//but if too much are instanced, we just don't readd them, they'll be garbage collected * Releases this instance of TempVars.
//This can happen only in case of recursive calls that are instancing the TempVar * Once released, the contents of the TempVars are undefined.
if (stackLevel < arraySize - 1) { * The TempVars must be released in the opposite order that they are retrieved,
stackLevel++; * e.g. Acquiring vars1, then acquiring vars2, vars2 MUST be released
varsLocal.get()[stackLevel] = this; * first otherwise an exception will be thrown.
*/
public void release() {
if (!isUsed){
throw new IllegalStateException("This instance of TempVars was already released!");
}
isUsed = false;
TempVarsStack stack = varsLocal.get();
// Return it to the stack
stack.index--;
// Check if it is actually there
if (stack.tempVars[stack.index] != this){
throw new IllegalStateException("An instance of TempVars has not been released in a called method!");
} }
} }
/** /**
* For interfacing with OpenGL in Renderer. * For interfacing with OpenGL in Renderer.
*/ */

@ -85,7 +85,7 @@ public class TestTempVars {
TempVars vars = TempVars.get(); TempVars vars = TempVars.get();
vars.vect1.set(123, 999, -55); vars.vect1.set(123, 999, -55);
recurse++; recurse++;
if(recurse<100){ if(recurse<5){
recursiveMethod(); recursiveMethod();
} }

Loading…
Cancel
Save