src/Pure/System/bash.scala
author wenzelm
Wed, 09 Apr 2025 17:40:27 +0200
changeset 82465 3cc075052033
parent 80480 972f7a4cdc0e
child 82466 d5ef492dd673
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
74158
wenzelm
parents: 74147
diff changeset
     4
Support for GNU bash: portable external processes with propagation of
wenzelm
parents: 74147
diff changeset
     5
interrupts.
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
     6
*/
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
     7
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
     8
package isabelle
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
     9
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    10
80218
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    11
import java.util.{List => JList, Map => JMap, LinkedList}
74273
0a4cdf0036a0 more robust: client could have terminated already;
wenzelm
parents: 74212
diff changeset
    12
import java.io.{BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter,
0a4cdf0036a0 more robust: client could have terminated already;
wenzelm
parents: 74212
diff changeset
    13
  File => JFile, IOException}
71706
wenzelm
parents: 71705
diff changeset
    14
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
    15
import scala.jdk.OptionConverters._
71706
wenzelm
parents: 71705
diff changeset
    16
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
    17
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
    18
object Bash {
64304
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    19
  /* concrete syntax */
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    20
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
    21
  private def bash_chr(c: Byte): String = {
64304
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 =
80354
wenzelm
parents: 80271
diff changeset
    39
    if (s.isEmpty) "\"\""
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
78910
d305af09fbad tuned signature;
wenzelm
parents: 76146
diff changeset
    42
  def strings(ss: Iterable[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
82465
3cc075052033 tuned signature;
wenzelm
parents: 80480
diff changeset
    45
  def exports(environment: String*): String =
3cc075052033 tuned signature;
wenzelm
parents: 80480
diff changeset
    46
    environment.iterator.map(a => "export " + string(a)).mkString("", "\n", "\n")
3cc075052033 tuned signature;
wenzelm
parents: 80480
diff changeset
    47
64304
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    48
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    49
  /* process and result */
96bc94c87a81 clarified modules;
wenzelm
parents: 63996
diff changeset
    50
80227
af6b60c75d7d clarified context for (remote) bash scripts: export variables are optional, support cwd;
wenzelm
parents: 80224
diff changeset
    51
  def context(script: String,
af6b60c75d7d clarified context for (remote) bash scripts: export variables are optional, support cwd;
wenzelm
parents: 80224
diff changeset
    52
    user_home: String = "",
af6b60c75d7d clarified context for (remote) bash scripts: export variables are optional, support cwd;
wenzelm
parents: 80224
diff changeset
    53
    isabelle_identifier: String = "",
af6b60c75d7d clarified context for (remote) bash scripts: export variables are optional, support cwd;
wenzelm
parents: 80224
diff changeset
    54
    cwd: Path = Path.current
af6b60c75d7d clarified context for (remote) bash scripts: export variables are optional, support cwd;
wenzelm
parents: 80224
diff changeset
    55
  ): String = {
af6b60c75d7d clarified context for (remote) bash scripts: export variables are optional, support cwd;
wenzelm
parents: 80224
diff changeset
    56
    if_proper(user_home,
af6b60c75d7d clarified context for (remote) bash scripts: export variables are optional, support cwd;
wenzelm
parents: 80224
diff changeset
    57
      "export USER_HOME=" + Bash.string(user_home) + "\n") +
af6b60c75d7d clarified context for (remote) bash scripts: export variables are optional, support cwd;
wenzelm
parents: 80224
diff changeset
    58
    if_proper(isabelle_identifier,
af6b60c75d7d clarified context for (remote) bash scripts: export variables are optional, support cwd;
wenzelm
parents: 80224
diff changeset
    59
      "export ISABELLE_IDENTIFIER=" + Bash.string(isabelle_identifier) + "\n") +
af6b60c75d7d clarified context for (remote) bash scripts: export variables are optional, support cwd;
wenzelm
parents: 80224
diff changeset
    60
    (if (cwd == null || cwd.is_current) "" else "cd " + quote(cwd.implode) + "\n") +
af6b60c75d7d clarified context for (remote) bash scripts: export variables are optional, support cwd;
wenzelm
parents: 80224
diff changeset
    61
    script
af6b60c75d7d clarified context for (remote) bash scripts: export variables are optional, support cwd;
wenzelm
parents: 80224
diff changeset
    62
  }
af6b60c75d7d clarified context for (remote) bash scripts: export variables are optional, support cwd;
wenzelm
parents: 80224
diff changeset
    63
74212
a1ccecae6a57 clarified process description;
wenzelm
parents: 74158
diff changeset
    64
  private def make_description(description: String): String =
a1ccecae6a57 clarified process description;
wenzelm
parents: 74158
diff changeset
    65
    proper_string(description) getOrElse "bash_process"
a1ccecae6a57 clarified process description;
wenzelm
parents: 74158
diff changeset
    66
80218
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    67
  def local_bash_process(): String =
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    68
    File.platform_path(Path.variable("ISABELLE_BASH_PROCESS"))
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    69
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    70
  def local_bash(): String =
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    71
    if (Platform.is_unix) "bash"
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    72
    else isabelle.setup.Environment.cygwin_root() + "\\bin\\bash.exe"
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    73
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    74
  def remote_bash_process(ssh: SSH.Session): String = {
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    75
    val component = Components.provide(Component_Bash_Process.home, ssh = ssh)
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    76
    val exe = Component_Bash_Process.remote_program(component)
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    77
    ssh.make_command(args_host = true, args = ssh.bash_path(exe))
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    78
  }
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    79
80235
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
    80
  object Watchdog {
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
    81
    val none: Watchdog = new Watchdog(Time.zero, _ => false) {
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
    82
      override def toString: String = "Bash.Watchdog.none"
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
    83
    }
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
    84
    def apply(time: Time, check: Process => Boolean = _ => true): Watchdog =
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
    85
      if (time.is_zero) none else new Watchdog(time, check)
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
    86
  }
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
    87
  class Watchdog private(val time: Time, val check: Process => Boolean) {
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
    88
    override def toString: String = "Bash.Watchdog(" + time + ")"
80237
305d2f4a395f more robust: avoid crash of sleep() for negative time;
wenzelm
parents: 80235
diff changeset
    89
    def defined: Boolean = time > Time.zero
80235
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
    90
  }
72598
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
    91
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
    92
  def process(script: String,
74212
a1ccecae6a57 clarified process description;
wenzelm
parents: 74158
diff changeset
    93
      description: String = "",
80218
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    94
      ssh: SSH.System = SSH.Local,
80224
db92e0b6a11a clarified signature: prefer symbolic isabelle.Path over physical java.io.File;
wenzelm
parents: 80221
diff changeset
    95
      cwd: Path = Path.current,
80229
5e32da8238e1 clarified comments;
wenzelm
parents: 80228
diff changeset
    96
      env: JMap[String, String] = Isabelle_System.settings(),  // ignored for remote ssh
62602
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
    97
      redirect: Boolean = false,
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
    98
      cleanup: () => Unit = () => ()): Process =
80218
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
    99
    new Process(script, description, ssh, cwd, env, redirect, cleanup)
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   100
63996
3f47fec9edfc tuned whitespace;
wenzelm
parents: 63805
diff changeset
   101
  class Process private[Bash](
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   102
    script: String,
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   103
    description: String,
80218
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   104
    ssh: SSH.System,
80224
db92e0b6a11a clarified signature: prefer symbolic isabelle.Path over physical java.io.File;
wenzelm
parents: 80221
diff changeset
   105
    cwd: Path,
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   106
    env: JMap[String, String],
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   107
    redirect: Boolean,
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   108
    cleanup: () => Unit
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   109
  ) {
74212
a1ccecae6a57 clarified process description;
wenzelm
parents: 74158
diff changeset
   110
    override def toString: String = make_description(description)
a1ccecae6a57 clarified process description;
wenzelm
parents: 74158
diff changeset
   111
80218
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   112
    private val proc_command: JList[String] = new LinkedList[String]
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   113
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   114
    private val winpid_file: Option[JFile] =
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   115
      if (ssh.is_local && Platform.is_windows) Some(Isabelle_System.tmp_file("bash_winpid"))
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   116
      else None
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   117
    private val winpid_script =
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   118
      winpid_file match {
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   119
        case None => ""
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   120
        case Some(file) =>
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   121
          "read < /proc/self/winpid > /dev/null 2> /dev/null\n" +
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   122
            """echo -n "$REPLY" > """ + File.bash_path(file) + "\n\n"
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   123
      }
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   124
80220
928e02d0cab7 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80218
diff changeset
   125
    private val List(timing_file, script_file) =
928e02d0cab7 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80218
diff changeset
   126
      ssh.tmp_files(List("bash_timing", "bash_script"))
928e02d0cab7 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80218
diff changeset
   127
62569
5db10482f4cf bash process with builtin timing;
wenzelm
parents: 62558
diff changeset
   128
    private val timing = Synchronized[Option[Timing]](None)
65317
b9f5cd845616 more informative session result;
wenzelm
parents: 65316
diff changeset
   129
    def get_timing: Timing = timing.value getOrElse Timing.zero
62569
5db10482f4cf bash process with builtin timing;
wenzelm
parents: 62558
diff changeset
   130
80228
df84e8ff4839 proper support for remote cwd;
wenzelm
parents: 80227
diff changeset
   131
    ssh.write(script_file,
df84e8ff4839 proper support for remote cwd;
wenzelm
parents: 80227
diff changeset
   132
      if (ssh.is_local) winpid_script + script
df84e8ff4839 proper support for remote cwd;
wenzelm
parents: 80227
diff changeset
   133
      else Bash.context(script, cwd = cwd))
80218
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   134
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   135
    private val ssh_file: Option[JFile] =
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   136
      ssh.ssh_session match {
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   137
        case None =>
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   138
          proc_command.add(local_bash_process())
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   139
          proc_command.add(File.platform_path(timing_file))
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   140
          proc_command.add("bash")
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   141
          proc_command.add(File.platform_path(script_file))
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   142
          None
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   143
        case Some(ssh_session) =>
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   144
          val ssh_script =
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   145
            "exec " + remote_bash_process(ssh_session) + " " +
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   146
              ssh.bash_path(timing_file) + " bash " +
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   147
              ssh.bash_path(script_file)
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   148
          val file = Isabelle_System.tmp_file("bash_ssh")
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   149
          File.write(file, ssh_script)
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   150
          proc_command.add(local_bash())
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   151
          proc_command.add(file.getPath)
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   152
          Some(file)
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
   153
      }
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
   154
62296
b04a5ddd6121 clarified bash process -- similar to ML version;
wenzelm
parents: 61025
diff changeset
   155
    private val proc =
80224
db92e0b6a11a clarified signature: prefer symbolic isabelle.Path over physical java.io.File;
wenzelm
parents: 80221
diff changeset
   156
      isabelle.setup.Environment.process_builder(
db92e0b6a11a clarified signature: prefer symbolic isabelle.Path over physical java.io.File;
wenzelm
parents: 80221
diff changeset
   157
        proc_command,
80228
df84e8ff4839 proper support for remote cwd;
wenzelm
parents: 80227
diff changeset
   158
        if (!ssh.is_local || cwd == null || cwd.is_current) null else cwd.file,
80224
db92e0b6a11a clarified signature: prefer symbolic isabelle.Path over physical java.io.File;
wenzelm
parents: 80221
diff changeset
   159
        env,
db92e0b6a11a clarified signature: prefer symbolic isabelle.Path over physical java.io.File;
wenzelm
parents: 80221
diff changeset
   160
        redirect
db92e0b6a11a clarified signature: prefer symbolic isabelle.Path over physical java.io.File;
wenzelm
parents: 80221
diff changeset
   161
      ).start()
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   162
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   163
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   164
    // channels
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   165
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   166
    val stdin: BufferedWriter =
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   167
      new BufferedWriter(new OutputStreamWriter(proc.getOutputStream, UTF8.charset))
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   168
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   169
    val stdout: BufferedReader =
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   170
      new BufferedReader(new InputStreamReader(proc.getInputStream, UTF8.charset))
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   171
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   172
    val stderr: BufferedReader =
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   173
      new BufferedReader(new InputStreamReader(proc.getErrorStream, UTF8.charset))
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   174
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   175
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   176
    // signals
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   177
73601
19c558ea903c clarified signature;
wenzelm
parents: 73599
diff changeset
   178
    private val group_pid = stdout.readLine
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   179
80218
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   180
    private def local_process_alive(pid: String): Boolean =
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   181
      ssh.is_local &&
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   182
        (for {
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   183
          p <- Value.Long.unapply(pid)
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   184
          handle <- ProcessHandle.of(p).toScala
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   185
        } yield handle.isAlive).getOrElse(false)
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
   186
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
   187
    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
   188
      winpid_file match {
80218
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   189
        case None => local_process_alive(group_pid)
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
   190
        case Some(file) =>
80218
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   191
          file.exists() && local_process_alive(Library.trim_line(File.read(file)))
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
   192
      }
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
   193
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   194
    @tailrec private def signal(s: String, count: Int = 1): Boolean = {
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   195
      count <= 0 || {
80218
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   196
        ssh.kill_process(group_pid, s)
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   197
        val running = root_process_alive() || ssh.kill_process(group_pid, "0")
71707
wenzelm
parents: 71706
diff changeset
   198
        if (running) {
80218
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   199
          Time.seconds(if (ssh.is_local) 0.1 else 0.25).sleep()
73601
19c558ea903c clarified signature;
wenzelm
parents: 73599
diff changeset
   200
          signal(s, count - 1)
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   201
        }
71706
wenzelm
parents: 71705
diff changeset
   202
        else false
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   203
      }
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   204
    }
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   205
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   206
    def terminate(): Unit = Isabelle_Thread.try_uninterruptible {
73601
19c558ea903c clarified signature;
wenzelm
parents: 73599
diff changeset
   207
      signal("INT", count = 7) && signal("TERM", count = 3) && signal("KILL")
73367
77ef8bef0593 clarified signature --- fewer warnings;
wenzelm
parents: 73340
diff changeset
   208
      proc.destroy()
62602
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
   209
      do_cleanup()
62574
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   210
    }
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   211
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   212
    def interrupt(): Unit = Isabelle_Thread.try_uninterruptible {
80218
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   213
      ssh.kill_process(group_pid, "INT")
71705
7b75d52a1bf1 clarified interrupt handling;
wenzelm
parents: 71684
diff changeset
   214
    }
7b75d52a1bf1 clarified interrupt handling;
wenzelm
parents: 71684
diff changeset
   215
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   216
62574
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   217
    // 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
   218
76146
a64f3496d93a clarified signature;
wenzelm
parents: 75393
diff changeset
   219
    private val shutdown_hook =
a64f3496d93a clarified signature;
wenzelm
parents: 75393
diff changeset
   220
      Isabelle_System.create_shutdown_hook { terminate() }
a64f3496d93a clarified signature;
wenzelm
parents: 75393
diff changeset
   221
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   222
    private def do_cleanup(): Unit = {
76146
a64f3496d93a clarified signature;
wenzelm
parents: 75393
diff changeset
   223
      Isabelle_System.remove_shutdown_hook(shutdown_hook)
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   224
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
   225
      winpid_file.foreach(_.delete())
80218
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   226
      ssh_file.foreach(_.delete())
875968a3b2f9 suport Isabelle_System.bash via SSH.System;
wenzelm
parents: 78910
diff changeset
   227
62569
5db10482f4cf bash process with builtin timing;
wenzelm
parents: 62558
diff changeset
   228
      timing.change {
5db10482f4cf bash process with builtin timing;
wenzelm
parents: 62558
diff changeset
   229
        case None =>
80221
0d89f0a39854 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80220
diff changeset
   230
          val timing_text =
0d89f0a39854 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80220
diff changeset
   231
            try { ssh.read(timing_file) }
0d89f0a39854 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80220
diff changeset
   232
            catch { case ERROR(_) => "" }
0d89f0a39854 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80220
diff changeset
   233
          val t =
0d89f0a39854 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80220
diff changeset
   234
            Word.explode(timing_text) match {
0d89f0a39854 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80220
diff changeset
   235
              case List(Value.Long(elapsed), Value.Long(cpu)) =>
0d89f0a39854 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80220
diff changeset
   236
                Timing(Time.ms(elapsed), Time.ms(cpu), Time.zero)
0d89f0a39854 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80220
diff changeset
   237
              case _ => Timing.zero
0d89f0a39854 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80220
diff changeset
   238
            }
0d89f0a39854 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80220
diff changeset
   239
          Some(t)
62569
5db10482f4cf bash process with builtin timing;
wenzelm
parents: 62558
diff changeset
   240
        case some => some
5db10482f4cf bash process with builtin timing;
wenzelm
parents: 62558
diff changeset
   241
      }
62602
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
   242
80221
0d89f0a39854 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80220
diff changeset
   243
      ssh.delete(timing_file, script_file)
0d89f0a39854 minor performance tuning: save approx. 70ms per SSH command;
wenzelm
parents: 80220
diff changeset
   244
62602
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
   245
      cleanup()
62574
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   246
    }
62569
5db10482f4cf bash process with builtin timing;
wenzelm
parents: 62558
diff changeset
   247
62574
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   248
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   249
    // join
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   250
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   251
    def join(): Int = {
74141
bba35ad317ab tuned signature;
wenzelm
parents: 74067
diff changeset
   252
      val rc = proc.waitFor()
62602
96e679f042ec more thorough cleanup -- in Scala;
wenzelm
parents: 62584
diff changeset
   253
      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
   254
      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
   255
    }
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   256
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   257
62574
ec382bc689e5 more robust cleanup;
wenzelm
parents: 62573
diff changeset
   258
    // result
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   259
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   260
    def result(
74142
0f051404f487 clarified signature: more options for bash_process;
wenzelm
parents: 74141
diff changeset
   261
      input: String = "",
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   262
      progress_stdout: String => Unit = (_: String) => (),
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   263
      progress_stderr: String => Unit = (_: String) => (),
80235
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
   264
      watchdog: Watchdog = Watchdog.none,
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   265
      strict: Boolean = true
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   266
    ): Process_Result = {
74142
0f051404f487 clarified signature: more options for bash_process;
wenzelm
parents: 74141
diff changeset
   267
      val in =
0f051404f487 clarified signature: more options for bash_process;
wenzelm
parents: 74141
diff changeset
   268
        if (input.isEmpty) Future.value(stdin.close())
0f051404f487 clarified signature: more options for bash_process;
wenzelm
parents: 74141
diff changeset
   269
        else Future.thread("bash_stdin") { stdin.write(input); stdin.flush(); stdin.close(); }
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   270
      val out_lines =
72105
a1fb4d28e609 unused --- superseded by PIDE messages;
wenzelm
parents: 71712
diff changeset
   271
        Future.thread("bash_stdout") { File.read_lines(stdout, progress_stdout) }
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   272
      val err_lines =
72105
a1fb4d28e609 unused --- superseded by PIDE messages;
wenzelm
parents: 71712
diff changeset
   273
        Future.thread("bash_stderr") { File.read_lines(stderr, progress_stderr) }
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   274
72598
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   275
      val watchdog_thread =
80235
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
   276
        if (watchdog.defined) {
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
   277
          Some(
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
   278
            Future.thread("bash_watchdog") {
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
   279
              while (proc.isAlive) {
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
   280
                watchdog.time.sleep()
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
   281
                if (watchdog.check(this)) interrupt()
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
   282
              }
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
   283
            })
72598
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   284
        }
80235
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
   285
        else None
72598
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   286
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   287
      val rc =
74141
bba35ad317ab tuned signature;
wenzelm
parents: 74067
diff changeset
   288
        try { join() }
74306
a117c076aa22 clarified signature;
wenzelm
parents: 74273
diff changeset
   289
        catch { case Exn.Interrupt() => terminate(); Process_Result.RC.interrupt }
72598
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   290
73367
77ef8bef0593 clarified signature --- fewer warnings;
wenzelm
parents: 73340
diff changeset
   291
      watchdog_thread.foreach(_.cancel())
72598
d9f2be66ebad support for watchdog thread;
wenzelm
parents: 72105
diff changeset
   292
74142
0f051404f487 clarified signature: more options for bash_process;
wenzelm
parents: 74141
diff changeset
   293
      in.join
0f051404f487 clarified signature: more options for bash_process;
wenzelm
parents: 74141
diff changeset
   294
      out_lines.join
0f051404f487 clarified signature: more options for bash_process;
wenzelm
parents: 74141
diff changeset
   295
      err_lines.join
0f051404f487 clarified signature: more options for bash_process;
wenzelm
parents: 74141
diff changeset
   296
74306
a117c076aa22 clarified signature;
wenzelm
parents: 74273
diff changeset
   297
      if (strict && rc == Process_Result.RC.interrupt) throw Exn.Interrupt()
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   298
73134
8a8ffe78eee7 clarified return code: re-use SIGALRM for soft timeout;
wenzelm
parents: 72598
diff changeset
   299
      Process_Result(rc, out_lines.join, err_lines.join, get_timing)
62543
57f379ef662f clarified modules;
wenzelm
parents: 62400
diff changeset
   300
    }
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   301
  }
73228
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   302
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   303
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   304
  /* server */
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   305
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   306
  object Server {
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   307
    // input messages
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   308
    private val RUN = "run"
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   309
    private val KILL = "kill"
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   310
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   311
    // output messages
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   312
    private val UUID = "uuid"
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   313
    private val INTERRUPT = "interrupt"
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   314
    private val FAILURE = "failure"
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   315
    private val RESULT = "result"
73228
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   316
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   317
    def start(port: Int = 0, debugging: => Boolean = false): Server = {
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   318
      val server = new Server(port, debugging)
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   319
      server.start()
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   320
      server
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   321
    }
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   322
  }
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   323
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   324
  class Server private(port: Int, debugging: => Boolean)
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   325
  extends isabelle.Server.Handler(port) {
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   326
    server =>
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   327
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   328
    private val _processes = Synchronized(Map.empty[UUID.T, Bash.Process])
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   329
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   330
    override def stop(): Unit = {
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   331
      for ((_, process) <- _processes.value) process.terminate()
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   332
      super.stop()
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   333
    }
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   334
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   335
    override def handle(connection: isabelle.Server.Connection): Unit = {
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   336
      def reply(chunks: List[String]): Unit =
74273
0a4cdf0036a0 more robust: client could have terminated already;
wenzelm
parents: 74212
diff changeset
   337
        try { connection.write_byte_message(chunks.map(Bytes.apply)) }
0a4cdf0036a0 more robust: client could have terminated already;
wenzelm
parents: 74212
diff changeset
   338
        catch { case _: IOException => }
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   339
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   340
      def reply_failure(exn: Throwable): Unit =
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   341
        reply(
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   342
          if (Exn.is_interrupt(exn)) List(Server.INTERRUPT)
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   343
          else List(Server.FAILURE, Exn.message(exn)))
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   344
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   345
      def reply_result(result: Process_Result): Unit =
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   346
        reply(
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   347
          Server.RESULT ::
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   348
          result.rc.toString ::
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   349
          result.timing.elapsed.ms.toString ::
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   350
          result.timing.cpu.ms.toString ::
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   351
          result.out_lines.length.toString ::
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   352
          result.out_lines :::
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   353
          result.err_lines)
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   354
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   355
      connection.read_byte_message().map(_.map(_.text)) match {
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   356
        case None =>
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   357
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   358
        case Some(List(Server.KILL, UUID(uuid))) =>
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   359
          if (debugging) Output.writeln("kill " + uuid)
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   360
          _processes.value.get(uuid).foreach(_.terminate())
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   361
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   362
        case Some(List(Server.RUN, script, input, cwd, putenv,
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   363
            Value.Boolean(redirect), Value.Seconds(timeout), description)) =>
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   364
          val uuid = UUID.random()
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   365
74212
a1ccecae6a57 clarified process description;
wenzelm
parents: 74158
diff changeset
   366
          val descr = make_description(description)
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   367
          if (debugging) {
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   368
            Output.writeln(
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   369
              "start " + quote(descr) + " (uuid=" + uuid + ", timeout=" + timeout.seconds + ")")
74142
0f051404f487 clarified signature: more options for bash_process;
wenzelm
parents: 74141
diff changeset
   370
          }
73228
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   371
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   372
          Exn.capture {
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   373
            Bash.process(script,
74212
a1ccecae6a57 clarified process description;
wenzelm
parents: 74158
diff changeset
   374
              description = description,
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   375
              cwd =
80480
972f7a4cdc0e clarified YXML.Source: more direct support for String and Bytes, instead of CharSequence;
wenzelm
parents: 80354
diff changeset
   376
                XML.Decode.option(XML.Decode.string)(YXML.parse_body(YXML.Source(cwd))) match {
80224
db92e0b6a11a clarified signature: prefer symbolic isabelle.Path over physical java.io.File;
wenzelm
parents: 80221
diff changeset
   377
                  case None => Path.current
db92e0b6a11a clarified signature: prefer symbolic isabelle.Path over physical java.io.File;
wenzelm
parents: 80221
diff changeset
   378
                  case Some(s) => Path.explode(s)
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   379
                },
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   380
              env =
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   381
                Isabelle_System.settings(
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   382
                  XML.Decode.list(XML.Decode.pair(XML.Decode.string, XML.Decode.string))(
80480
972f7a4cdc0e clarified YXML.Source: more direct support for String and Bytes, instead of CharSequence;
wenzelm
parents: 80354
diff changeset
   383
                    YXML.parse_body(YXML.Source(putenv)))),
80271
198fc882ec0f tuned whitespace;
wenzelm
parents: 80237
diff changeset
   384
              redirect = redirect)
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   385
          }
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   386
          match {
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   387
            case Exn.Exn(exn) => reply_failure(exn)
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   388
            case Exn.Res(process) =>
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   389
              _processes.change(processes => processes + (uuid -> process))
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   390
              reply(List(Server.UUID, uuid.toString))
73228
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   391
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   392
              Isabelle_Thread.fork(name = "bash_process") {
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   393
                @volatile var is_timeout = false
80235
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
   394
                val watchdog =
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
   395
                  if (timeout.is_zero) Watchdog.none
06036a16779f clarified signature: more explicit types;
wenzelm
parents: 80229
diff changeset
   396
                  else Watchdog(timeout, _ => { is_timeout = true; true })
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   397
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   398
                Exn.capture { process.result(input = input, watchdog = watchdog, strict = false) }
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   399
                match {
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   400
                  case Exn.Exn(exn) => reply_failure(exn)
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   401
                  case Exn.Res(res0) =>
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   402
                    val res = if (!res0.ok && is_timeout) res0.timeout_rc else res0
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   403
                    if (debugging) {
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   404
                      Output.writeln(
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   405
                        "stop " + quote(descr) + " (uuid=" + uuid + ", return_code=" + res.rc + ")")
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   406
                    }
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   407
                    reply_result(res)
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   408
                }
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   409
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   410
                _processes.change(provers => provers - uuid)
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   411
              }
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   412
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   413
              connection.await_close()
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   414
          }
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   415
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   416
        case Some(_) => reply_failure(ERROR("Bad protocol message"))
73228
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   417
      }
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   418
    }
0575cfd2ecfc support multi-threaded Bash.process invocation on Apple Silicon, where Poly/ML is running on Rosetta 2;
wenzelm
parents: 73134
diff changeset
   419
  }
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   420
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   421
  class Handler extends Session.Protocol_Handler {
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   422
    private var server: Server = null
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   423
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   424
    override def init(session: Session): Unit = {
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   425
      exit()
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   426
      server = Server.start(debugging = session.session_options.bool("bash_process_debugging"))
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   427
    }
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   428
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   429
    override def exit(): Unit = {
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   430
      if (server != null) {
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   431
        server.stop()
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   432
        server = null
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   433
      }
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   434
    }
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   435
75393
87ebf5a50283 clarified formatting, for the sake of scala3;
wenzelm
parents: 74306
diff changeset
   436
    override def prover_options(options: Options): Options = {
74147
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   437
      val address = if (server == null) "" else server.address
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   438
      val password = if (server == null) "" else server.password
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   439
      options +
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   440
        ("bash_process_address=" + address) +
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   441
        ("bash_process_password=" + password)
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   442
    }
d030b988d470 provide bash_process server for Isabelle/ML and other external programs;
wenzelm
parents: 74142
diff changeset
   443
  }
60991
2fc5a44346b5 clarified modules, like ML version;
wenzelm
parents:
diff changeset
   444
}