@@ -1772,50 +1772,66 @@ private[spark] object Utils extends Logging {
17721772 }
17731773
17741774 /**
1775- * Terminates a process waiting for at most the specified duration. Returns whether
1776- * the process terminated.
1775+ * Terminates a process waiting for at most the specified duration.
1776+ *
1777+ * @return the process exit value if it was successfully terminated, else None
17771778 */
17781779 def terminateProcess (process : Process , timeoutMs : Long ): Option [Int ] = {
1779- try {
1780- // Java8 added a new API which will more forcibly kill the process. Use that if available.
1781- val destroyMethod = process.getClass().getMethod(" destroyForcibly" );
1782- destroyMethod.setAccessible(true )
1783- destroyMethod.invoke(process)
1784- } catch {
1785- case NonFatal (e) =>
1786- if (! e.isInstanceOf [NoSuchMethodException ]) {
1787- logWarning(" Exception when attempting to kill process" , e)
1788- }
1789- process.destroy()
1790- }
1780+ // Politely destroy first
1781+ process.destroy()
1782+
17911783 if (waitForProcess(process, timeoutMs)) {
1784+ // Successful exit
17921785 Option (process.exitValue())
17931786 } else {
1794- None
1787+ // Java 8 added a new API which will more forcibly kill the process. Use that if available.
1788+ try {
1789+ classOf [Process ].getMethod(" destroyForcibly" ).invoke(process)
1790+ } catch {
1791+ case _ : NoSuchMethodException => return None // Not available; give up
1792+ case NonFatal (e) => logWarning(" Exception when attempting to kill process" , e)
1793+ }
1794+ // Wait, again, although this really should return almost immediately
1795+ if (waitForProcess(process, timeoutMs)) {
1796+ Option (process.exitValue())
1797+ } else {
1798+ logWarning(" Timed out waiting to forcibly kill process" )
1799+ None
1800+ }
17951801 }
17961802 }
17971803
17981804 /**
17991805 * Wait for a process to terminate for at most the specified duration.
1800- * Return whether the process actually terminated after the given timeout.
1806+ *
1807+ * @return whether the process actually terminated before the given timeout.
18011808 */
18021809 def waitForProcess (process : Process , timeoutMs : Long ): Boolean = {
1803- var terminated = false
1804- val startTime = System .currentTimeMillis
1805- while (! terminated) {
1806- try {
1807- process.exitValue()
1808- terminated = true
1809- } catch {
1810- case e : IllegalThreadStateException =>
1811- // Process not terminated yet
1812- if (System .currentTimeMillis - startTime > timeoutMs) {
1813- return false
1810+ try {
1811+ // Use Java 8 method if available
1812+ classOf [Process ].getMethod(" waitFor" , java.lang.Long .TYPE , classOf [TimeUnit ])
1813+ .invoke(process, timeoutMs.asInstanceOf [java.lang.Long ], TimeUnit .MILLISECONDS )
1814+ .asInstanceOf [Boolean ]
1815+ } catch {
1816+ case _ : NoSuchMethodError =>
1817+ // Otherwise implement it manually
1818+ var terminated = false
1819+ val startTime = System .currentTimeMillis
1820+ while (! terminated) {
1821+ try {
1822+ process.exitValue()
1823+ terminated = true
1824+ } catch {
1825+ case e : IllegalThreadStateException =>
1826+ // Process not terminated yet
1827+ if (System .currentTimeMillis - startTime > timeoutMs) {
1828+ return false
1829+ }
1830+ Thread .sleep(100 )
18141831 }
1815- Thread .sleep( 100 )
1816- }
1832+ }
1833+ true
18171834 }
1818- true
18191835 }
18201836
18211837 /**
0 commit comments