src/Pure/Concurrent/bash.ML
author wenzelm
Thu, 05 Dec 2013 20:06:28 +0100
changeset 56019 ae5426994961
parent 55300 d71e7908eec3
child 59180 85ec71012df8
permissions -rw-r--r--
strict EXEC_PROCESS: component can be expected to be present;
wenzelm@41001
     1
(*  Title:      Pure/Concurrent/bash.ML
wenzelm@41001
     2
    Author:     Makarius
wenzelm@41001
     3
wenzelm@41001
     4
GNU bash processes, with propagation of interrupts.
wenzelm@41001
     5
*)
wenzelm@41001
     6
wenzelm@44925
     7
signature BASH =
wenzelm@44925
     8
sig
wenzelm@48370
     9
  val process: string -> {out: string, err: string, rc: int, terminate: unit -> unit}
wenzelm@44925
    10
end;
wenzelm@44925
    11
wenzelm@44925
    12
structure Bash: BASH =
wenzelm@44721
    13
struct
wenzelm@44721
    14
wenzelm@44721
    15
val process = uninterruptible (fn restore_attributes => fn script =>
wenzelm@41002
    16
  let
wenzelm@41002
    17
    datatype result = Wait | Signal | Result of int;
wenzelm@41002
    18
    val result = Synchronized.var "bash_result" Wait;
wenzelm@41001
    19
wenzelm@41002
    20
    val id = serial_string ();
wenzelm@41002
    21
    val script_path = File.tmp_path (Path.basic ("bash_script" ^ id));
wenzelm@48370
    22
    val out_path = File.tmp_path (Path.basic ("bash_out" ^ id));
wenzelm@48370
    23
    val err_path = File.tmp_path (Path.basic ("bash_err" ^ id));
wenzelm@41002
    24
    val pid_path = File.tmp_path (Path.basic ("bash_pid" ^ id));
wenzelm@41001
    25
wenzelm@55300
    26
    fun cleanup_files () =
wenzelm@55300
    27
     (try File.rm script_path;
wenzelm@55300
    28
      try File.rm out_path;
wenzelm@55300
    29
      try File.rm err_path;
wenzelm@55300
    30
      try File.rm pid_path);
wenzelm@55300
    31
    val _ = cleanup_files ();
wenzelm@55300
    32
wenzelm@41002
    33
    val system_thread =
wenzelm@41002
    34
      Simple_Thread.fork false (fn () =>
wenzelm@41002
    35
        Multithreading.with_attributes Multithreading.private_interrupts (fn _ =>
wenzelm@41002
    36
          let
wenzelm@41002
    37
            val _ = File.write script_path script;
wenzelm@48651
    38
            val bash_script =
wenzelm@48651
    39
              "exec bash " ^
wenzelm@48651
    40
                File.shell_path script_path ^
wenzelm@48651
    41
                " > " ^ File.shell_path out_path ^
wenzelm@48651
    42
                " 2> " ^ File.shell_path err_path;
wenzelm@56019
    43
            val _ = getenv_strict "EXEC_PROCESS";
wenzelm@41002
    44
            val status =
wenzelm@41002
    45
              OS.Process.system
wenzelm@56019
    46
                ("exec \"$EXEC_PROCESS\" " ^ File.shell_path pid_path ^ " " ^ quote bash_script);
wenzelm@41002
    47
            val res =
wenzelm@41002
    48
              (case Posix.Process.fromStatus status of
wenzelm@41002
    49
                Posix.Process.W_EXITED => Result 0
wenzelm@41002
    50
              | Posix.Process.W_EXITSTATUS 0wx82 => Signal
wenzelm@41002
    51
              | Posix.Process.W_EXITSTATUS w => Result (Word8.toInt w)
wenzelm@41002
    52
              | Posix.Process.W_SIGNALED s =>
wenzelm@41002
    53
                  if s = Posix.Signal.int then Signal
wenzelm@41002
    54
                  else Result (256 + LargeWord.toInt (Posix.Signal.toWord s))
wenzelm@41002
    55
              | Posix.Process.W_STOPPED s =>
wenzelm@41002
    56
                  Result (512 + LargeWord.toInt (Posix.Signal.toWord s)));
wenzelm@41002
    57
          in Synchronized.change result (K res) end
wenzelm@41033
    58
          handle exn =>
wenzelm@41033
    59
            (Synchronized.change result (fn Wait => Signal | res => res); reraise exn)));
wenzelm@41001
    60
wenzelm@55300
    61
    fun read_pid 0 = NONE
wenzelm@55300
    62
      | read_pid count =
wenzelm@55300
    63
          (case (Int.fromString (File.read pid_path) handle IO.Io _ => NONE) of
wenzelm@55300
    64
            NONE => (OS.Process.sleep (seconds 0.1); read_pid (count - 1))
wenzelm@55300
    65
          | some => some);
wenzelm@44718
    66
wenzelm@55300
    67
    fun terminate NONE = ()
wenzelm@55300
    68
      | terminate (SOME pid) =
wenzelm@55300
    69
          let
wenzelm@55300
    70
            val sig_test = Posix.Signal.fromWord 0w0;
wenzelm@41003
    71
wenzelm@55300
    72
            fun kill_group pid s =
wenzelm@55300
    73
              (Posix.Process.kill
wenzelm@55300
    74
                (Posix.Process.K_GROUP (Posix.Process.wordToPid (LargeWord.fromInt pid)), s); true)
wenzelm@55300
    75
              handle OS.SysErr _ => false;
wenzelm@41003
    76
wenzelm@55300
    77
            fun kill s = (kill_group pid s; kill_group pid sig_test);
wenzelm@41003
    78
wenzelm@55300
    79
            fun multi_kill count s =
wenzelm@55300
    80
              count = 0 orelse
wenzelm@55300
    81
                kill s andalso (OS.Process.sleep (seconds 0.1); multi_kill (count - 1) s);
wenzelm@55300
    82
            val _ =
wenzelm@55300
    83
              multi_kill 10 Posix.Signal.int andalso
wenzelm@55300
    84
              multi_kill 10 Posix.Signal.term andalso
wenzelm@55300
    85
              multi_kill 10 Posix.Signal.kill;
wenzelm@55300
    86
          in () end;
wenzelm@41003
    87
wenzelm@41002
    88
    fun cleanup () =
wenzelm@44995
    89
     (Simple_Thread.interrupt_unsynchronized system_thread;
wenzelm@55300
    90
      cleanup_files ());
wenzelm@41002
    91
  in
wenzelm@41001
    92
    let
wenzelm@41002
    93
      val _ =
wenzelm@41002
    94
        restore_attributes (fn () =>
wenzelm@41003
    95
          Synchronized.guarded_access result (fn Wait => NONE | x => SOME ((), x))) ();
wenzelm@41001
    96
wenzelm@48370
    97
      val out = the_default "" (try File.read out_path);
wenzelm@48370
    98
      val err = the_default "" (try File.read err_path);
wenzelm@41002
    99
      val rc = (case Synchronized.value result of Signal => Exn.interrupt () | Result rc => rc);
wenzelm@55300
   100
      val pid = read_pid 1;
wenzelm@41002
   101
      val _ = cleanup ();
wenzelm@48370
   102
    in {out = out, err = err, rc = rc, terminate = fn () => terminate pid} end
wenzelm@55300
   103
    handle exn => (terminate (read_pid 10); cleanup (); reraise exn)
wenzelm@41002
   104
  end);
wenzelm@41001
   105
wenzelm@44721
   106
end;
wenzelm@44721
   107