src/Pure/Concurrent/bash.ML
author wenzelm
Sat, 27 Nov 2010 19:17:55 +0100
changeset 41002 cb6698d2dbaf
parent 41001 591b6778d076
child 41003 2064991db2ac
permissions -rw-r--r--
prefer Isabelle/ML concurrency elements;
more careful propagation of interrupts;
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@41002
     7
val bash_output = uninterruptible (fn restore_attributes => fn script =>
wenzelm@41002
     8
  let
wenzelm@41002
     9
    datatype result = Wait | Signal | Result of int;
wenzelm@41002
    10
    val result = Synchronized.var "bash_result" Wait;
wenzelm@41001
    11
wenzelm@41002
    12
    val id = serial_string ();
wenzelm@41002
    13
    val script_path = File.tmp_path (Path.basic ("bash_script" ^ id));
wenzelm@41002
    14
    val output_path = File.tmp_path (Path.basic ("bash_output" ^ id));
wenzelm@41002
    15
    val pid_path = File.tmp_path (Path.basic ("bash_pid" ^ id));
wenzelm@41001
    16
wenzelm@41002
    17
    val system_thread =
wenzelm@41002
    18
      Simple_Thread.fork false (fn () =>
wenzelm@41002
    19
        Multithreading.with_attributes Multithreading.private_interrupts (fn _ =>
wenzelm@41002
    20
          let
wenzelm@41002
    21
            val _ = File.write script_path script;
wenzelm@41002
    22
            val status =
wenzelm@41002
    23
              OS.Process.system
wenzelm@41002
    24
                ("exec \"$ISABELLE_HOME/lib/scripts/process\" group " ^
wenzelm@41002
    25
                  File.shell_path pid_path ^ " script \"exec bash " ^
wenzelm@41002
    26
                  File.shell_path script_path ^ " > " ^
wenzelm@41002
    27
                  File.shell_path output_path ^ "\"");
wenzelm@41002
    28
            val res =
wenzelm@41002
    29
              (case Posix.Process.fromStatus status of
wenzelm@41002
    30
                Posix.Process.W_EXITED => Result 0
wenzelm@41002
    31
              | Posix.Process.W_EXITSTATUS 0wx82 => Signal
wenzelm@41002
    32
              | Posix.Process.W_EXITSTATUS w => Result (Word8.toInt w)
wenzelm@41002
    33
              | Posix.Process.W_SIGNALED s =>
wenzelm@41002
    34
                  if s = Posix.Signal.int then Signal
wenzelm@41002
    35
                  else Result (256 + LargeWord.toInt (Posix.Signal.toWord s))
wenzelm@41002
    36
              | Posix.Process.W_STOPPED s =>
wenzelm@41002
    37
                  Result (512 + LargeWord.toInt (Posix.Signal.toWord s)));
wenzelm@41002
    38
          in Synchronized.change result (K res) end
wenzelm@41002
    39
          handle _ (*sic*) => Synchronized.change result (fn Wait => Signal | res => res)));
wenzelm@41001
    40
wenzelm@41002
    41
    fun cleanup () =
wenzelm@41002
    42
     (Simple_Thread.interrupt system_thread;
wenzelm@41002
    43
      try File.rm script_path;
wenzelm@41002
    44
      try File.rm output_path;
wenzelm@41002
    45
      try File.rm pid_path);
wenzelm@41001
    46
wenzelm@41002
    47
    fun kill n =
wenzelm@41002
    48
      (case Int.fromString (File.read pid_path) of
wenzelm@41002
    49
        SOME pid =>
wenzelm@41002
    50
          Posix.Process.kill
wenzelm@41002
    51
            (Posix.Process.K_GROUP (Posix.Process.wordToPid (LargeWord.fromInt pid)),
wenzelm@41002
    52
              Posix.Signal.int)
wenzelm@41002
    53
      | NONE => ())
wenzelm@41002
    54
      handle OS.SysErr _ => ()
wenzelm@41002
    55
        | IO.Io _ => (OS.Process.sleep (seconds 0.1); if n > 0 then kill (n - 1) else ());
wenzelm@41002
    56
  in
wenzelm@41001
    57
    let
wenzelm@41002
    58
      (*proxy for interrupts*)
wenzelm@41002
    59
      val _ =
wenzelm@41002
    60
        restore_attributes (fn () =>
wenzelm@41002
    61
          Synchronized.guarded_access result (fn Wait => NONE | x => SOME ((), x))) ()
wenzelm@41002
    62
        handle exn => (if Exn.is_interrupt exn then kill 10 else (); reraise exn);
wenzelm@41001
    63
wenzelm@41002
    64
      val output = the_default "" (try File.read output_path);
wenzelm@41002
    65
      val rc = (case Synchronized.value result of Signal => Exn.interrupt () | Result rc => rc);
wenzelm@41002
    66
      val _ = cleanup ();
wenzelm@41002
    67
    in (output, rc) end
wenzelm@41002
    68
    handle exn => (cleanup (); reraise exn)
wenzelm@41002
    69
  end);
wenzelm@41001
    70