src/Pure/Concurrent/bash.ML
author wenzelm
Sat, 16 Jul 2011 20:52:41 +0200
changeset 44721 7f2cbc713344
parent 44718 529159f81d06
child 44925 da5fcc0f6c52
permissions -rw-r--r--
moved bash operations to Isabelle_System (cf. Scala version);
     1 (*  Title:      Pure/Concurrent/bash.ML
     2     Author:     Makarius
     3 
     4 GNU bash processes, with propagation of interrupts.
     5 *)
     6 
     7 structure Bash =
     8 struct
     9 
    10 val process = uninterruptible (fn restore_attributes => fn script =>
    11   let
    12     datatype result = Wait | Signal | Result of int;
    13     val result = Synchronized.var "bash_result" Wait;
    14 
    15     val id = serial_string ();
    16     val script_path = File.tmp_path (Path.basic ("bash_script" ^ id));
    17     val output_path = File.tmp_path (Path.basic ("bash_output" ^ id));
    18     val pid_path = File.tmp_path (Path.basic ("bash_pid" ^ id));
    19 
    20     val system_thread =
    21       Simple_Thread.fork false (fn () =>
    22         Multithreading.with_attributes Multithreading.private_interrupts (fn _ =>
    23           let
    24             val _ = File.write script_path script;
    25             val status =
    26               OS.Process.system
    27                 ("exec \"$ISABELLE_HOME/lib/scripts/process\" group " ^
    28                   File.shell_path pid_path ^ " script \"exec bash " ^
    29                   File.shell_path script_path ^ " > " ^
    30                   File.shell_path output_path ^ "\"");
    31             val res =
    32               (case Posix.Process.fromStatus status of
    33                 Posix.Process.W_EXITED => Result 0
    34               | Posix.Process.W_EXITSTATUS 0wx82 => Signal
    35               | Posix.Process.W_EXITSTATUS w => Result (Word8.toInt w)
    36               | Posix.Process.W_SIGNALED s =>
    37                   if s = Posix.Signal.int then Signal
    38                   else Result (256 + LargeWord.toInt (Posix.Signal.toWord s))
    39               | Posix.Process.W_STOPPED s =>
    40                   Result (512 + LargeWord.toInt (Posix.Signal.toWord s)));
    41           in Synchronized.change result (K res) end
    42           handle exn =>
    43             (Synchronized.change result (fn Wait => Signal | res => res); reraise exn)));
    44 
    45     fun get_pid () = Int.fromString (File.read pid_path) handle IO.Io _ => NONE;
    46 
    47     fun terminate opt_pid =
    48       let
    49         val sig_test = Posix.Signal.fromWord 0w0;
    50 
    51         fun kill_group pid s =
    52           (Posix.Process.kill
    53             (Posix.Process.K_GROUP (Posix.Process.wordToPid (LargeWord.fromInt pid)), s); true)
    54           handle OS.SysErr _ => false;
    55 
    56         fun kill s =
    57           (case opt_pid of
    58             NONE => true
    59           | SOME pid => (kill_group pid s; kill_group pid sig_test));
    60 
    61         fun multi_kill count s =
    62           count = 0 orelse
    63             kill s andalso (OS.Process.sleep (seconds 0.1); multi_kill (count - 1) s);
    64         val _ =
    65           multi_kill 10 Posix.Signal.int andalso
    66           multi_kill 10 Posix.Signal.term andalso
    67           multi_kill 10 Posix.Signal.kill;
    68       in () end;
    69 
    70     fun cleanup () =
    71      (Simple_Thread.interrupt system_thread;
    72       try File.rm script_path;
    73       try File.rm output_path;
    74       try File.rm pid_path);
    75   in
    76     let
    77       val _ =
    78         restore_attributes (fn () =>
    79           Synchronized.guarded_access result (fn Wait => NONE | x => SOME ((), x))) ();
    80 
    81       val output = the_default "" (try File.read output_path);
    82       val rc = (case Synchronized.value result of Signal => Exn.interrupt () | Result rc => rc);
    83       val pid = get_pid ();
    84       val _ = cleanup ();
    85     in {output = output, rc = rc, terminate = fn () => terminate pid} end
    86     handle exn => (terminate (get_pid ()); cleanup (); reraise exn)
    87   end);
    88 
    89 end;
    90