Commit f98bdcda authored by Jeff Baumes's avatar Jeff Baumes
Browse files

ENH: Updating Java wrapping to resolve memory leaking and race condition...

ENH: Updating Java wrapping to resolve memory leaking and race condition issues.  A thread-safe hash table of VTK pointers to Java objects is kept in the Java class vtkGlobalJavaHash, instead of in the C++ JNI layer.  All Java-wrapped VTK objects have a reference count owned by the Java wrapper (whether made from "new" or from the return value of a function).  That reference is deleted when the object is finalized.  To ensure all remaining objects are deleted at program termination the program should call vtkGlobalJavaHash.DeleteAll() just before exiting, which calls Delete() on the remaining Java-wrapped VTK objects.
parent 65c60466
......@@ -50,7 +50,7 @@ Java_vtk_vtkPanel_RenderCreate(JNIEnv *env, jobject canvas, jobject id0)
// get the render window pointer
vtkRenderWindow *temp0;
temp0 = (vtkRenderWindow *)(vtkJavaGetPointerFromObject(env,id0,(char *) "vtkRenderWindow"));
temp0 = (vtkRenderWindow *)(vtkJavaGetPointerFromObject(env,id0));
/* Get the AWT */
awt.version = JAWT_VERSION_1_3;
......
......@@ -13,11 +13,6 @@
=========================================================================*/
// include stdmutex for borland
#ifdef __BORLANDC__
#include <rw/stdmutex.h>
#endif
#ifdef _INTEGRAL_MAX_BITS
#undef _INTEGRAL_MAX_BITS
#endif
......@@ -27,395 +22,24 @@
#include "vtkDebugLeaks.h"
#include "vtkWindows.h"
#ifdef _WIN32
HANDLE vtkGlobalMutex = NULL;
#define VTK_GET_MUTEX() WaitForSingleObject(vtkGlobalMutex,INFINITE)
#define VTK_RELEASE_MUTEX() ReleaseMutex(vtkGlobalMutex)
#include <mapiform.h>
#else
#ifdef VTK_USE_SPROC
// for SGI's
#include <abi_mutex.h>
abilock_t vtkGlobalMutex;
static void vtk_get_mutex() {
static int inited = 0;
if (!inited) {
if (init_lock(&vtkGlobalMutex) < 0)
perror("initializing mutex");
inited = 1;
}
spin_lock(&vtkGlobalMutex);
}
static void vtk_release_mutex() {
if (release_lock(&vtkGlobalMutex) < 0)
perror("releasing mutex");
}
#define VTK_GET_MUTEX() vtk_get_mutex()
#define VTK_RELEASE_MUTEX() vtk_release_mutex()
#elif defined(__FreeBSD__) || defined(__linux__) || defined(sgi) || defined(__APPLE_CC__)
#include <pthread.h>
pthread_mutex_t vtkGlobalMutex;
static void vtk_get_mutex() {
static int inited = 0;
if (!inited)
{
inited = 1;
pthread_mutex_init ( &vtkGlobalMutex, NULL );
}
pthread_mutex_lock(&vtkGlobalMutex);
}
// #define VTK_GET_MUTEX() pthread_mutex_lock(&vtkGlobalMutex)
#define VTK_GET_MUTEX() vtk_get_mutex()
#define VTK_RELEASE_MUTEX() pthread_mutex_unlock(&vtkGlobalMutex)
#else
// for solaris
#include <thread.h>
#include <synch.h>
mutex_t vtkGlobalMutex;
#define VTK_GET_MUTEX() mutex_lock(&vtkGlobalMutex)
#define VTK_RELEASE_MUTEX() mutex_unlock(&vtkGlobalMutex)
#endif
#endif
#include "vtkJavaUtil.h"
int vtkJavaIdCount = 1;
//#define VTKJAVADEBUG
class vtkHashNode
{
public:
vtkHashNode *next;
void *key;
void *value;
};
class vtkHashTable
{
public:
vtkHashTable();
vtkHashNode *(nodes[64]);
void AddHashEntry(void *key,void *value);
void *GetHashTableValue(void *key);
void *GetHashTableValue(int key);
void *InternalGetHashTableValue(int loc, void *key);
void DeleteHashEntry(void *key);
};
vtkHashTable::vtkHashTable()
{
int i;
for (i = 0; i < 64; i++)
{
this->nodes[i] = NULL;
}
}
vtkHashTable *vtkInstanceLookup = NULL;
vtkHashTable *vtkPointerLookup = NULL;
vtkHashTable *vtkTypecastLookup = NULL;
void vtkHashTable::AddHashEntry(void *key,void *value)
{
vtkHashNode *pos;
vtkHashNode *newpos;
int loc;
newpos = new vtkHashNode;
newpos->key = key;
newpos->value = value;
newpos->next = NULL;
loc = (unsigned long)((size_t)key & 0x03f0) / 16;
pos = this->nodes[loc];
if (!pos)
{
this->nodes[loc] = newpos;
return;
}
while (pos->next)
{
pos = pos->next;
}
pos->next = newpos;
}
void *vtkHashTable::GetHashTableValue(int key)
{
int loc = (unsigned long)(key & 0x03f0) / 16;
return this->InternalGetHashTableValue(loc, (void *)(size_t)key);
}
void *vtkHashTable::GetHashTableValue(void *key)
{
int loc = (unsigned long)((size_t)key & 0x03f0) / 16;
return this->InternalGetHashTableValue(loc, key);
}
void *vtkHashTable::InternalGetHashTableValue(int loc, void *key)
{
vtkHashNode *pos;
pos = this->nodes[loc];
if (!pos)
{
return NULL;
}
while ((pos)&&(pos->key != key))
{
pos = pos->next;
}
if (pos)
{
return pos->value;
}
return NULL;
}
void vtkHashTable::DeleteHashEntry(void *key)
{
vtkHashNode *pos;
vtkHashNode *prev = NULL;
int loc = (unsigned long)((size_t)key & 0x03f0) / 16;
pos = this->nodes[loc];
while ((pos)&&(pos->key != key))
{
prev = pos;
pos = pos->next;
}
if (pos)
{
// we found this object
if (prev)
{
prev->next = pos->next;
}
else
{
this->nodes[loc] = pos->next;
}
delete pos;
}
}
JNIEXPORT int vtkJavaGetId(JNIEnv *env,jobject obj)
JNIEXPORT jlong vtkJavaGetId(JNIEnv *env,jobject obj)
{
jfieldID id;
int result;
jlong result;
id = env->GetFieldID(env->GetObjectClass(obj),"vtkId","I");
id = env->GetFieldID(env->GetObjectClass(obj),"vtkId","J");
result = (int)env->GetIntField(obj,id);
result = env->GetLongField(obj,id);
return result;
}
JNIEXPORT void vtkJavaSetId(JNIEnv *env,jobject obj, int newVal)
{
jfieldID id;
jint jNewVal = (jint)newVal;
id = env->GetFieldID(env->GetObjectClass(obj),"vtkId","I");
env->SetIntField(obj,id,jNewVal);
}
JNIEXPORT void vtkJavaRegisterCastFunction(JNIEnv *vtkNotUsed(env),
jobject vtkNotUsed(obj),
int id, void *tcFunc)
{
VTK_GET_MUTEX();
#ifdef VTKJAVADEBUG
if (id == 0) {
vtkGenericWarningMacro("RegisterCastFunction: Try to add a CastFuction to a unregistered function");
}
#endif
vtkTypecastLookup->AddHashEntry((void *)(size_t)id,tcFunc);
VTK_RELEASE_MUTEX();
}
// add an object to the hash table
JNIEXPORT int vtkJavaRegisterNewObject(JNIEnv *env, jobject obj, void *ptr)
{
if (!vtkInstanceLookup) // first call ?
{
vtkInstanceLookup = new vtkHashTable();
vtkPointerLookup = new vtkHashTable();
vtkTypecastLookup = new vtkHashTable();
// Java does not guarantee object destruction. Do not produce an
// error when leaks are detected.
vtkDebugLeaks::SetExitError(0);
#ifdef _WIN32
vtkGlobalMutex = CreateMutex(NULL, FALSE, NULL);
#endif
}
VTK_GET_MUTEX();
#ifdef VTKJAVADEBUG
vtkGenericWarningMacro("RegisterNewObject: Adding an object to hash ptr = " << ptr);
#endif
// lets make sure it isn't already there
int id = 0;
id = vtkJavaGetId(env,obj);
if (id)
{
#ifdef VTKJAVADEBUG
vtkGenericWarningMacro("RegisterNewObject: Attempt to add an object to the hash when one already exists!!!");
#endif
VTK_RELEASE_MUTEX();
return id;
}
// get a unique id for this object
// just use vtkJavaIdCount and then increment
// to handle loop around make sure the id isn't currently in use
while (vtkInstanceLookup->GetHashTableValue(vtkJavaIdCount))
{
vtkJavaIdCount++;
if (vtkJavaIdCount > 268435456) vtkJavaIdCount = 1;
}
id = vtkJavaIdCount;
vtkInstanceLookup->AddHashEntry((void *)(size_t)vtkJavaIdCount,ptr);
vtkPointerLookup->AddHashEntry(ptr,(void *)env->NewGlobalRef(obj));
vtkJavaSetId(env,obj,vtkJavaIdCount);
#ifdef VTKJAVADEBUG
vtkGenericWarningMacro("RegisterNewObject: Added object to hash id= " << vtkJavaIdCount << " " << ptr);
#endif
vtkJavaIdCount++;
VTK_RELEASE_MUTEX();
return id;
}
// delete an object from the hash
// doesn't need a mutex because it is only called from within
// the above func which does have a mutex
JNIEXPORT void vtkJavaDeleteObjectFromHash(JNIEnv *env, int id)
{
void *ptr;
void *vptr;
ptr = vtkInstanceLookup->GetHashTableValue(id);
if (!ptr)
{
#ifdef VTKJAVADEBUG
vtkGenericWarningMacro("DeleteObjectFromHash: Attempt to delete an object that doesn't exist!");
#endif
return;
}
vtkInstanceLookup->DeleteHashEntry((void *)(size_t)id);
vtkTypecastLookup->DeleteHashEntry((void *)(size_t)id);
vptr = vtkPointerLookup->GetHashTableValue(ptr);
env->DeleteGlobalRef((jobject)vptr);
vtkPointerLookup->DeleteHashEntry(ptr);
}
// should we delete this object
JNIEXPORT void vtkJavaDeleteObject(JNIEnv *env,jobject obj)
{
int id = vtkJavaGetId(env,obj);
VTK_GET_MUTEX();
#ifdef VTKJAVADEBUG
vtkGenericWarningMacro("DeleteObject: Deleting id = " << id);
#endif
vtkJavaDeleteObjectFromHash(env, id);
VTK_RELEASE_MUTEX();
}
JNIEXPORT jobject vtkJavaGetObjectFromPointer(void *ptr)
{
jobject obj;
#ifdef VTKJAVADEBUG
vtkGenericWarningMacro("GetObjectFromPointer: Checking into pointer " << ptr);
#endif
obj = (jobject)vtkPointerLookup->GetHashTableValue((jobject *)ptr);
#ifdef VTKJAVADEBUG
vtkGenericWarningMacro("GetObjectFromPointer: Checking into pointer " << ptr << " obj = " << obj);
#endif
return obj;
}
JNIEXPORT void *vtkJavaGetPointerFromObject(JNIEnv *env, jobject obj, char *result_type)
{
void *ptr;
void *(*command)(void *,char *);
int id;
if (!obj)
{
return NULL;
}
id = vtkJavaGetId(env,obj);
VTK_GET_MUTEX();
ptr = vtkInstanceLookup->GetHashTableValue(id);
command = (void *(*)(void *,char *))vtkTypecastLookup->GetHashTableValue(id);
VTK_RELEASE_MUTEX();
#ifdef VTKJAVADEBUG
vtkGenericWarningMacro("GetPointerFromObject: Checking into id " << id << " ptr = " << ptr);
#endif
if (!ptr)
{
return NULL;
}
void* res= command(ptr,result_type);
if (res)
{
#ifdef VTKJAVADEBUG
vtkGenericWarningMacro("GetPointerFromObject: Got id= " << id << " ptr= " << ptr << " " << result_type);
#endif
return res;
}
else
{
vtkGenericWarningMacro("GetPointerFromObject: vtk bad argument, type conversion failed.");
return NULL;
}
}
JNIEXPORT jobject vtkJavaCreateNewJavaStubForObject(JNIEnv *env, vtkObject* obj)
JNIEXPORT void *vtkJavaGetPointerFromObject(JNIEnv *env, jobject obj)
{
char fullname[512];
const char* classname= obj->GetClassName();
fullname[0]= 'v';
fullname[1]= 't';
fullname[2]= 'k';
fullname[3]= '/';
strcpy(&fullname[4], classname);
obj->Register(obj);
return vtkJavaCreateNewJavaStub(env, fullname, (void*)obj);
return (void*)(size_t)vtkJavaGetId(env, obj);
}
JNIEXPORT jobject vtkJavaCreateNewJavaStub(JNIEnv *env, const char* fullclassname, void* obj)
{
jclass cl= env->FindClass(fullclassname);
if (!cl) { return NULL; }
jobject stub= env->NewObject(cl, env->GetMethodID(cl, "<init>","(I)V"), (int)0 );
vtkJavaRegisterNewObject(env, stub, obj);
env->CallVoidMethod(stub, env->GetMethodID(cl, "VTKCastInit", "()V"));
return stub;
}
JNIEXPORT jarray vtkJavaMakeJArrayOfDoubleFromDouble(JNIEnv *env, double *ptr, int size)
{
jdoubleArray ret;
......@@ -935,17 +559,6 @@ JNIEXPORT void vtkJavaVoidFuncArgDelete(void* arg)
delete arg2;
}
jobject vtkJavaExportedGetObjectFromPointer(void *ptr)
{
return vtkJavaGetObjectFromPointer(ptr);
}
void* vtkJavaExportedGetPointerFromObject(JNIEnv *env,jobject obj,
char *result_type)
{
return vtkJavaGetPointerFromObject(env, obj, result_type);
}
vtkJavaCommand::vtkJavaCommand()
{
this->vm = NULL;
......
......@@ -21,15 +21,9 @@
#include "vtkCommand.h"
extern JNIEXPORT int vtkJavaGetId(JNIEnv *env,jobject obj);
extern JNIEXPORT jlong vtkJavaGetId(JNIEnv *env,jobject obj);
extern JNIEXPORT int vtkJavaRegisterNewObject(JNIEnv *env, jobject obj, void *ptr);
extern JNIEXPORT void vtkJavaRegisterCastFunction(JNIEnv *env, jobject obj, int id, void *tcFunc);
extern JNIEXPORT void *vtkJavaGetPointerFromObject(JNIEnv *env,jobject obj,
char *result_type);
extern JNIEXPORT void vtkJavaDeleteObject(JNIEnv *env, jobject obj);
extern JNIEXPORT jobject vtkJavaGetObjectFromPointer(void *ptr);
extern JNIEXPORT void *vtkJavaGetPointerFromObject(JNIEnv *env,jobject obj);
extern JNIEXPORT char *vtkJavaUTFToChar(JNIEnv *env, jstring in);
extern JNIEXPORT jstring vtkJavaMakeJavaString(JNIEnv *env, const char *in);
......@@ -58,11 +52,6 @@ extern JNIEXPORT jarray vtkJavaMakeJArrayOfUnsignedIntFromUnsignedInt(JNIEnv *en
extern JNIEXPORT jarray vtkJavaMakeJArrayOfUnsignedShortFromUnsignedShort(JNIEnv *env,unsigned short *ptr,int size);
extern JNIEXPORT jarray vtkJavaMakeJArrayOfUnsignedLongFromUnsignedLong(JNIEnv *env, unsigned long *arr, int size);
extern JNIEXPORT jobject vtkJavaCreateNewJavaStubForObject(JNIEnv *env, vtkObject* obj);
extern JNIEXPORT jobject vtkJavaCreateNewJavaStub(JNIEnv *env,
const char* fullclassname, void* obj);
// this is the void pointer parameter passed to the vtk callback routines on
// behalf of the Java interface for callbacks.
struct vtkJavaVoidFuncArg
......
......@@ -110,6 +110,9 @@ IF(JAVA_COMPILE)
${VTK_BINARY_DIR}/java/vtk/vtkBuildAllDriver.java)
ADD_CUSTOM_TARGET(VTKBuildAll ALL)
CONFIGURE_FILE(${VTK_SOURCE_DIR}/Wrapping/Java/vtk/vtkGlobalJavaHash.java
${VTK_BINARY_DIR}/java/vtk/vtkGlobalJavaHash.java COPYONLY)
# ADD_CUSTOM_COMMAND(OUTPUT ${VTK_BINARY_DIR}/java/vtk/vtkBuildAllDriver.class
# COMMAND ${JAVA_COMPILE}
# ARGS -classpath ${VTK_JAVA_HOME}/.. -d ${VTK_JAVA_HOME}/.. ${VTK_BINARY_DIR}/java/vtk/vtkBuildAllDriver.java
......
package vtk;
import vtk.vtkGlobalJavaHash;
import vtk.vtkObject;
import vtk.vtkObjectBase;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.lang.ref.WeakReference;
public class vtkGlobalJavaHash
{
public static Map PointerToReference = Collections.synchronizedMap(new HashMap());
public static void DeleteAll() {
Iterator iter = PointerToReference.values().iterator();
synchronized (PointerToReference) {
while (iter.hasNext()) {
WeakReference value = (WeakReference)iter.next();
vtkObjectBase obj = (vtkObjectBase)value.get();
if (obj != null) {
obj.Delete();
}
}
}
}
}
\ No newline at end of file
......@@ -108,6 +108,44 @@ void return_result(FILE *fp)
}
}
/* same as return_result except we return a long (the c++ pointer) rather than an object */
void return_result_native(FILE *fp)
{
switch (currentFunction->ReturnType % 0x1000)
{
case 0x1: fprintf(fp,"double "); break;
case 0x2: fprintf(fp,"void "); break;
case 0x3: fprintf(fp,"char "); break;
case 0x7: fprintf(fp,"double "); break;
case 0x4: case 0x5: case 0x6: case 0xA: case 0xB: case 0xC: case 0xD:
case 0x13: case 0x14: case 0x15: case 0x16: case 0x1A: case 0x1B: case 0x1C:
fprintf(fp,"int ");
break;
case 0xE:
fprintf(fp,"boolean ");
break;
case 0x303: fprintf(fp,"String "); break;
case 0x109:
case 0x309:
fprintf(fp,"long ");
break;
/* handle functions returning vectors */
/* this is done by looking them up in a hint file */
case 0x301: case 0x307:
fprintf(fp,"double[] ");
break;
case 0x313:
fprintf(fp,"byte[] ");
break;
case 0x304: case 0x305: case 0x306: case 0x30A: case 0x30B: case 0x30C: case 0x30D:
case 0x314: case 0x315: case 0x316: case 0x31A: case 0x31B: case 0x31C:
fprintf(fp,"int[] "); break;
case 0x30E:
fprintf(fp,"boolean[] "); break;
}
}
/* have we done one of these yet */
int DoneOne()
{
......@@ -478,7 +516,7 @@ void outputFunction(FILE *fp, FileInfo *data)
if (!DoneOne())
{
fprintf(fp,"\n private native ");
return_result(fp);
return_result_native(fp);
fprintf(fp,"%s_%i(",currentFunction->Name,numberOfWrappedFunctions);
for (i = 0; i < currentFunction->NumberOfArguments; i++)
......@@ -502,28 +540,66 @@ void outputFunction(FILE *fp, FileInfo *data)
}
output_temp(fp,i);
}
/* if not void then need return otherwise none */
if (currentFunction->ReturnType % 0x1000 == 0x2)
/* if returning object, lookup in global hash */
if (currentFunction->ReturnType % 0x1000 == 0x109 ||
currentFunction->ReturnType % 0x1000 == 0x309)
{
fprintf(fp,")\n { %s_%i(",currentFunction->Name,
numberOfWrappedFunctions);
fprintf(fp,") {");
fprintf(fp,"\n long temp = %s_%i(",currentFunction->Name, numberOfWrappedFunctions);
for (i = 0; i < currentFunction->NumberOfArguments; i++)
{
if (i)
{
fprintf(fp,",");
}
fprintf(fp,"id%i",i);
}
fprintf(fp,");\n");
fprintf(fp,"\n %s obj = null;", currentFunction->ReturnClass);
fprintf(fp,"\n java.lang.ref.WeakReference ref = (java.lang.ref.WeakReference)vtkGlobalJavaHash.PointerToReference.get(new Long(temp));");
fprintf(fp,"\n if (ref != null) {");
fprintf(fp,"\n obj = (%s)ref.get();", currentFunction->ReturnClass);
fprintf(fp,"\n }");
fprintf(fp,"\n if (obj == null) {");
fprintf(fp,"\n %s tempObj = new %s(temp);", currentFunction->ReturnClass, currentFunction->ReturnClass);
fprintf(fp,"\n String className = tempObj.GetClassName();");
fprintf(fp,"\n try {");
fprintf(fp,"\n Class c = Class.forName(\"vtk.\" + className);");
fprintf(fp,"\n java.lang.reflect.Constructor cons = c.getConstructor(new Class[] {long.class} );");
fprintf(fp,"\n obj = (%s)cons.newInstance(new Object[] {new Long(temp)});", currentFunction->ReturnClass);
fprintf(fp,"\n } catch (Exception e) {");
fprintf(fp,"\n e.printStackTrace();");
fprintf(fp,"\n }");
fprintf(fp,"\n tempObj.Delete();");
fprintf(fp,"\n }");
fprintf(fp,"\n return obj;");
fprintf(fp,"\n }\n");
}
else
{
fprintf(fp,")\n { return %s_%i(",currentFunction->Name,
numberOfWrappedFunctions);
}
for (i = 0; i < currentFunction->NumberOfArguments; i++)
{