bodewig 2003/02/26 05:06:06
Modified: . build.xml
src/main/org/apache/tools/ant/taskdefs ProcessDestroyer.java
Log:
Only register ProcessDestroyer when needed (and unregister it when no
longer needed).
PR: 17216
Submitted by: James Allers <jallers at advancedreality dot com>
Revision Changes Path
1.353 +2 -0 ant/build.xml
Index: build.xml
===================================================================
RCS file: /home/cvs/ant/build.xml,v
retrieving revision 1.352
retrieving revision 1.353
diff -u -r1.352 -r1.353
--- build.xml 26 Feb 2003 11:37:20 -0000 1.352
+++ build.xml 26 Feb 2003 13:06:06 -0000 1.353
@@ -1608,6 +1608,8 @@
unless="tests.and.ant.share.classloader"/>
<exclude name="${optional.package}/metamata/MAuditParserTest.java"
unless="tests.and.ant.share.classloader"/>
+ <exclude name="${ant.package}/taskdefs/ProcessDestroyerTest.java"
+ unless="tests.and.ant.share.classloader"/>
<!-- can only run if cvs is installed on your machine
enable by setting the property have.cvs
1.6 +123 -18
ant/src/main/org/apache/tools/ant/taskdefs/ProcessDestroyer.java
Index: ProcessDestroyer.java
===================================================================
RCS file:
/home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/ProcessDestroyer.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- ProcessDestroyer.java 10 Feb 2003 14:13:36 -0000 1.5
+++ ProcessDestroyer.java 26 Feb 2003 13:06:06 -0000 1.6
@@ -1,7 +1,7 @@
/*
* The Apache Software License, Version 1.1
*
- * Copyright (c) 2001-2002 The Apache Software Foundation. All rights
+ * Copyright (c) 2001-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -54,6 +54,7 @@
package org.apache.tools.ant.taskdefs;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Vector;
@@ -65,42 +66,140 @@
* @since Ant 1.5
*/
class ProcessDestroyer
- extends Thread {
+ implements Runnable {
private Vector processes = new Vector();
-
+ // methods to register and unregister shutdown hooks
+ private Method addShutdownHookMethod;
+ private Method removeShutdownHookMethod;
+ private ProcessDestroyerImpl destroyProcessThread = null;
+
+ // whether or not this ProcessDestroyer has been registered as a
+ // shutdown hook
+ private boolean added = false;
+
+ private class ProcessDestroyerImpl extends Thread{
+ private boolean shouldDestroy = true;
+
+ public ProcessDestroyerImpl(){
+ super("ProcessDestroyer Shutdown Hook");
+ }
+ public void run(){
+ if(shouldDestroy){
+ ProcessDestroyer.this.run();
+ }
+ }
+
+ public void setShouldDestroy(boolean shouldDestroy){
+ this.shouldDestroy = shouldDestroy;
+ }
+ }
+
/**
- * Constructs a <code>ProcessDestroyer</code> and registers it as
- * a shutdown hook.
+ * Constructs a <code>ProcessDestroyer</code> and obtains
+ * <code>Runtime.addShutdownHook()</code> and
+ * <code>Runtime.removeShutdownHook()</code> through reflection. The
+ * ProcessDestroyer manages a list of processes to be destroyed when the
+ * VM exits. If a process is added when the list is empty,
+ * this <code>ProcessDestroyer</code> is registered as a shutdown hook.
If
+ * removing a process results in an empty list, the
+ * <code>ProcessDestroyer</code> is removed as a shutdown hook.
*/
public ProcessDestroyer() {
try {
- // check to see if the method exists (support pre-JDK 1.3 VMs)
- //
+ // check to see if the shutdown hook methods exists
+ // (support pre-JDK 1.3 VMs)
Class[] paramTypes = {Thread.class};
- Method addShutdownHook =
+ addShutdownHookMethod =
Runtime.class.getMethod("addShutdownHook", paramTypes);
-
- // add the hook
- //
- Object[] args = {this};
- addShutdownHook.invoke(Runtime.getRuntime(), args);
+
+ removeShutdownHookMethod =
+ Runtime.class.getMethod("removeShutdownHook", paramTypes);
+ // wait to add shutdown hook as needed
} catch (Exception e) {
// it just won't be added as a shutdown hook... :(
}
}
-
+
+ /**
+ * Registers this <code>ProcessDestroyer</code> as a shutdown hook,
+ * uses reflection to ensure pre-JDK 1.3 compatibility.
+ */
+ private void addShutdownHook(){
+ if(addShutdownHookMethod != null){
+ destroyProcessThread = new ProcessDestroyerImpl();
+ Object[] args = {destroyProcessThread};
+ try {
+ addShutdownHookMethod.invoke(Runtime.getRuntime(),args);
+ added = true;
+ } catch (IllegalAccessException e) {
+ // it just won't be added as a shutdown hook... :(
+ } catch (InvocationTargetException e) {
+ // it just won't be added as a shutdown hook... :(
+ }
+ }
+ }
+
+ /**
+ * Registers this <code>ProcessDestroyer</code> as a shutdown hook,
+ * uses reflection to ensure pre-JDK 1.3 compatibility
+ */
+ private void removeShutdownHook(){
+ if(removeShutdownHookMethod != null && destroyProcessThread != null){
+ Object[] args = {destroyProcessThread};
+ try{
+ Boolean removed =
+ (Boolean) removeShutdownHookMethod.invoke(
+ Runtime.getRuntime(),
+ args);
+ if(!removed.booleanValue()){
+ System.err.println("Could not remove shutdown hook");
+ }
+ // start the hook thread, a unstarted thread may not be
+ // eligible for garbage collection
+ destroyProcessThread.setShouldDestroy(false);
+ destroyProcessThread.start();
+ // this should return quickly, since Process.destroy()
+ try{
+ destroyProcessThread.join(20000);
+ }catch(InterruptedException ie){
+ // the thread didn't die in time
+ // it should not kill any processes unexpectedly
+ }
+ destroyProcessThread = null;
+ added = false;
+ }catch(IllegalAccessException e){
+ }catch(InvocationTargetException e){
+ }
+ }
+ }
+
+ /**
+ * Returns whether or not the ProcessDestroyer is registered as
+ * as shutdown hook
+ * @return true if this is currently added as shutdown hook
+ */
+ public boolean isAddedAsShutdownHook(){
+ return added;
+ }
+
/**
* Returns <code>true</code> if the specified <code>Process</code> was
* successfully added to the list of processes to destroy upon VM exit.
- *
+ *
* @param process the process to add
* @return <code>true</code> if the specified <code>Process</code> was
* successfully added
*/
public boolean add(Process process) {
- processes.addElement(process);
- return processes.contains(process);
+ synchronized(processes){
+ // if this list is empty, register the shutdown hook
+ if(processes.size() == 0){
+ addShutdownHook();
+ }
+ processes.addElement(process);
+ return processes.contains(process);
+ }
}
/**
@@ -112,7 +211,13 @@
* successfully removed
*/
public boolean remove(Process process) {
- return processes.removeElement(process);
+ synchronized(processes){
+ boolean processRemoved = processes.removeElement(process);
+ if(processes.size() == 0){
+ removeShutdownHook();
+ }
+ return processRemoved;
+ }
}
/**