src/Pure/Concurrent/bash.ML
author wenzelm
Sat, 30 Nov 2013 15:05:10 +0100
changeset 54651 d71e7908eec3
parent 47764 d141f1193789
child 54677 ae5426994961
permissions -rw-r--r--
more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
40748
591b6778d076 removed bash from ML system bootstrap, and past the Secure ML barrier;
wenzelm
parents:
diff changeset
     1
(*  Title:      Pure/Concurrent/bash.ML
591b6778d076 removed bash from ML system bootstrap, and past the Secure ML barrier;
wenzelm
parents:
diff changeset
     2
    Author:     Makarius
591b6778d076 removed bash from ML system bootstrap, and past the Secure ML barrier;
wenzelm
parents:
diff changeset
     3
591b6778d076 removed bash from ML system bootstrap, and past the Secure ML barrier;
wenzelm
parents:
diff changeset
     4
GNU bash processes, with propagation of interrupts.
591b6778d076 removed bash from ML system bootstrap, and past the Secure ML barrier;
wenzelm
parents:
diff changeset
     5
*)
591b6778d076 removed bash from ML system bootstrap, and past the Secure ML barrier;
wenzelm
parents:
diff changeset
     6
44054
da5fcc0f6c52 proper signature;
wenzelm
parents: 43850
diff changeset
     7
signature BASH =
da5fcc0f6c52 proper signature;
wenzelm
parents: 43850
diff changeset
     8
sig
47499
4b0daca2bf88 redirect bash stderr to Isabelle warning as appropriate -- avoid raw process error output which may either get ignored or overload PIDE syslog in extreme cases;
wenzelm
parents: 44112
diff changeset
     9
  val process: string -> {out: string, err: string, rc: int, terminate: unit -> unit}
44054
da5fcc0f6c52 proper signature;
wenzelm
parents: 43850
diff changeset
    10
end;
da5fcc0f6c52 proper signature;
wenzelm
parents: 43850
diff changeset
    11
da5fcc0f6c52 proper signature;
wenzelm
parents: 43850
diff changeset
    12
structure Bash: BASH =
43850
7f2cbc713344 moved bash operations to Isabelle_System (cf. Scala version);
wenzelm
parents: 43847
diff changeset
    13
struct
7f2cbc713344 moved bash operations to Isabelle_System (cf. Scala version);
wenzelm
parents: 43847
diff changeset
    14
7f2cbc713344 moved bash operations to Isabelle_System (cf. Scala version);
wenzelm
parents: 43847
diff changeset
    15
val process = uninterruptible (fn restore_attributes => fn script =>
40749
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    16
  let
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    17
    datatype result = Wait | Signal | Result of int;
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    18
    val result = Synchronized.var "bash_result" Wait;
40748
591b6778d076 removed bash from ML system bootstrap, and past the Secure ML barrier;
wenzelm
parents:
diff changeset
    19
40749
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    20
    val id = serial_string ();
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    21
    val script_path = File.tmp_path (Path.basic ("bash_script" ^ id));
47499
4b0daca2bf88 redirect bash stderr to Isabelle warning as appropriate -- avoid raw process error output which may either get ignored or overload PIDE syslog in extreme cases;
wenzelm
parents: 44112
diff changeset
    22
    val out_path = File.tmp_path (Path.basic ("bash_out" ^ id));
4b0daca2bf88 redirect bash stderr to Isabelle warning as appropriate -- avoid raw process error output which may either get ignored or overload PIDE syslog in extreme cases;
wenzelm
parents: 44112
diff changeset
    23
    val err_path = File.tmp_path (Path.basic ("bash_err" ^ id));
40749
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    24
    val pid_path = File.tmp_path (Path.basic ("bash_pid" ^ id));
40748
591b6778d076 removed bash from ML system bootstrap, and past the Secure ML barrier;
wenzelm
parents:
diff changeset
    25
54651
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    26
    fun cleanup_files () =
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    27
     (try File.rm script_path;
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    28
      try File.rm out_path;
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    29
      try File.rm err_path;
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    30
      try File.rm pid_path);
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    31
    val _ = cleanup_files ();
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    32
40749
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    33
    val system_thread =
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    34
      Simple_Thread.fork false (fn () =>
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    35
        Multithreading.with_attributes Multithreading.private_interrupts (fn _ =>
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    36
          let
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    37
            val _ = File.write script_path script;
47764
d141f1193789 more direct bash process group invocation on Cygwin, bypassing extra sh.exe and perl.exe which tend to crash;
wenzelm
parents: 47499
diff changeset
    38
            val bash_script =
d141f1193789 more direct bash process group invocation on Cygwin, bypassing extra sh.exe and perl.exe which tend to crash;
wenzelm
parents: 47499
diff changeset
    39
              "exec bash " ^
d141f1193789 more direct bash process group invocation on Cygwin, bypassing extra sh.exe and perl.exe which tend to crash;
wenzelm
parents: 47499
diff changeset
    40
                File.shell_path script_path ^
d141f1193789 more direct bash process group invocation on Cygwin, bypassing extra sh.exe and perl.exe which tend to crash;
wenzelm
parents: 47499
diff changeset
    41
                " > " ^ File.shell_path out_path ^
d141f1193789 more direct bash process group invocation on Cygwin, bypassing extra sh.exe and perl.exe which tend to crash;
wenzelm
parents: 47499
diff changeset
    42
                " 2> " ^ File.shell_path err_path;
40749
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    43
            val status =
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    44
              OS.Process.system
47764
d141f1193789 more direct bash process group invocation on Cygwin, bypassing extra sh.exe and perl.exe which tend to crash;
wenzelm
parents: 47499
diff changeset
    45
                (if getenv "EXEC_PROCESS" = "" then
d141f1193789 more direct bash process group invocation on Cygwin, bypassing extra sh.exe and perl.exe which tend to crash;
wenzelm
parents: 47499
diff changeset
    46
                  ("exec \"$ISABELLE_HOME/lib/scripts/process\" group " ^
d141f1193789 more direct bash process group invocation on Cygwin, bypassing extra sh.exe and perl.exe which tend to crash;
wenzelm
parents: 47499
diff changeset
    47
                    File.shell_path pid_path ^ " script " ^ quote bash_script)
d141f1193789 more direct bash process group invocation on Cygwin, bypassing extra sh.exe and perl.exe which tend to crash;
wenzelm
parents: 47499
diff changeset
    48
                 else
d141f1193789 more direct bash process group invocation on Cygwin, bypassing extra sh.exe and perl.exe which tend to crash;
wenzelm
parents: 47499
diff changeset
    49
                  ("exec \"$EXEC_PROCESS\" " ^
d141f1193789 more direct bash process group invocation on Cygwin, bypassing extra sh.exe and perl.exe which tend to crash;
wenzelm
parents: 47499
diff changeset
    50
                    File.shell_path pid_path ^ " " ^ quote bash_script));
40749
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    51
            val res =
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    52
              (case Posix.Process.fromStatus status of
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    53
                Posix.Process.W_EXITED => Result 0
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    54
              | Posix.Process.W_EXITSTATUS 0wx82 => Signal
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    55
              | Posix.Process.W_EXITSTATUS w => Result (Word8.toInt w)
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    56
              | Posix.Process.W_SIGNALED s =>
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    57
                  if s = Posix.Signal.int then Signal
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    58
                  else Result (256 + LargeWord.toInt (Posix.Signal.toWord s))
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    59
              | Posix.Process.W_STOPPED s =>
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    60
                  Result (512 + LargeWord.toInt (Posix.Signal.toWord s)));
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    61
          in Synchronized.change result (K res) end
40783
21f7e8d66a39 more conventional exception propagation -- taking into account Simple_Thread.fork wrapping;
wenzelm
parents: 40750
diff changeset
    62
          handle exn =>
21f7e8d66a39 more conventional exception propagation -- taking into account Simple_Thread.fork wrapping;
wenzelm
parents: 40750
diff changeset
    63
            (Synchronized.change result (fn Wait => Signal | res => res); reraise exn)));
40748
591b6778d076 removed bash from ML system bootstrap, and past the Secure ML barrier;
wenzelm
parents:
diff changeset
    64
54651
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    65
    fun read_pid 0 = NONE
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    66
      | read_pid count =
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    67
          (case (Int.fromString (File.read pid_path) handle IO.Io _ => NONE) of
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    68
            NONE => (OS.Process.sleep (seconds 0.1); read_pid (count - 1))
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    69
          | some => some);
43847
529159f81d06 more general bash_process, which allows to terminate background processes as well;
wenzelm
parents: 40896
diff changeset
    70
54651
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    71
    fun terminate NONE = ()
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    72
      | terminate (SOME pid) =
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    73
          let
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    74
            val sig_test = Posix.Signal.fromWord 0w0;
40750
2064991db2ac more thorough process termination (cf. Scala version);
wenzelm
parents: 40749
diff changeset
    75
54651
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    76
            fun kill_group pid s =
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    77
              (Posix.Process.kill
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    78
                (Posix.Process.K_GROUP (Posix.Process.wordToPid (LargeWord.fromInt pid)), s); true)
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    79
              handle OS.SysErr _ => false;
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    80
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    81
            fun kill s = (kill_group pid s; kill_group pid sig_test);
40750
2064991db2ac more thorough process termination (cf. Scala version);
wenzelm
parents: 40749
diff changeset
    82
54651
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    83
            fun multi_kill count s =
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    84
              count = 0 orelse
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    85
                kill s andalso (OS.Process.sleep (seconds 0.1); multi_kill (count - 1) s);
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    86
            val _ =
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    87
              multi_kill 10 Posix.Signal.int andalso
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    88
              multi_kill 10 Posix.Signal.term andalso
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    89
              multi_kill 10 Posix.Signal.kill;
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    90
          in () end;
40750
2064991db2ac more thorough process termination (cf. Scala version);
wenzelm
parents: 40749
diff changeset
    91
40749
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    92
    fun cleanup () =
44112
ef876972fdc1 more explicit Simple_Thread.interrupt_unsynchronized, to emphasize its meaning;
wenzelm
parents: 44054
diff changeset
    93
     (Simple_Thread.interrupt_unsynchronized system_thread;
54651
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
    94
      cleanup_files ());
40749
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    95
  in
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    96
    let
40748
591b6778d076 removed bash from ML system bootstrap, and past the Secure ML barrier;
wenzelm
parents:
diff changeset
    97
      val _ =
40749
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
    98
        restore_attributes (fn () =>
40750
2064991db2ac more thorough process termination (cf. Scala version);
wenzelm
parents: 40749
diff changeset
    99
          Synchronized.guarded_access result (fn Wait => NONE | x => SOME ((), x))) ();
40748
591b6778d076 removed bash from ML system bootstrap, and past the Secure ML barrier;
wenzelm
parents:
diff changeset
   100
47499
4b0daca2bf88 redirect bash stderr to Isabelle warning as appropriate -- avoid raw process error output which may either get ignored or overload PIDE syslog in extreme cases;
wenzelm
parents: 44112
diff changeset
   101
      val out = the_default "" (try File.read out_path);
4b0daca2bf88 redirect bash stderr to Isabelle warning as appropriate -- avoid raw process error output which may either get ignored or overload PIDE syslog in extreme cases;
wenzelm
parents: 44112
diff changeset
   102
      val err = the_default "" (try File.read err_path);
40749
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
   103
      val rc = (case Synchronized.value result of Signal => Exn.interrupt () | Result rc => rc);
54651
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
   104
      val pid = read_pid 1;
40749
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
   105
      val _ = cleanup ();
47499
4b0daca2bf88 redirect bash stderr to Isabelle warning as appropriate -- avoid raw process error output which may either get ignored or overload PIDE syslog in extreme cases;
wenzelm
parents: 44112
diff changeset
   106
    in {out = out, err = err, rc = rc, terminate = fn () => terminate pid} end
54651
d71e7908eec3 more thorough read_pid with extra delay, to give external process a chance to write pid file before attempting to terminate it -- especially relevant for PIDE document processing, where interrupts can happen in the range of milliseconds;
wenzelm
parents: 47764
diff changeset
   107
    handle exn => (terminate (read_pid 10); cleanup (); reraise exn)
40749
cb6698d2dbaf prefer Isabelle/ML concurrency elements;
wenzelm
parents: 40748
diff changeset
   108
  end);
40748
591b6778d076 removed bash from ML system bootstrap, and past the Secure ML barrier;
wenzelm
parents:
diff changeset
   109
43850
7f2cbc713344 moved bash operations to Isabelle_System (cf. Scala version);
wenzelm
parents: 43847
diff changeset
   110
end;
7f2cbc713344 moved bash operations to Isabelle_System (cf. Scala version);
wenzelm
parents: 43847
diff changeset
   111