summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephan Bergmann <sbergman@redhat.com>2014-12-09 12:51:40 +0100
committerStephan Bergmann <sbergman@redhat.com>2014-12-09 13:17:03 +0100
commit6ddde10b4006ece33bc358a391a13e108a35f6fa (patch)
tree2a1b66691b588c2cb931934133fc911b052ae61b
parent4c66ce8ec8f67d3bc2db5c79de68f4bda26d2e60 (diff)
rhbz#1036877: Join Java AsynchronousFinalizer thread well before exit
AsynchronousFinalizer was originally added as 870a4401c05beec3d31c1f6055a64591edd0a9d9 "INTEGRATION: CWS mtg1: #i57753# Avoid long-running finalize methods" referring to <https://issues.apache.org/ooo/show_bug.cgi?id=57753> " Fix JNI-UNO bridge so that the JVM doesn't run out of memory when a destructor locks the SolarMutex." It is unclear to me how relevant "If JVMs are getting more mature and should no longer have problems with long-running finalize methods, this class could be removed again" really is in practice. After all, advice on hotspot-gc-devel is to avoid finalize() if possible (<http://mail.openjdk.java.net/pipermail/hotspot-gc-dev/2014-June/010215.html> "Re: History of finalizer execution and gc progress?"). So stick with this approach of home-grown draining for now (where a home-grown approach using PhantomReferencens would need a dedicated draining thread, too, so would not have much benefit over the existing code in practice). Timely termination of AsynchronousFinalizer threads is achieved by using a dedicated thread per bridge and joining it in the remote bridge's dispose() resp. the JNI environment's new java_env_dispose. Change-Id: Idcef2dbf361a1de22f60db73828f59e85711aea7
-rw-r--r--bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/JNI_proxy.java24
-rw-r--r--bridges/source/jni_uno/jni_base.h3
-rw-r--r--bridges/source/jni_uno/jni_bridge.cxx123
-rw-r--r--bridges/source/jni_uno/jni_bridge.h11
-rw-r--r--bridges/source/jni_uno/jni_info.cxx3
-rw-r--r--bridges/source/jni_uno/jni_java2uno.cxx16
-rw-r--r--bridges/source/jni_uno/jni_uno2java.cxx10
-rw-r--r--cppuhelper/source/component_context.cxx16
-rw-r--r--jurt/com/sun/star/lib/uno/bridges/java_remote/ProxyFactory.java15
-rw-r--r--jurt/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java12
-rw-r--r--jurt/com/sun/star/lib/util/AsynchronousFinalizer.java71
11 files changed, 217 insertions, 87 deletions
diff --git a/bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/JNI_proxy.java b/bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/JNI_proxy.java
index 732d30b925b5..12817e57eeda 100644
--- a/bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/JNI_proxy.java
+++ b/bridges/source/jni_uno/java/com/sun/star/bridges/jni_uno/JNI_proxy.java
@@ -63,7 +63,7 @@ public final class JNI_proxy implements java.lang.reflect.InvocationHandler
private final Type m_type;
private final String m_oid;
private final Class m_class;
-
+ private final AsynchronousFinalizer m_finalizer;
public static String get_stack_trace( Throwable throwable )
throws Throwable
@@ -98,16 +98,19 @@ public final class JNI_proxy implements java.lang.reflect.InvocationHandler
@Override
protected void finalize()
{
- AsynchronousFinalizer.add(new AsynchronousFinalizer.Job() {
- public void run() throws Throwable {
- JNI_proxy.this.finalize( m_bridge_handle );
- }
- });
+ if (m_finalizer != null) {
+ m_finalizer.add(new AsynchronousFinalizer.Job() {
+ public void run() throws Throwable {
+ JNI_proxy.this.finalize( m_bridge_handle );
+ }
+ });
+ }
}
private JNI_proxy(
long bridge_handle, IEnvironment java_env,
- long receiver_handle, long td_handle, Type type, String oid )
+ long receiver_handle, long td_handle, Type type, String oid,
+ AsynchronousFinalizer finalizer)
{
m_bridge_handle = bridge_handle;
m_java_env = java_env;
@@ -116,16 +119,19 @@ public final class JNI_proxy implements java.lang.reflect.InvocationHandler
m_type = type;
m_oid = oid;
m_class = m_type.getZClass();
+ m_finalizer = finalizer;
}
public static Object create(
long bridge_handle, IEnvironment java_env,
long receiver_handle, long td_handle, Type type, String oid,
- java.lang.reflect.Constructor proxy_ctor )
+ java.lang.reflect.Constructor proxy_ctor,
+ AsynchronousFinalizer finalizer)
throws Throwable
{
JNI_proxy handler = new JNI_proxy(
- bridge_handle, java_env, receiver_handle, td_handle, type, oid );
+ bridge_handle, java_env, receiver_handle, td_handle, type, oid,
+ finalizer);
Object proxy = proxy_ctor.newInstance( new Object [] { handler } );
return java_env.registerInterface( proxy, new String [] { oid }, type );
}
diff --git a/bridges/source/jni_uno/jni_base.h b/bridges/source/jni_uno/jni_base.h
index 5e29c12674b0..25dee559c98a 100644
--- a/bridges/source/jni_uno/jni_base.h
+++ b/bridges/source/jni_uno/jni_base.h
@@ -125,7 +125,8 @@ class JNI_guarded_context
public:
inline explicit JNI_guarded_context(
- JNI_info const * jni_info, ::jvmaccess::UnoVirtualMachine * vm_access )
+ JNI_info const * jni_info,
+ rtl::Reference<jvmaccess::UnoVirtualMachine> const & vm_access)
: AttachGuard( vm_access->getVirtualMachine() ),
JNI_context(
jni_info, AttachGuard::getEnvironment(),
diff --git a/bridges/source/jni_uno/jni_bridge.cxx b/bridges/source/jni_uno/jni_bridge.cxx
index da3a33be415d..809eb3b96c90 100644
--- a/bridges/source/jni_uno/jni_bridge.cxx
+++ b/bridges/source/jni_uno/jni_bridge.cxx
@@ -84,8 +84,8 @@ void SAL_CALL Mapping_map_to_uno(
static_cast< Mapping const * >( mapping )->m_bridge;
JNI_guarded_context jni(
bridge->m_jni_info,
- reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
- bridge->m_java_env->pContext ) );
+ (static_cast<jni_uno::Context *>(bridge->m_java_env->pContext)
+ ->machine));
JNI_interface_type_info const * info =
static_cast< JNI_interface_type_info const * >(
@@ -135,8 +135,9 @@ void SAL_CALL Mapping_map_to_java(
static_cast< Mapping const * >( mapping )->m_bridge;
JNI_guarded_context jni(
bridge->m_jni_info,
- reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
- bridge->m_java_env->pContext ) );
+ (static_cast<jni_uno::Context *>(
+ bridge->m_java_env->pContext)
+ ->machine));
jni->DeleteGlobalRef( *ppJavaI );
*ppJavaI = 0;
}
@@ -147,8 +148,8 @@ void SAL_CALL Mapping_map_to_java(
static_cast< Mapping const * >( mapping )->m_bridge;
JNI_guarded_context jni(
bridge->m_jni_info,
- reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
- bridge->m_java_env->pContext ) );
+ (static_cast<jni_uno::Context *>(bridge->m_java_env->pContext)
+ ->machine));
JNI_interface_type_info const * info =
static_cast< JNI_interface_type_info const * >(
@@ -233,8 +234,7 @@ Bridge::Bridge(
{
// bootstrapping bridge jni_info
m_jni_info = JNI_info::get_jni_info(
- reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
- m_java_env->pContext ) );
+ static_cast<jni_uno::Context *>(m_java_env->pContext)->machine);
assert(m_java_env != 0);
assert(m_uno_env != 0);
@@ -409,21 +409,51 @@ OUString JNI_context::get_stack_trace( jobject jo_exc ) const
using namespace ::jni_uno;
-extern "C"
-{
-namespace
-{
-
+extern "C" {
-void SAL_CALL java_env_disposing( uno_Environment * java_env )
- SAL_THROW_EXTERN_C()
-{
- ::jvmaccess::UnoVirtualMachine * machine =
- reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
- java_env->pContext );
- java_env->pContext = 0;
- machine->release();
+void SAL_CALL java_env_dispose(uno_Environment * env) {
+ jni_uno::Context * context = static_cast<jni_uno::Context *>(env->pContext);
+ jobject async;
+ {
+ osl::MutexGuard g(context->mutex);
+ async = context->asynchronousFinalizer;
+ context->asynchronousFinalizer = nullptr;
+ }
+ if (async != nullptr) {
+ try {
+ jvmaccess::VirtualMachine::AttachGuard g(
+ context->machine->getVirtualMachine());
+ JNIEnv * jniEnv = g.getEnvironment();
+ jclass cl = jniEnv->FindClass(
+ "com/sun/star/lib/util/AsynchronousFinalizer");
+ if (cl == nullptr) {
+ jniEnv->ExceptionClear();
+ SAL_WARN("bridges", "exception in FindClass");
+ } else {
+ jmethodID id = jniEnv->GetMethodID(cl, "drain", "()V");
+ if (id == nullptr) {
+ jniEnv->ExceptionClear();
+ SAL_WARN("bridges", "exception in GetMethodID");
+ } else {
+ jniEnv->CallObjectMethod(async, id);
+ if (jniEnv->ExceptionOccurred()) {
+ jniEnv->ExceptionClear();
+ SAL_WARN("bridges", "exception in CallObjectMethod");
+ }
+ }
+ }
+ jniEnv->DeleteGlobalRef(async);
+ } catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) {
+ SAL_WARN(
+ "bridges",
+ "jvmaccess::VirtualMachine::AttachGuard::CreationException");
+ }
+ }
}
+
+void SAL_CALL java_env_disposing(uno_Environment * env) {
+ java_env_dispose(env);
+ delete static_cast<jni_uno::Context *>(env->pContext);
}
#ifdef DISABLE_DYNLOADING
@@ -434,14 +464,53 @@ void SAL_CALL java_env_disposing( uno_Environment * java_env )
SAL_DLLPUBLIC_EXPORT void SAL_CALL uno_initEnvironment( uno_Environment * java_env )
SAL_THROW_EXTERN_C()
{
+ java_env->pContext = new jni_uno::Context(
+ static_cast<jvmaccess::UnoVirtualMachine *>(java_env->pContext));
+ java_env->dispose = java_env_dispose;
java_env->environmentDisposing = java_env_disposing;
java_env->pExtEnv = 0; // no extended support
- assert(java_env->pContext != 0);
-
- ::jvmaccess::UnoVirtualMachine * machine =
- reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
- java_env->pContext );
- machine->acquire();
+ try {
+ jvmaccess::VirtualMachine::AttachGuard g(
+ static_cast<jni_uno::Context *>(java_env->pContext)->machine
+ ->getVirtualMachine());
+ JNIEnv * jniEnv = g.getEnvironment();
+ jclass cl = jniEnv->FindClass(
+ "com/sun/star/lib/util/AsynchronousFinalizer");
+ if (cl == nullptr) {
+ jniEnv->ExceptionClear();
+ SAL_WARN("bridges", "exception in FindClass");
+ //TODO: report failure
+ } else {
+ jmethodID id = jniEnv->GetMethodID(cl, "<init>", "()V");
+ if (id == nullptr) {
+ jniEnv->ExceptionClear();
+ SAL_WARN("bridges", "exception in GetMethodID");
+ //TODO: report failure
+ } else {
+ jobject o = jniEnv->NewObject(cl, id);
+ if (o == nullptr) {
+ jniEnv->ExceptionClear();
+ SAL_WARN("bridges", "exception in NewObject");
+ //TODO: report failure
+ } else {
+ o = jniEnv->NewGlobalRef(o);
+ if (o == nullptr) {
+ jniEnv->ExceptionClear();
+ SAL_WARN("bridges", "exception in NewGlobalRef");
+ //TODO: report failure
+ } else {
+ (static_cast<jni_uno::Context *>(java_env->pContext)->
+ asynchronousFinalizer)
+ = o;
+ }
+ }
+ }
+ }
+ } catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) {
+ SAL_WARN(
+ "bridges",
+ "jvmaccess::VirtualMachine::AttachGuard::CreationException");
+ }
}
#ifdef DISABLE_DYNLOADING
diff --git a/bridges/source/jni_uno/jni_bridge.h b/bridges/source/jni_uno/jni_bridge.h
index e1a2aadecbb6..7c5bf379a301 100644
--- a/bridges/source/jni_uno/jni_bridge.h
+++ b/bridges/source/jni_uno/jni_bridge.h
@@ -36,6 +36,17 @@
namespace jni_uno
{
+struct Context: boost::noncopyable {
+ explicit Context(
+ rtl::Reference<jvmaccess::UnoVirtualMachine> const & theMachine):
+ machine(theMachine), asynchronousFinalizer(nullptr)
+ {}
+
+ rtl::Reference<jvmaccess::UnoVirtualMachine> machine;
+ osl::Mutex mutex;
+ jobject asynchronousFinalizer;
+};
+
//==== holds environments and mappings =========================================
struct Bridge;
struct Mapping : public uno_Mapping
diff --git a/bridges/source/jni_uno/jni_info.cxx b/bridges/source/jni_uno/jni_info.cxx
index 038d971dc271..aa910f12d5b2 100644
--- a/bridges/source/jni_uno/jni_info.cxx
+++ b/bridges/source/jni_uno/jni_info.cxx
@@ -724,7 +724,8 @@ JNI_info::JNI_info(
m_method_JNI_proxy_create = jni->GetStaticMethodID(
(jclass) jo_JNI_proxy.get(), "create",
"(JLcom/sun/star/uno/IEnvironment;JJLcom/sun/star/uno/Type;Ljava/lang"
- "/String;Ljava/lang/reflect/Constructor;)Ljava/lang/Object;" );
+ "/String;Ljava/lang/reflect/Constructor;"
+ "Lcom/sun/star/lib/util/AsynchronousFinalizer;)Ljava/lang/Object;" );
jni.ensure_no_exception();
assert( 0 != m_method_JNI_proxy_create );
// field JNI_proxy.m_receiver_handle
diff --git a/bridges/source/jni_uno/jni_java2uno.cxx b/bridges/source/jni_uno/jni_java2uno.cxx
index 9aaa02f13994..086399e26dd0 100644
--- a/bridges/source/jni_uno/jni_java2uno.cxx
+++ b/bridges/source/jni_uno/jni_java2uno.cxx
@@ -58,7 +58,7 @@ jobject Bridge::map_to_java(
oid.pData, (typelib_InterfaceTypeDescription *)info->m_td.get() );
// create java and register java proxy
- jvalue args2[ 7 ];
+ jvalue args2[ 8 ];
acquire();
args2[ 0 ].j = reinterpret_cast< sal_Int64 >( this );
(*pUnoI->acquire)( pUnoI );
@@ -69,6 +69,12 @@ jobject Bridge::map_to_java(
args2[ 4 ].l = info->m_type;
args2[ 5 ].l = jo_oid.get();
args2[ 6 ].l = info->m_proxy_ctor;
+ jni_uno::Context * context = static_cast<jni_uno::Context *>(
+ m_java_env->pContext);
+ {
+ osl::MutexGuard g(context->mutex);
+ args2[ 7 ].l = context->asynchronousFinalizer;
+ }
jo_iface = jni->CallStaticObjectMethodA(
m_jni_info->m_class_JNI_proxy,
m_jni_info->m_method_JNI_proxy_create, args2 );
@@ -373,8 +379,8 @@ JNICALL Java_com_sun_star_bridges_jni_1uno_JNI_1proxy_dispatch_1call(
JNI_context jni(
jni_info, jni_env,
static_cast< jobject >(
- reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
- bridge->m_java_env->pContext )->getClassLoader() ) );
+ static_cast<Context *>(bridge->m_java_env->pContext)->machine
+ ->getClassLoader()));
OUString method_name;
@@ -620,8 +626,8 @@ JNICALL Java_com_sun_star_bridges_jni_1uno_JNI_1proxy_finalize__J(
JNI_context jni(
jni_info, jni_env,
static_cast< jobject >(
- reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
- bridge->m_java_env->pContext )->getClassLoader() ) );
+ static_cast<Context *>(bridge->m_java_env->pContext)->machine
+ ->getClassLoader()));
uno_Interface * pUnoI = reinterpret_cast< uno_Interface * >(
jni->GetLongField(
diff --git a/bridges/source/jni_uno/jni_uno2java.cxx b/bridges/source/jni_uno/jni_uno2java.cxx
index d84acf3a931c..c50be5de9a37 100644
--- a/bridges/source/jni_uno/jni_uno2java.cxx
+++ b/bridges/source/jni_uno/jni_uno2java.cxx
@@ -128,8 +128,7 @@ void Bridge::call_java(
assert( function_pos_offset == 0 || function_pos_offset == 1 );
JNI_guarded_context jni(
- m_jni_info, reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
- m_java_env->pContext ) );
+ m_jni_info, static_cast<Context *>(m_java_env->pContext)->machine);
// assure fully initialized iface_td:
::com::sun::star::uno::TypeDescription iface_holder;
@@ -529,8 +528,7 @@ void SAL_CALL UNO_proxy_free( uno_ExtEnvironment * env, void * proxy )
{
JNI_guarded_context jni(
bridge->m_jni_info,
- reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
- bridge->m_java_env->pContext ) );
+ static_cast<Context *>(bridge->m_java_env->pContext)->machine);
jni->DeleteGlobalRef( that->m_javaI );
jni->DeleteGlobalRef( that->m_jo_oid );
@@ -674,8 +672,8 @@ void SAL_CALL UNO_proxy_dispatch(
JNI_info const * jni_info = bridge->m_jni_info;
JNI_guarded_context jni(
jni_info,
- reinterpret_cast< ::jvmaccess::UnoVirtualMachine * >(
- bridge->m_java_env->pContext ) );
+ (static_cast<Context *>(bridge->m_java_env->pContext)
+ ->machine));
JNI_interface_type_info const * info =
static_cast< JNI_interface_type_info const * >(
diff --git a/cppuhelper/source/component_context.cxx b/cppuhelper/source/component_context.cxx
index 16642e9133fb..53b5eb000468 100644
--- a/cppuhelper/source/component_context.cxx
+++ b/cppuhelper/source/component_context.cxx
@@ -731,6 +731,22 @@ void ComponentContext::disposing()
for ( ; iPos != iEnd; ++iPos )
delete iPos->second;
m_map.clear();
+
+ // Hack to terminate any JNI bridge's AsynchronousFinalizer thread (as JNI
+ // proxies get finalized with arbitrary delay, so the bridge typically does
+ // not dispose itself early enough before the process exits):
+ uno_Environment ** envs;
+ sal_Int32 envCount;
+ uno_getRegisteredEnvironments(
+ &envs, &envCount, &rtl_allocateMemory, OUString("java").pData);
+ assert(envCount >= 0);
+ assert(envCount == 0 || envs != nullptr);
+ for (sal_Int32 i = 0; i != envCount; ++i) {
+ assert(envs[i] != nullptr);
+ assert(envs[i]->dispose != nullptr);
+ (*envs[i]->dispose)(envs[i]);
+ }
+ rtl_freeMemory(envs);
}
ComponentContext::ComponentContext(
diff --git a/jurt/com/sun/star/lib/uno/bridges/java_remote/ProxyFactory.java b/jurt/com/sun/star/lib/uno/bridges/java_remote/ProxyFactory.java
index edcceca44757..e249062fa07e 100644
--- a/jurt/com/sun/star/lib/uno/bridges/java_remote/ProxyFactory.java
+++ b/jurt/com/sun/star/lib/uno/bridges/java_remote/ProxyFactory.java
@@ -57,6 +57,10 @@ final class ProxyFactory {
}
}
+ public void dispose() throws InterruptedException {
+ asynchronousFinalizer.drain();
+ }
+
public static XBridge getBridge(Object obj) {
if (Proxy.isProxyClass(obj.getClass())) {
InvocationHandler h = Proxy.getInvocationHandler(obj);
@@ -126,13 +130,10 @@ final class ProxyFactory {
@Override
protected void finalize() {
- AsynchronousFinalizer.add(new AsynchronousFinalizer.Job() {
+ decrementDebugCount();
+ asynchronousFinalizer.add(new AsynchronousFinalizer.Job() {
public void run() throws Throwable {
- try {
- request("release", null);
- } finally {
- decrementDebugCount();
- }
+ request("release", null);
}
});
}
@@ -187,4 +188,6 @@ final class ProxyFactory {
private final RequestHandler requestHandler;
private final XBridge bridge;
+ private final AsynchronousFinalizer asynchronousFinalizer =
+ new AsynchronousFinalizer();
}
diff --git a/jurt/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java b/jurt/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java
index 722f825e6ba5..48784283a99a 100644
--- a/jurt/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java
+++ b/jurt/com/sun/star/lib/uno/bridges/java_remote/java_remote_bridge.java
@@ -499,7 +499,12 @@ public class java_remote_bridge
try {
_messageDispatcher.terminate();
- _xConnection.close();
+ try {
+ _xConnection.close();
+ } catch (com.sun.star.io.IOException e) {
+ System.err.println(
+ getClass().getName() + ".dispose - IOException:" + e);
+ }
if (Thread.currentThread() != _messageDispatcher
&& _messageDispatcher.isAlive())
@@ -519,6 +524,8 @@ public class java_remote_bridge
// assert _java_environment instanceof java_environment;
((java_environment) _java_environment).revokeAllProxies();
+ proxyFactory.dispose();
+
if (DEBUG) {
if (_life_count != 0) {
System.err.println(getClass().getName()
@@ -535,9 +542,6 @@ public class java_remote_bridge
} catch (InterruptedException e) {
System.err.println(getClass().getName()
+ ".dispose - InterruptedException:" + e);
- } catch (com.sun.star.io.IOException e) {
- System.err.println(getClass().getName() + ".dispose - IOException:"
- + e);
}
}
diff --git a/jurt/com/sun/star/lib/util/AsynchronousFinalizer.java b/jurt/com/sun/star/lib/util/AsynchronousFinalizer.java
index 4dcd69a97e1f..71743b25241b 100644
--- a/jurt/com/sun/star/lib/util/AsynchronousFinalizer.java
+++ b/jurt/com/sun/star/lib/util/AsynchronousFinalizer.java
@@ -35,6 +35,37 @@ import java.util.LinkedList;
* long-running finalize methods, this class could be removed again.</p>
*/
public final class AsynchronousFinalizer {
+ public AsynchronousFinalizer() {
+ thread = new Thread("AsynchronousFinalizer") {
+ @Override
+ public void run() {
+ for (;;) {
+ Job j;
+ synchronized (queue) {
+ for (;;) {
+ if (done) {
+ return;
+ }
+ if (!queue.isEmpty()) {
+ break;
+ }
+ try {
+ queue.wait();
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ j = queue.remove(0);
+ }
+ try {
+ j.run();
+ } catch (Throwable e) {}
+ }
+ }
+ };
+ thread.start();
+ }
+
/**
* Add a job to be executed asynchronously.
*
@@ -43,7 +74,7 @@ public final class AsynchronousFinalizer {
*
* @param job represents the body of some finalize method; must not be null.
*/
- public static void add(Job job) {
+ public void add(Job job) {
synchronized (queue) {
boolean first = queue.isEmpty();
queue.add(job);
@@ -53,6 +84,14 @@ public final class AsynchronousFinalizer {
}
}
+ public void drain() throws InterruptedException {
+ synchronized (queue) {
+ done = true;
+ queue.notify();
+ }
+ thread.join();
+ }
+
/**
* An interface to represent bodies of finalize methods.
*
@@ -65,31 +104,7 @@ public final class AsynchronousFinalizer {
void run() throws Throwable;
}
- private static final LinkedList<Job> queue = new LinkedList<Job>();
-
- static {
- Thread t = new Thread("AsynchronousFinalizer") {
- @Override
- public void run() {
- for (;;) {
- Job j;
- synchronized (queue) {
- while (queue.isEmpty()) {
- try {
- queue.wait();
- } catch (InterruptedException e) {}
- }
- j = queue.remove(0);
- }
- try {
- j.run();
- } catch (Throwable e) {}
- }
- }
- };
- t.setDaemon(true);
- t.start();
- }
-
- private AsynchronousFinalizer() {}
+ private final LinkedList<Job> queue = new LinkedList<Job>();
+ private final Thread thread;
+ private boolean done = false;
}