@@ -45,17 +45,25 @@ case class proc(command: Shellable*) {
4545 * `call` provides a number of parameters that let you configure how the subprocess
4646 * is run:
4747 *
48- * @param cwd the working directory of the subprocess
49- * @param env any additional environment variables you wish to set in the subprocess
50- * @param stdin any data you wish to pass to the subprocess's standard input
51- * @param stdout How the process's output stream is configured.
52- * @param stderr How the process's error stream is configured.
53- * @param mergeErrIntoOut merges the subprocess's stderr stream into it's stdout
54- * @param timeout how long to wait in milliseconds for the subprocess to complete
55- * @param check disable this to avoid throwing an exception if the subprocess
56- * fails with a non-zero exit code
57- * @param propagateEnv disable this to avoid passing in this parent process's
58- * environment variables to the subprocess
48+ * @param cwd the working directory of the subprocess
49+ * @param env any additional environment variables you wish to set in the subprocess
50+ * @param stdin any data you wish to pass to the subprocess's standard input
51+ * @param stdout How the process's output stream is configured.
52+ * @param stderr How the process's error stream is configured.
53+ * @param mergeErrIntoOut merges the subprocess's stderr stream into it's stdout
54+ * @param timeout how long to wait in milliseconds for the subprocess to complete
55+ * (-1 for no timeout)
56+ * @param check disable this to avoid throwing an exception if the subprocess
57+ * fails with a non-zero exit code
58+ * @param propagateEnv disable this to avoid passing in this parent process's
59+ * environment variables to the subprocess
60+ * @param timeoutGracePeriod if the timeout is enabled, how long in milliseconds for the
61+ * subprocess to gracefully terminate before attempting to
62+ * forcibly kill it
63+ * (-1 for no kill, 0 for always kill immediately)
64+ *
65+ * @note the issuing of `SIGTERM` instead of `SIGKILL` is implementation dependent on your JVM version. Pre-Java 9, no `SIGTERM` may be
66+ * issued. Check the documentation for your JDK's `Process.destroy`.
5967 */
6068 def call (
6169 cwd : Path = null ,
@@ -66,7 +74,9 @@ case class proc(command: Shellable*) {
6674 mergeErrIntoOut : Boolean = false ,
6775 timeout : Long = - 1 ,
6876 check : Boolean = true ,
69- propagateEnv : Boolean = true
77+ propagateEnv : Boolean = true ,
78+ // this cannot be next to `timeout` as this will introduce a bin-compat break (default arguments are numbered in the bytecode)
79+ timeoutGracePeriod : Long = 100
7080 ): CommandResult = {
7181
7282 val chunks = new java.util.concurrent.ConcurrentLinkedQueue [Either [geny.Bytes , geny.Bytes ]]
@@ -87,14 +97,38 @@ case class proc(command: Shellable*) {
8797 propagateEnv
8898 )
8999
90- sub.join(timeout)
100+ sub.join(timeout, timeoutGracePeriod )
91101
92102 val chunksSeq = chunks.iterator.asScala.toIndexedSeq
93103 val res = CommandResult (commandChunks, sub.exitCode(), chunksSeq)
94104 if (res.exitCode == 0 || ! check) res
95105 else throw SubprocessException (res)
96106 }
97107
108+ // forwarder for the new timeoutGracePeriod flag
109+ private [os] def call (
110+ cwd : Path ,
111+ env : Map [String , String ],
112+ stdin : ProcessInput ,
113+ stdout : ProcessOutput ,
114+ stderr : ProcessOutput ,
115+ mergeErrIntoOut : Boolean ,
116+ timeout : Long ,
117+ check : Boolean ,
118+ propagateEnv : Boolean
119+ ): CommandResult = call(
120+ cwd,
121+ env,
122+ stdin,
123+ stdout,
124+ stderr,
125+ mergeErrIntoOut,
126+ timeout,
127+ check,
128+ propagateEnv,
129+ timeoutGracePeriod = 100
130+ )
131+
98132 /**
99133 * The most flexible of the [[os.proc ]] calls, `os.proc.spawn` simply configures
100134 * and starts a subprocess, and returns it as a `java.lang.Process` for you to
@@ -181,24 +215,31 @@ case class ProcGroup private[os] (commands: Seq[proc]) {
181215 * `call` provides a number of parameters that let you configure how the pipeline
182216 * is run:
183217 *
184- * @param cwd the working directory of the pipeline
185- * @param env any additional environment variables you wish to set in the pipeline
186- * @param stdin any data you wish to pass to the pipelines's standard input (to the first process)
187- * @param stdout How the pipelines's output stream is configured (the last process stdout)
188- * @param stderr How the process's error stream is configured (set for all processes)
189- * @param mergeErrIntoOut merges the pipeline's stderr stream into it's stdout. Note that then the
190- * stderr will be forwarded with stdout to subsequent processes in the pipeline.
191- * @param timeout how long to wait in milliseconds for the pipeline to complete
192- * @param check disable this to avoid throwing an exception if the pipeline
193- * fails with a non-zero exit code
194- * @param propagateEnv disable this to avoid passing in this parent process's
195- * environment variables to the pipeline
196- * @param pipefail if true, the pipeline's exitCode will be the exit code of the first
197- * failing process. If no process fails, the exit code will be 0.
198- * @param handleBrokenPipe if true, every [[java.io.IOException ]] when redirecting output of a process
199- * will be caught and handled by killing the writing process. This behaviour
200- * is consistent with handlers of SIGPIPE signals in most programs
201- * supporting interruptable piping. Disabled by default on Windows.
218+ * @param cwd the working directory of the pipeline
219+ * @param env any additional environment variables you wish to set in the pipeline
220+ * @param stdin any data you wish to pass to the pipelines's standard input (to the first process)
221+ * @param stdout How the pipelines's output stream is configured (the last process stdout)
222+ * @param stderr How the process's error stream is configured (set for all processes)
223+ * @param mergeErrIntoOut merges the pipeline's stderr stream into it's stdout. Note that then the
224+ * stderr will be forwarded with stdout to subsequent processes in the pipeline.
225+ * @param timeout how long to wait in milliseconds for the pipeline to complete
226+ * @param check disable this to avoid throwing an exception if the pipeline
227+ * fails with a non-zero exit code
228+ * @param propagateEnv disable this to avoid passing in this parent process's
229+ * environment variables to the pipeline
230+ * @param pipefail if true, the pipeline's exitCode will be the exit code of the first
231+ * failing process. If no process fails, the exit code will be 0.
232+ * @param handleBrokenPipe if true, every [[java.io.IOException ]] when redirecting output of a process
233+ * will be caught and handled by killing the writing process. This behaviour
234+ * is consistent with handlers of SIGPIPE signals in most programs
235+ * supporting interruptable piping. Disabled by default on Windows.
236+ * @param timeoutGracePeriod if the timeout is enabled, how long in milliseconds for the
237+ * subprocess to gracefully terminate before attempting to
238+ * forcibly kill it
239+ * (-1 for no kill, 0 for always kill immediately)
240+ *
241+ * @note the issuing of `SIGTERM` instead of `SIGKILL` is implementation dependent on your JVM version. Pre-Java 9, no `SIGTERM` may be
242+ * issued. Check the documentation for your JDK's `Process.destroy`.
202243 */
203244 def call (
204245 cwd : Path = null ,
@@ -211,7 +252,9 @@ case class ProcGroup private[os] (commands: Seq[proc]) {
211252 check : Boolean = true ,
212253 propagateEnv : Boolean = true ,
213254 pipefail : Boolean = true ,
214- handleBrokenPipe : Boolean = ! isWindows
255+ handleBrokenPipe : Boolean = ! isWindows,
256+ // this cannot be next to `timeout` as this will introduce a bin-compat break (default arguments are numbered in the bytecode)
257+ timeoutGracePeriod : Long = 100
215258 ): CommandResult = {
216259 val chunks = new java.util.concurrent.ConcurrentLinkedQueue [Either [geny.Bytes , geny.Bytes ]]
217260
@@ -232,7 +275,7 @@ case class ProcGroup private[os] (commands: Seq[proc]) {
232275 pipefail
233276 )
234277
235- sub.join(timeout)
278+ sub.join(timeout, timeoutGracePeriod )
236279
237280 val chunksSeq = chunks.iterator.asScala.toIndexedSeq
238281 val res =
@@ -241,6 +284,33 @@ case class ProcGroup private[os] (commands: Seq[proc]) {
241284 else throw SubprocessException (res)
242285 }
243286
287+ private [os] def call (
288+ cwd : Path ,
289+ env : Map [String , String ],
290+ stdin : ProcessInput ,
291+ stdout : ProcessOutput ,
292+ stderr : ProcessOutput ,
293+ mergeErrIntoOut : Boolean ,
294+ timeout : Long ,
295+ check : Boolean ,
296+ propagateEnv : Boolean ,
297+ pipefail : Boolean ,
298+ handleBrokenPipe : Boolean
299+ ): CommandResult = call(
300+ cwd,
301+ env,
302+ stdin,
303+ stdout,
304+ stderr,
305+ mergeErrIntoOut,
306+ timeout,
307+ check,
308+ propagateEnv,
309+ pipefail,
310+ handleBrokenPipe,
311+ timeoutGracePeriod = 100
312+ )
313+
244314 /**
245315 * The most flexible of the [[os.ProcGroup ]] calls. It sets-up a pipeline of processes,
246316 * and returns a [[ProcessPipeline ]] for you to interact with however you like.
0 commit comments