- Timestamp:
- 2017-05-25T19:03:32+12:00 (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java
r31697 r31705 609 609 610 610 if(mainHandler != null) mainHandler.beforeProcessDestroy(); 611 SafeProcess.destroyProcess(process); // see runProcess(2 args/3 args) 611 boolean noNeedToDestroyIfOnLinux = true; // Interrupt handling suffices to cleanup process and subprocesses on Linux 612 SafeProcess.destroyProcess(process, noNeedToDestroyIfOnLinux); // see runProcess(2 args/3 args) 612 613 if(mainHandler != null) mainHandler.afterProcessDestroy(); 613 614 … … 731 732 732 733 733 // stackoverflow.com/questions/1835885/send-ctrl-c-to-process-open-by-java734 // Can't artificially send Ctrl-C: stackoverflow.com/questions/1835885/send-ctrl-c-to-process-open-by-java 734 735 // (Taskkill command can kill all running perl. But what if we kill perl instances not launched by GS?) 735 736 // stackoverflow.com/questions/912889/how-to-send-interrupt-key-sequence-to-a-java-process … … 751 752 } 752 753 753 754 // On linux and mac, p.destroy() suffices to kill processes launched by p as well. 755 // On Windows we need to do more work, since otherwise processes launched by p remain around executing until they naturally terminate. 754 // Kill signals, their names and numerical equivalents: http://www.faqs.org/qa/qa-831.html 755 // https://stackoverflow.com/questions/8533377/why-child-process-still-alive-after-parent-process-was-killed-in-linux 756 // Didn't work for when build scripts run from GLI: kill -TERM -pid 757 // but the other suggestion did work: pkill -TERM -P pid did work 758 // More reading: 759 // https://linux.die.net/man/1/kill (manual) 760 // https://unix.stackexchange.com/questions/117227/why-pidof-and-pgrep-are-behaving-differently 761 // https://unix.stackexchange.com/questions/67635/elegantly-get-list-of-children-processes 762 // https://stackoverflow.com/questions/994033/mac-os-x-quickest-way-to-kill-quit-an-entire-process-tree-from-within-a-cocoa-a 763 // https://unix.stackexchange.com/questions/132224/is-it-possible-to-get-process-group-id-from-proc 764 // https://unix.stackexchange.com/questions/99112/default-exit-code-when-process-is-terminated 765 766 /** 767 * On Unix, will kill the process denoted by processID and any subprocessed this launched. Tested on a Mac, where this is used. 768 * @param force if true will send the -KILL (-9) signal, which may result in abrupt termination without cleanup 769 * if false, will send the -TERM (-15) signal, which will allow cleanup before termination. Sending a SIGTERM is preferred. 770 * @param killEntireTree if false, will terminate only the process denoted by processID, otherwise all descendants/subprocesses too. 771 * @return true if running the kill process returned an exit value of 0 or if it had already been terminated 772 */ 773 static boolean killUnixProcessWithID(long processID, boolean force, boolean killEntireTree) { 774 775 String signal = force ? "KILL" : "TERM"; // kill -KILL (kill -9) vs preferred kill -TERM (kill -15) 776 String cmd; 777 if(killEntireTree) { // kill the process denoted by processID and any subprocesses this launched 778 779 if(Misc.isMac()) { 780 // this cmd works on Mac (tested Snow Leopard), but on Linux this cmd only terminates toplevel process 781 // when doing full-import, and doesn't always terminate subprocesses when doing full-buildcol.pl 782 cmd = "pkill -"+signal + " -P " + processID; // e.g. "pkill -TERM -P pid" 783 } 784 else { // other unix 785 // this cmd works on linux, not recognised on Mac (tested Snow Leopard): 786 cmd = "kill -"+signal + " -"+processID; // e.g. "kill -TERM -pid" 787 // note the hyphen before pid to terminate subprocesses too 788 } 789 790 } else { // kill only the process represented by the processID. 791 cmd = "kill -"+signal + " " + processID; // e.g. "kill -TERM pid" 792 } 793 794 SafeProcess proc = new SafeProcess(cmd); 795 int exitValue = proc.runProcess(); 796 797 798 if(exitValue == 0) { 799 if(force) { 800 log("@@@ Successfully sent SIGKILL to unix process tree rooted at " + processID); 801 } else { 802 log("@@@ Successfully sent SIGTERM to unix process tree rooted at " + processID); 803 } 804 return true; 805 } else if(!Misc.isMac() && exitValue == 1) { 806 // https://stackoverflow.com/questions/28332888/return-value-of-kill 807 // "kill returns an exit code of 0 (true) if the process still existed it and was killed. 808 // kill returns an exit code of 1 (false) if the kill failed, probably because the process was no longer running." 809 // On Linux, interrupting the process and its worker threads and closing resources already successfully terminates 810 // the process and its subprocesses (don't need to call this method at all to terminate the processes: the processes 811 // aren't running when we get to this method) 812 log("@@@ Sending termination signal returned exit value 1. On linux this happens when the process has already been terminated"); 813 return true; 814 } else { 815 log("@@@ Not able to successfully terminate process, got exitvalue " + exitValue); 816 log("@@@ Got output: |" + proc.getStdOutput() + "|"); 817 log("@@@ Got err output: |" + proc.getStdError() + "|"); 818 // caller can try again with kill -KILL, by setting force parameter to true 819 return false; 820 } 821 } 822 823 public static void destroyProcess(Process p) { 824 // A cancel action results in an interruption to the process thread, which in turn interrupts 825 // the SafeProcess' worker threads, all which clean up after themselves. 826 // On linux, this suffices to cleanly terminate a Process and any subprocesses that may have launched 827 // so we don't need to do extra work in such a case. But the interrupts happen only when SafeProcess calls 828 // destroyProcess() on the Process it was running internally, and not if anyone else tries to end a 829 // Process by calling SafeProcess.destroyProcess(p). In such cases, the Process needs to be actively terminated: 830 boolean canSkipExtraWorkIfLinux = true; 831 SafeProcess.destroyProcess(p, !canSkipExtraWorkIfLinux); 832 } 833 834 // On linux, the SafeProcess code handling an Interruption suffices to successfully and cleanly terminate 835 // the process and any subprocesses launched by p as well (and not even an extra p.destroy() is needed). 836 // On Windows, and Mac too, we need to do more work, since otherwise processes launched by p remain 837 // around executing until they naturally terminate. 756 838 // e.g. full-import.pl may be terminated with p.destroy(), but it launches import.pl which is left running until it naturally terminates. 757 static void destroyProcess(Process p) {839 private static void destroyProcess(Process p, boolean canSkipExtraWorkIfLinux) { 758 840 log("### in SafeProcess.destroyProcess(Process p)"); 759 841 760 842 // If it isn't windows, process.destroy() terminates any child processes too 761 if(!Misc.isWindows()) { 843 if(Misc.isWindows()) { 844 845 if(!SafeProcess.isAvailable("wmic")) { 846 log("wmic, used to kill subprocesses, is not available. Unable to terminate subprocesses..."); 847 log("Kill them manually from the TaskManager or they will proceed to run to termination"); 848 849 // At least we can get rid of the top level process we launched 850 p.destroy(); 851 return; 852 } 853 854 // get the process id of the process we launched, 855 // so we can use it to find the pids of any subprocesses it launched in order to terminate those too. 856 857 long processID = SafeProcess.getProcessID(p); 858 if(processID == -1) { // the process doesn't exist or no longer exists (terminated naturally?) 859 p.destroy(); // minimum step, do this anyway, at worst there's no process and this won't have any effect 860 } else { 861 log("Attempting to terminate sub processes of Windows process with pid " + processID); 862 terminateSubProcessesRecursively(processID, p); 863 } 864 return; 865 866 } 867 else { // linux or mac 868 869 // if we're on linux and would have already terminated by now (in which case canSkipExtraWorkForLinux would be true), 870 // then there's nothing much left to do. This would only be the case if SafeProcess is calling this method on its 871 // internal process, since it would have successfully cleaned up on Interruption and there would be no process left running 872 if(!Misc.isMac() && canSkipExtraWorkIfLinux) { 873 log("@@@ Linux: Cancelling a SafeProcess instance does not require any complicated system destroy operation"); 874 p.destroy(); // vestigial: this will have no effect if the process had already terminated, which is the case in this block 875 return; 876 } 877 // else we're on a Mac or an external caller (not SafeProcess) has requested explicit termination on Linux 878 879 long pid = SafeProcess.getProcessID(p); 880 /* 881 // On Macs (all Unix?) can't get the child processes of a process once it's been destroyed 882 macTerminateSubProcessesRecursively(pid, p); // pid, true) 883 */ 884 885 if(pid == -1) { 886 p.destroy(); // at minimum, will have no effect if the process had already terminated 887 } else { 888 boolean forceKill = true; 889 boolean killEntireProcessTree = true; 890 891 if(!killUnixProcessWithID(pid, !forceKill, killEntireProcessTree)) { // send sig TERM (kill -15 or kill -TERM) 892 killUnixProcessWithID(pid, forceKill, killEntireProcessTree); // send sig KILL (kill -9 or kill -KILL) 893 } 894 } 895 896 return; 897 } 898 } 899 900 901 // UNUSED and INCOMPLETE 902 // But if this method is needed, then need to parse childpids printed by "pgrep -P pid" and write recursive step 903 // The childpids are probably listed one per line, see https://unix.stackexchange.com/questions/117227/why-pidof-and-pgrep-are-behaving-differently 904 private static void macTerminateSubProcessesRecursively(long parent_pid, Process p) { //boolean isTopLevelProcess) { 905 log("@@@ Attempting to terminate mac process recursively"); 906 907 // https://unix.stackexchange.com/questions/67635/elegantly-get-list-of-children-processes 908 SafeProcess proc = new SafeProcess("pgrep -P "+parent_pid); 909 int exitValue = proc.runProcess(); 910 String stdOutput = proc.getStdOutput(); 911 String stdErrOutput = proc.getStdError(); 912 913 // now we have the child processes, can terminate the parent process 914 if(p != null) { // top level process, can just be terminated the java way with p.destroy() 762 915 p.destroy(); 916 } else { 917 boolean forceKill = true; 918 boolean killSubprocesses = true; 919 // get rid of process denoted by the current pid (but not killing subprocesses it may have launched, 920 // since we'll deal with them recursively) 921 if(!SafeProcess.killUnixProcessWithID(parent_pid, !forceKill, !killSubprocesses)) { // send kill -TERM, kill -15 922 SafeProcess.killUnixProcessWithID(parent_pid, forceKill, !killSubprocesses); // send kill -9, kill -KILL 923 } 924 } 925 926 /* 927 // get rid of any process with current pid 928 if(!isTopLevelProcess && !SafeProcess.killUnixProcessWithID(parent_pid, false)) { // send kill -TERM, kill -15 929 SafeProcess.killUnixProcessWithID(parent_pid, true); // send kill -9, kill -KILL 930 } 931 */ 932 933 if(stdOutput.trim().equals("") && stdErrOutput.trim().equals("") && exitValue == 1) { 934 log("No child processes"); 935 // we're done 763 936 return; 764 }765 766 if(!SafeProcess.isAvailable("wmic")) {767 log("wmic, used to kill subprocesses, is not available. Unable to terminate subprocesses...");768 log("Kill them manually from the TaskManager or they will proceed to run to termination");769 770 // At least we can get rid of the top level process we launched771 p.destroy();772 return;773 }774 775 // get the process id of the process we launched,776 // so we can use it to find the pids of any subprocesses it launched in order to terminate those too.777 778 long processID = SafeProcess.getProcessID(p);779 if(processID == -1) { // the process doesn't exist or no longer exists (terminated naturally?)780 p.destroy(); // minimum step, do this anyway, at worst there's no process and this won't have any effect781 937 } else { 782 log("Attempting to terminate sub processes of Windows process with pid " + processID); 783 terminateSubProcessesRecursively(processID, p); 784 } 785 938 log("Got childpids on STDOUT: " + stdOutput); 939 log("Got childpids on STDERR: " + stdErrOutput); 940 } 786 941 } 787 942 … … 812 967 p.destroy(); 813 968 } else { // terminate windows way 814 SafeProcess.killWinProcessWithID(parent_pid); // get rid of f current pid969 SafeProcess.killWinProcessWithID(parent_pid); // get rid of process with current pid 815 970 } 816 971
Note:
See TracChangeset
for help on using the changeset viewer.