src/Pure/System/bash.scala
author wenzelm
Sat, 07 Aug 2021 21:25:47 +0200
changeset 74141 bba35ad317ab
parent 74067 0b1462ce5fda
child 74142 0f051404f487
permissions -rw-r--r--
tuned signature;
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
62584
6cd36a0d2a28 clarified files;
wenzelm
parents: 62574
diff changeset
     1
/*  Title:      Pure/System/bash.scala
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
     2
    Author:     Makarius
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
     3
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
     4
GNU bash processes, with propagation of interrupts.
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
     5
*/
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
     6
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
     7
package isabelle
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
     8
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
     9
73897
0ddb5de0506e clarified signature: prefer Java interfaces;
wenzelm
parents: 73896
diff changeset
    10
import java.util.{LinkedList, List => JList, Map => JMap}
73895
b709faa96586 clarified signature;
wenzelm
parents: 73890
diff changeset
    11
import java.io.{BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter, File => JFile}
71706
wenzelm
parents: 71705
diff changeset
    12
import scala.annotation.tailrec
73602
37243ad3ecb6 fast approximation of test for process group (NB: initial process might already be terminated, while background processes are still running);
wenzelm
parents: 73601
diff changeset
    13
import scala.jdk.OptionConverters._
71706
wenzelm
parents: 71705
diff changeset
    14
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    15
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    16
object Bash
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    17
{
64304
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    18
  /* concrete syntax */
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    19
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    20
  private def bash_chr(c: Byte): String =
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    21
  {
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    22
    val ch = c.toChar
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    23
    ch match {
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    24
      case '\t' => "$'\\t'"
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    25
      case '\n' => "$'\\n'"
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    26
      case '\f' => "$'\\f'"
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    27
      case '\r' => "$'\\r'"
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    28
      case _ =>
67200
wenzelm
parents: 65317
diff changeset
    29
        if (Symbol.is_ascii_letter(ch) || Symbol.is_ascii_digit(ch) || "+,-./:_".contains(ch))
64304
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    30
          Symbol.ascii(ch)
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    31
        else if (c < 0) "$'\\x" + Integer.toHexString(256 + c) + "'"
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    32
        else if (c < 16) "$'\\x0" + Integer.toHexString(c) + "'"
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    33
        else if (c < 32 || c >= 127) "$'\\x" + Integer.toHexString(c) + "'"
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    34
        else  "\\" + ch
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    35
    }
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    36
  }
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    37
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    38
  def string(s: String): String =
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    39
    if (s == "") "\"\""
71601
97ccf48c2f0c misc tuning based on hints by IntelliJ IDEA;
wenzelm
parents: 67200
diff changeset
    40
    else UTF8.bytes(s).iterator.map(bash_chr).mkString
64304
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    41
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    42
  def strings(ss: List[String]): String =
71601
97ccf48c2f0c misc tuning based on hints by IntelliJ IDEA;
wenzelm
parents: 67200
diff changeset
    43
    ss.iterator.map(Bash.string).mkString(" ")
64304
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    44
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    45
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    46
  /* process and result */
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    47
72598
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
    48
  type Watchdog = (Time, Process => Boolean)
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
    49
62545
8ebffdaf2ce2 Bash.process always uses a closed script instead of an open argument list, for extra robustness on Windows, where quoting is not well-defined;
wenzelm
parents: 62543
diff changeset
    50
  def process(script: String,
62573
27f90319a499 isabelle.Build uses ML_Process directly;
wenzelm
parents: 62569
diff changeset
    51
      cwd: JFile = null,
73897
0ddb5de0506e clarified signature: prefer Java interfaces;
wenzelm
parents: 73896
diff changeset
    52
      env: JMap[String, String] = Isabelle_System.settings(),
62602
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
    53
      redirect: Boolean = false,
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
    54
      cleanup: () => Unit = () => ()): Process =
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
    55
    new Process(script, cwd, env, redirect, cleanup)
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    56
63996
3f47fec9edfc tuned whitespace;
wenzelm
parents: 63805
diff changeset
    57
  class Process private[Bash](
62602
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
    58
      script: String,
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
    59
      cwd: JFile,
73897
0ddb5de0506e clarified signature: prefer Java interfaces;
wenzelm
parents: 73896
diff changeset
    60
      env: JMap[String, String],
62602
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
    61
      redirect: Boolean,
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
    62
      cleanup: () => Unit)
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    63
  {
62604
wenzelm
parents: 62602
diff changeset
    64
    private val timing_file = Isabelle_System.tmp_file("bash_timing")
62569
5db10482f4cf bash process with builtin timing;
wenzelm
parents: 62558
diff changeset
    65
    private val timing = Synchronized[Option[Timing]](None)
65317
b9f5cd845616 more informative session result;
wenzelm
parents: 65316
diff changeset
    66
    def get_timing: Timing = timing.value getOrElse Timing.zero
62569
5db10482f4cf bash process with builtin timing;
wenzelm
parents: 62558
diff changeset
    67
73603
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
    68
    private val winpid_file: Option[JFile] =
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
    69
      if (Platform.is_windows) Some(Isabelle_System.tmp_file("bash_winpid")) else None
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
    70
    private val winpid_script =
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
    71
      winpid_file match {
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
    72
        case None => script
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
    73
        case Some(file) =>
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
    74
          "read < /proc/self/winpid > /dev/null 2> /dev/null\n" +
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
    75
          """echo -n "$REPLY" > """ + File.bash_path(file) + "\n\n" + script
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
    76
      }
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
    77
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
    78
    private val script_file: JFile = Isabelle_System.tmp_file("bash_script")
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
    79
    File.write(script_file, winpid_script)
62545
8ebffdaf2ce2 Bash.process always uses a closed script instead of an open argument list, for extra robustness on Windows, where quoting is not well-defined;
wenzelm
parents: 62543
diff changeset
    80
62296
b04a5ddd6121 clarified bash process -- similar to ML version;
wenzelm
parents: 61025
diff changeset
    81
    private val proc =
73906
f627ffab387b support for Isabelle setup in pure Java;
wenzelm
parents: 73904
diff changeset
    82
      isabelle.setup.Environment.process_builder(
73895
b709faa96586 clarified signature;
wenzelm
parents: 73890
diff changeset
    83
        JList.of(File.platform_path(Path.variable("ISABELLE_BASH_PROCESS")),
62610
4c89504c76fb more uniform signature for various process invocations;
wenzelm
parents: 62604
diff changeset
    84
          File.standard_path(timing_file), "bash", File.standard_path(script_file)),
73906
f627ffab387b support for Isabelle setup in pure Java;
wenzelm
parents: 73904
diff changeset
    85
        cwd, env, redirect).start()
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    86
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    87
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    88
    // channels
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    89
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    90
    val stdin: BufferedWriter =
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    91
      new BufferedWriter(new OutputStreamWriter(proc.getOutputStream, UTF8.charset))
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    92
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    93
    val stdout: BufferedReader =
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    94
      new BufferedReader(new InputStreamReader(proc.getInputStream, UTF8.charset))
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    95
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    96
    val stderr: BufferedReader =
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    97
      new BufferedReader(new InputStreamReader(proc.getErrorStream, UTF8.charset))
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    98
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    99
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   100
    // signals
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   101
73601
19c558ea903c clarified signature;
wenzelm
parents: 73599
diff changeset
   102
    private val group_pid = stdout.readLine
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   103
73603
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
   104
    private def process_alive(pid: String): Boolean =
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
   105
      (for {
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
   106
        p <- Value.Long.unapply(pid)
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
   107
        handle <- ProcessHandle.of(p).toScala
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
   108
      } yield handle.isAlive) getOrElse false
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
   109
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
   110
    private def root_process_alive(): Boolean =
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
   111
      winpid_file match {
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
   112
        case None => process_alive(group_pid)
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
   113
        case Some(file) =>
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
   114
          file.exists() && process_alive(Library.trim_line(File.read(file)))
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
   115
      }
73602
37243ad3ecb6 fast approximation of test for process group (NB: initial process might already be terminated, while background processes are still running);
wenzelm
parents: 73601
diff changeset
   116
73601
19c558ea903c clarified signature;
wenzelm
parents: 73599
diff changeset
   117
    @tailrec private def signal(s: String, count: Int = 1): Boolean =
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   118
    {
71706
wenzelm
parents: 71705
diff changeset
   119
      count <= 0 ||
wenzelm
parents: 71705
diff changeset
   120
      {
73911
a8c5ee444991 clarified modules and signatures;
wenzelm
parents: 73906
diff changeset
   121
        isabelle.setup.Environment.kill_process(group_pid, s)
a8c5ee444991 clarified modules and signatures;
wenzelm
parents: 73906
diff changeset
   122
        val running =
a8c5ee444991 clarified modules and signatures;
wenzelm
parents: 73906
diff changeset
   123
          root_process_alive() ||
a8c5ee444991 clarified modules and signatures;
wenzelm
parents: 73906
diff changeset
   124
          isabelle.setup.Environment.kill_process(group_pid, "0")
71707
wenzelm
parents: 71706
diff changeset
   125
        if (running) {
73702
7202e12cb324 tuned signature --- following hints by IntelliJ IDEA;
wenzelm
parents: 73604
diff changeset
   126
          Time.seconds(0.1).sleep()
73601
19c558ea903c clarified signature;
wenzelm
parents: 73599
diff changeset
   127
          signal(s, count - 1)
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   128
        }
71706
wenzelm
parents: 71705
diff changeset
   129
        else false
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   130
      }
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   131
    }
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   132
71712
c6b7f4da67b3 more robust kill: not always running on Isabelle_Thread (e.g. POSIX_Interrupt handler);
wenzelm
parents: 71708
diff changeset
   133
    def terminate(): Unit = Isabelle_Thread.try_uninterruptible
62574
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   134
    {
73601
19c558ea903c clarified signature;
wenzelm
parents: 73599
diff changeset
   135
      signal("INT", count = 7) && signal("TERM", count = 3) && signal("KILL")
73367
77ef8bef0593 clarified signature --- fewer warnings;
wenzelm
parents: 73340
diff changeset
   136
      proc.destroy()
62602
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
   137
      do_cleanup()
62574
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   138
    }
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   139
71712
c6b7f4da67b3 more robust kill: not always running on Isabelle_Thread (e.g. POSIX_Interrupt handler);
wenzelm
parents: 71708
diff changeset
   140
    def interrupt(): Unit = Isabelle_Thread.try_uninterruptible
71705
7b75d52a1bf1 clarified interrupt handling;
wenzelm
parents: 71684
diff changeset
   141
    {
73911
a8c5ee444991 clarified modules and signatures;
wenzelm
parents: 73906
diff changeset
   142
      isabelle.setup.Environment.kill_process(group_pid, "INT")
71705
7b75d52a1bf1 clarified interrupt handling;
wenzelm
parents: 71684
diff changeset
   143
    }
7b75d52a1bf1 clarified interrupt handling;
wenzelm
parents: 71684
diff changeset
   144
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   145
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   146
    // JVM shutdown hook
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   147
71712
c6b7f4da67b3 more robust kill: not always running on Isabelle_Thread (e.g. POSIX_Interrupt handler);
wenzelm
parents: 71708
diff changeset
   148
    private val shutdown_hook = Isabelle_Thread.create(() => terminate())
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   149
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   150
    try { Runtime.getRuntime.addShutdownHook(shutdown_hook) }
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   151
    catch { case _: IllegalStateException => }
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   152
62545
8ebffdaf2ce2 Bash.process always uses a closed script instead of an open argument list, for extra robustness on Windows, where quoting is not well-defined;
wenzelm
parents: 62543
diff changeset
   153
62574
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   154
    // cleanup
62545
8ebffdaf2ce2 Bash.process always uses a closed script instead of an open argument list, for extra robustness on Windows, where quoting is not well-defined;
wenzelm
parents: 62543
diff changeset
   155
73340
0ffcad1f6130 tuned --- fewer warnings;
wenzelm
parents: 73333
diff changeset
   156
    private def do_cleanup(): Unit =
62545
8ebffdaf2ce2 Bash.process always uses a closed script instead of an open argument list, for extra robustness on Windows, where quoting is not well-defined;
wenzelm
parents: 62543
diff changeset
   157
    {
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   158
      try { Runtime.getRuntime.removeShutdownHook(shutdown_hook) }
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   159
      catch { case _: IllegalStateException => }
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   160
73603
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
   161
      script_file.delete()
342362c9496c clarified check of root process on Windows (NB: the winpid is less stable than the Cygwin/Posix pid, so it needs to be "patched" into the the bash script, instead of bash_process.c);
wenzelm
parents: 73602
diff changeset
   162
      winpid_file.foreach(_.delete())
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   163
62569
5db10482f4cf bash process with builtin timing;
wenzelm
parents: 62558
diff changeset
   164
      timing.change {
5db10482f4cf bash process with builtin timing;
wenzelm
parents: 62558
diff changeset
   165
        case None =>
62574
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   166
          if (timing_file.isFile) {
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   167
            val t =
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   168
              Word.explode(File.read(timing_file)) match {
63805
c272680df665 clarified modules;
wenzelm
parents: 62610
diff changeset
   169
                case List(Value.Long(elapsed), Value.Long(cpu)) =>
62574
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   170
                  Timing(Time.ms(elapsed), Time.ms(cpu), Time.zero)
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   171
                case _ => Timing.zero
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   172
              }
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   173
            timing_file.delete
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   174
            Some(t)
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   175
          }
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   176
          else Some(Timing.zero)
62569
5db10482f4cf bash process with builtin timing;
wenzelm
parents: 62558
diff changeset
   177
        case some => some
5db10482f4cf bash process with builtin timing;
wenzelm
parents: 62558
diff changeset
   178
      }
62602
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
   179
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
   180
      cleanup()
62574
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   181
    }
62569
5db10482f4cf bash process with builtin timing;
wenzelm
parents: 62558
diff changeset
   182
62574
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   183
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   184
    // join
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   185
74141
bba35ad317ab tuned signature;
wenzelm
parents: 74067
diff changeset
   186
    def join(): Int =
62574
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   187
    {
74141
bba35ad317ab tuned signature;
wenzelm
parents: 74067
diff changeset
   188
      val rc = proc.waitFor()
62602
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
   189
      do_cleanup()
62545
8ebffdaf2ce2 Bash.process always uses a closed script instead of an open argument list, for extra robustness on Windows, where quoting is not well-defined;
wenzelm
parents: 62543
diff changeset
   190
      rc
8ebffdaf2ce2 Bash.process always uses a closed script instead of an open argument list, for extra robustness on Windows, where quoting is not well-defined;
wenzelm
parents: 62543
diff changeset
   191
    }
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   192
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   193
62574
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   194
    // result
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   195
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   196
    def result(
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   197
      progress_stdout: String => Unit = (_: String) => (),
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   198
      progress_stderr: String => Unit = (_: String) => (),
72598
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   199
      watchdog: Option[Watchdog] = None,
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   200
      strict: Boolean = true): Process_Result =
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   201
    {
73367
77ef8bef0593 clarified signature --- fewer warnings;
wenzelm
parents: 73340
diff changeset
   202
      stdin.close()
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   203
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   204
      val out_lines =
72105
a1fb4d28e609 unused --- superseded by PIDE messages;
wenzelm
parents: 71712
diff changeset
   205
        Future.thread("bash_stdout") { File.read_lines(stdout, progress_stdout) }
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   206
      val err_lines =
72105
a1fb4d28e609 unused --- superseded by PIDE messages;
wenzelm
parents: 71712
diff changeset
   207
        Future.thread("bash_stderr") { File.read_lines(stderr, progress_stderr) }
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   208
72598
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   209
      val watchdog_thread =
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   210
        for ((time, check) <- watchdog)
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   211
        yield {
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   212
          Future.thread("bash_watchdog") {
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   213
            while (proc.isAlive) {
73702
7202e12cb324 tuned signature --- following hints by IntelliJ IDEA;
wenzelm
parents: 73604
diff changeset
   214
              time.sleep()
72598
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   215
              if (check(this)) interrupt()
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   216
            }
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   217
          }
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   218
        }
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   219
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   220
      val rc =
74141
bba35ad317ab tuned signature;
wenzelm
parents: 74067
diff changeset
   221
        try { join() }
74067
0b1462ce5fda clarified signature;
wenzelm
parents: 73911
diff changeset
   222
        catch { case Exn.Interrupt() => terminate(); Process_Result.interrupt_rc }
72598
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   223
73367
77ef8bef0593 clarified signature --- fewer warnings;
wenzelm
parents: 73340
diff changeset
   224
      watchdog_thread.foreach(_.cancel())
72598
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   225
74067
0b1462ce5fda clarified signature;
wenzelm
parents: 73911
diff changeset
   226
      if (strict && rc == Process_Result.interrupt_rc) throw Exn.Interrupt()
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   227
73134
8a8ffe78eee7 clarified return code: re-use SIGALRM for soft timeout;
wenzelm
parents: 72598
diff changeset
   228
      Process_Result(rc, out_lines.join, err_lines.join, get_timing)
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   229
    }
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   230
  }
73228
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   231
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   232
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   233
  /* Scala function */
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   234
73565
1aa92bc4d356 clarified signature for Scala functions;
wenzelm
parents: 73419
diff changeset
   235
  object Process extends Scala.Fun_Strings("bash_process", thread = true)
73228
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   236
  {
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   237
    val here = Scala_Project.here
73565
1aa92bc4d356 clarified signature for Scala functions;
wenzelm
parents: 73419
diff changeset
   238
    def apply(args: List[String]): List[String] =
73228
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   239
    {
73604
51b291ae3e2d avoid "exec" to change the winpid;
wenzelm
parents: 73603
diff changeset
   240
      val result =
51b291ae3e2d avoid "exec" to change the winpid;
wenzelm
parents: 73603
diff changeset
   241
        Exn.capture {
51b291ae3e2d avoid "exec" to change the winpid;
wenzelm
parents: 73603
diff changeset
   242
          val redirect = args.head == "true"
51b291ae3e2d avoid "exec" to change the winpid;
wenzelm
parents: 73603
diff changeset
   243
          val script = cat_lines(args.tail)
51b291ae3e2d avoid "exec" to change the winpid;
wenzelm
parents: 73603
diff changeset
   244
          Isabelle_System.bash(script, redirect = redirect)
51b291ae3e2d avoid "exec" to change the winpid;
wenzelm
parents: 73603
diff changeset
   245
        }
73228
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   246
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   247
      val is_interrupt =
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   248
        result match {
74067
0b1462ce5fda clarified signature;
wenzelm
parents: 73911
diff changeset
   249
          case Exn.Res(res) => res.rc == Process_Result.interrupt_rc
73228
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   250
          case Exn.Exn(exn) => Exn.is_interrupt(exn)
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   251
        }
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   252
73268
c8abfe393c16 more accurate process_result in ML, corresponding to Process_Result in Scala;
wenzelm
parents: 73265
diff changeset
   253
      result match {
73565
1aa92bc4d356 clarified signature for Scala functions;
wenzelm
parents: 73419
diff changeset
   254
        case _ if is_interrupt => Nil
1aa92bc4d356 clarified signature for Scala functions;
wenzelm
parents: 73419
diff changeset
   255
        case Exn.Exn(exn) => List(Exn.message(exn))
73268
c8abfe393c16 more accurate process_result in ML, corresponding to Process_Result in Scala;
wenzelm
parents: 73265
diff changeset
   256
        case Exn.Res(res) =>
73565
1aa92bc4d356 clarified signature for Scala functions;
wenzelm
parents: 73419
diff changeset
   257
          res.rc.toString ::
1aa92bc4d356 clarified signature for Scala functions;
wenzelm
parents: 73419
diff changeset
   258
          res.timing.elapsed.ms.toString ::
1aa92bc4d356 clarified signature for Scala functions;
wenzelm
parents: 73419
diff changeset
   259
          res.timing.cpu.ms.toString ::
1aa92bc4d356 clarified signature for Scala functions;
wenzelm
parents: 73419
diff changeset
   260
          res.out_lines.length.toString ::
1aa92bc4d356 clarified signature for Scala functions;
wenzelm
parents: 73419
diff changeset
   261
          res.out_lines ::: res.err_lines
73228
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   262
      }
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   263
    }
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   264
  }
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   265
}