src/HOL/Tools/ATP_Manager/async_manager.ML
author blanchet
Fri, 25 Jun 2010 23:35:14 +0200
changeset 37585 c2ed8112ce57
parent 37584 2e289dc13615
child 38197 e557d511c791
permissions -rw-r--r--
multiplexing
blanchet@37583
     1
(*  Title:      HOL/Tools/ATP_Manager/async_manager.ML
blanchet@37583
     2
    Author:     Fabian Immler, TU Muenchen
blanchet@37583
     3
    Author:     Makarius
blanchet@37583
     4
    Author:     Jasmin Blanchette, TU Muenchen
blanchet@37583
     5
blanchet@37583
     6
Central manager for asynchronous diagnosis tool threads.
blanchet@37583
     7
*)
blanchet@37583
     8
blanchet@37583
     9
signature ASYNC_MANAGER =
blanchet@37583
    10
sig
blanchet@37584
    11
  val launch :
blanchet@37584
    12
    string -> bool -> Time.time -> Time.time -> string -> (unit -> string)
blanchet@37584
    13
    -> unit
blanchet@37585
    14
  val kill_threads : string -> string -> unit
blanchet@37585
    15
  val running_threads : string -> string -> unit
blanchet@37585
    16
  val thread_messages : string -> string -> int option -> unit
blanchet@37583
    17
end;
blanchet@37583
    18
blanchet@37583
    19
structure Async_Manager : ASYNC_MANAGER =
blanchet@37583
    20
struct
blanchet@37583
    21
blanchet@37583
    22
(** preferences **)
blanchet@37583
    23
blanchet@37583
    24
val message_store_limit = 20;
blanchet@37583
    25
val message_display_limit = 5;
blanchet@37583
    26
blanchet@37583
    27
blanchet@37583
    28
(** thread management **)
blanchet@37583
    29
blanchet@37583
    30
(* data structures over threads *)
blanchet@37583
    31
blanchet@37583
    32
structure Thread_Heap = Heap
blanchet@37583
    33
(
blanchet@37583
    34
  type elem = Time.time * Thread.thread;
blanchet@37583
    35
  fun ord ((a, _), (b, _)) = Time.compare (a, b);
blanchet@37583
    36
);
blanchet@37583
    37
blanchet@37583
    38
fun lookup_thread xs = AList.lookup Thread.equal xs;
blanchet@37583
    39
fun delete_thread xs = AList.delete Thread.equal xs;
blanchet@37583
    40
fun update_thread xs = AList.update Thread.equal xs;
blanchet@37583
    41
blanchet@37583
    42
blanchet@37583
    43
(* state of thread manager *)
blanchet@37583
    44
blanchet@37583
    45
type state =
blanchet@37585
    46
  {manager: Thread.thread option,
blanchet@37585
    47
   timeout_heap: Thread_Heap.T,
blanchet@37585
    48
   active: (Thread.thread * (string * Time.time * Time.time * string)) list,
blanchet@37585
    49
   canceling: (Thread.thread * (string * Time.time * string)) list,
blanchet@37585
    50
   messages: (string * string) list,
blanchet@37585
    51
   store: (string * string) list}
blanchet@37583
    52
blanchet@37583
    53
fun make_state manager timeout_heap active canceling messages store : state =
blanchet@37583
    54
  {manager = manager, timeout_heap = timeout_heap, active = active,
blanchet@37583
    55
   canceling = canceling, messages = messages, store = store}
blanchet@37583
    56
blanchet@37583
    57
val global_state = Synchronized.var "async_manager"
blanchet@37583
    58
  (make_state NONE Thread_Heap.empty [] [] [] []);
blanchet@37583
    59
blanchet@37583
    60
blanchet@37583
    61
(* unregister thread *)
blanchet@37583
    62
blanchet@37583
    63
fun unregister verbose message thread =
blanchet@37583
    64
  Synchronized.change global_state
blanchet@37583
    65
  (fn state as {manager, timeout_heap, active, canceling, messages, store} =>
blanchet@37583
    66
    (case lookup_thread active thread of
blanchet@37585
    67
      SOME (tool, birth_time, _, desc) =>
blanchet@37583
    68
        let
blanchet@37583
    69
          val active' = delete_thread thread active;
blanchet@37583
    70
          val now = Time.now ()
blanchet@37585
    71
          val canceling' = (thread, (tool, now, desc)) :: canceling
blanchet@37583
    72
          val message' =
blanchet@37583
    73
            desc ^ "\n" ^ message ^
blanchet@37583
    74
            (if verbose then
blanchet@37583
    75
               "Total time: " ^ Int.toString (Time.toMilliseconds
blanchet@37583
    76
                                          (Time.- (now, birth_time))) ^ " ms.\n"
blanchet@37583
    77
             else
blanchet@37583
    78
               "")
blanchet@37585
    79
          val messages' = (tool, message') :: messages;
blanchet@37585
    80
          val store' = (tool, message') ::
blanchet@37583
    81
            (if length store <= message_store_limit then store
blanchet@37583
    82
             else #1 (chop message_store_limit store));
blanchet@37583
    83
        in make_state manager timeout_heap active' canceling' messages' store' end
blanchet@37583
    84
    | NONE => state));
blanchet@37583
    85
blanchet@37583
    86
blanchet@37583
    87
(* main manager thread -- only one may exist *)
blanchet@37583
    88
blanchet@37583
    89
val min_wait_time = Time.fromMilliseconds 300;
blanchet@37583
    90
val max_wait_time = Time.fromSeconds 10;
blanchet@37583
    91
blanchet@37583
    92
fun replace_all bef aft =
blanchet@37583
    93
  let
blanchet@37583
    94
    fun aux seen "" = String.implode (rev seen)
blanchet@37583
    95
      | aux seen s =
blanchet@37583
    96
        if String.isPrefix bef s then
blanchet@37583
    97
          aux seen "" ^ aft ^ aux [] (unprefix bef s)
blanchet@37583
    98
        else
blanchet@37583
    99
          aux (String.sub (s, 0) :: seen) (String.extract (s, 1, NONE))
blanchet@37583
   100
  in aux [] end
blanchet@37583
   101
blanchet@37583
   102
(* This is a workaround for Proof General's off-by-a-few sendback display bug,
blanchet@37583
   103
   whereby "pr" in "proof" is not highlighted. *)
blanchet@37583
   104
val break_into_chunks =
blanchet@37585
   105
  maps (space_explode "\000" o replace_all "\n\n" "\000" o snd)
blanchet@37583
   106
blanchet@37585
   107
fun print_new_messages () =
blanchet@37583
   108
  case Synchronized.change_result global_state
blanchet@37583
   109
         (fn {manager, timeout_heap, active, canceling, messages, store} =>
blanchet@37583
   110
             (messages, make_state manager timeout_heap active canceling []
blanchet@37583
   111
                                   store)) of
blanchet@37583
   112
    [] => ()
blanchet@37585
   113
  | msgs as (tool, _) :: _ =>
blanchet@37585
   114
    let val ss = break_into_chunks msgs in
blanchet@37585
   115
      List.app priority (tool ^ ": " ^ hd ss :: tl ss)
blanchet@37585
   116
    end
blanchet@37583
   117
blanchet@37585
   118
fun check_thread_manager verbose = Synchronized.change global_state
blanchet@37583
   119
  (fn state as {manager, timeout_heap, active, canceling, messages, store} =>
blanchet@37583
   120
    if (case manager of SOME thread => Thread.isActive thread | NONE => false) then state
blanchet@37583
   121
    else let val manager = SOME (Toplevel.thread false (fn () =>
blanchet@37583
   122
      let
blanchet@37583
   123
        fun time_limit timeout_heap =
blanchet@37583
   124
          (case try Thread_Heap.min timeout_heap of
blanchet@37583
   125
            NONE => Time.+ (Time.now (), max_wait_time)
blanchet@37583
   126
          | SOME (time, _) => time);
blanchet@37583
   127
blanchet@37583
   128
        (*action: find threads whose timeout is reached, and interrupt canceling threads*)
blanchet@37583
   129
        fun action {manager, timeout_heap, active, canceling, messages, store} =
blanchet@37583
   130
          let val (timeout_threads, timeout_heap') =
blanchet@37583
   131
            Thread_Heap.upto (Time.now (), Thread.self ()) timeout_heap;
blanchet@37583
   132
          in
blanchet@37585
   133
            if null timeout_threads andalso null canceling then
blanchet@37585
   134
              NONE
blanchet@37583
   135
            else
blanchet@37583
   136
              let
blanchet@37583
   137
                val _ = List.app (Simple_Thread.interrupt o #1) canceling
blanchet@37583
   138
                val canceling' = filter (Thread.isActive o #1) canceling
blanchet@37583
   139
                val state' = make_state manager timeout_heap' active canceling' messages store;
blanchet@37583
   140
              in SOME (map #2 timeout_threads, state') end
blanchet@37583
   141
          end;
blanchet@37583
   142
      in
blanchet@37583
   143
        while Synchronized.change_result global_state
blanchet@37583
   144
          (fn state as {timeout_heap, active, canceling, messages, store, ...} =>
blanchet@37583
   145
            if null active andalso null canceling andalso null messages
blanchet@37583
   146
            then (false, make_state NONE timeout_heap active canceling messages store)
blanchet@37583
   147
            else (true, state))
blanchet@37583
   148
        do
blanchet@37583
   149
          (Synchronized.timed_access global_state (SOME o time_limit o #timeout_heap) action
blanchet@37583
   150
            |> these
blanchet@37583
   151
            |> List.app (unregister verbose "Timed out.\n");
blanchet@37585
   152
            print_new_messages ();
blanchet@37583
   153
            (*give threads some time to respond to interrupt*)
blanchet@37583
   154
            OS.Process.sleep min_wait_time)
blanchet@37583
   155
      end))
blanchet@37583
   156
    in make_state manager timeout_heap active canceling messages store end)
blanchet@37583
   157
blanchet@37583
   158
blanchet@37583
   159
(* register thread *)
blanchet@37583
   160
blanchet@37584
   161
fun register tool verbose birth_time death_time desc thread =
blanchet@37583
   162
 (Synchronized.change global_state
blanchet@37583
   163
    (fn {manager, timeout_heap, active, canceling, messages, store} =>
blanchet@37583
   164
      let
blanchet@37583
   165
        val timeout_heap' = Thread_Heap.insert (death_time, thread) timeout_heap;
blanchet@37585
   166
        val active' = update_thread (thread, (tool, birth_time, death_time, desc)) active;
blanchet@37583
   167
        val state' = make_state manager timeout_heap' active' canceling messages store;
blanchet@37583
   168
      in state' end);
blanchet@37585
   169
  check_thread_manager verbose);
blanchet@37583
   170
blanchet@37583
   171
blanchet@37584
   172
fun launch tool verbose birth_time death_time desc f =
blanchet@37584
   173
  (Toplevel.thread true
blanchet@37584
   174
       (fn () =>
blanchet@37584
   175
           let
blanchet@37584
   176
             val self = Thread.self ()
blanchet@37584
   177
             val _ = register tool verbose birth_time death_time desc self
blanchet@37584
   178
             val message = f ()
blanchet@37584
   179
           in unregister verbose message self end);
blanchet@37584
   180
   ())
blanchet@37584
   181
blanchet@37583
   182
blanchet@37583
   183
(** user commands **)
blanchet@37583
   184
blanchet@37583
   185
(* kill threads *)
blanchet@37583
   186
blanchet@37585
   187
fun kill_threads tool workers = Synchronized.change global_state
blanchet@37583
   188
  (fn {manager, timeout_heap, active, canceling, messages, store} =>
blanchet@37583
   189
    let
blanchet@37585
   190
      val killing =
blanchet@37585
   191
        map_filter (fn (th, (tool', _, _, desc)) =>
blanchet@37585
   192
                       if tool' = tool then SOME (th, (tool', Time.now (), desc))
blanchet@37585
   193
                       else NONE) active
blanchet@37583
   194
      val state' = make_state manager timeout_heap [] (killing @ canceling) messages store;
blanchet@37585
   195
      val _ = if null killing then () else priority ("Killed active " ^ workers ^ ".")
blanchet@37583
   196
    in state' end);
blanchet@37583
   197
blanchet@37583
   198
blanchet@37583
   199
(* running threads *)
blanchet@37583
   200
blanchet@37585
   201
fun seconds time = string_of_int (Time.toSeconds time) ^ " s"
blanchet@37583
   202
blanchet@37585
   203
fun running_threads tool workers =
blanchet@37583
   204
  let
blanchet@37583
   205
    val {active, canceling, ...} = Synchronized.value global_state;
blanchet@37583
   206
blanchet@37583
   207
    val now = Time.now ();
blanchet@37585
   208
    fun running_info (_, (tool', birth_time, death_time, desc)) =
blanchet@37585
   209
      if tool' = tool then
blanchet@37585
   210
        SOME ("Running: " ^ seconds (Time.- (now, birth_time)) ^ " -- " ^
blanchet@37585
   211
              seconds (Time.- (death_time, now)) ^ " to live:\n" ^ desc)
blanchet@37585
   212
      else
blanchet@37585
   213
        NONE
blanchet@37585
   214
    fun canceling_info (_, (tool', death_time, desc)) =
blanchet@37585
   215
      if tool' = tool then
blanchet@37585
   216
        SOME ("Trying to interrupt thread since " ^
blanchet@37585
   217
              seconds (Time.- (now, death_time)) ^ ":\n" ^ desc)
blanchet@37585
   218
      else
blanchet@37585
   219
        NONE
blanchet@37583
   220
    val running =
blanchet@37585
   221
      case map_filter running_info active of
blanchet@37585
   222
        [] => ["No " ^ workers ^ " running."]
blanchet@37585
   223
      | ss => "Running " ^ workers ^ ":" :: ss
blanchet@37583
   224
    val interrupting =
blanchet@37585
   225
      case map_filter canceling_info canceling of
blanchet@37583
   226
        [] => []
blanchet@37585
   227
      | ss => "Trying to interrupt the following " ^ workers ^ ":" :: ss
blanchet@37583
   228
  in priority (space_implode "\n\n" (running @ interrupting)) end
blanchet@37583
   229
blanchet@37585
   230
fun thread_messages tool worker opt_limit =
blanchet@37583
   231
  let
blanchet@37583
   232
    val limit = the_default message_display_limit opt_limit;
blanchet@37585
   233
    val tool_store = Synchronized.value global_state
blanchet@37585
   234
                     |> #store |> filter (curry (op =) tool o fst)
blanchet@37583
   235
    val header =
blanchet@37585
   236
      "Recent " ^ worker ^ " messages" ^
blanchet@37585
   237
        (if length tool_store <= limit then ":"
blanchet@37585
   238
         else " (" ^ string_of_int limit ^ " displayed):");
blanchet@37585
   239
  in List.app priority (header :: break_into_chunks (#1 (chop limit tool_store))) end
blanchet@37583
   240
blanchet@37583
   241
end;