aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ide/coq.ml234
-rw-r--r--ide/coq.mli69
-rw-r--r--ide/coqide.ml333
-rw-r--r--ide/wg_Command.ml19
-rw-r--r--ide/wg_Command.mli3
5 files changed, 411 insertions, 247 deletions
diff --git a/ide/coq.ml b/ide/coq.ml
index bca972006e..1ce9379d2b 100644
--- a/ide/coq.ml
+++ b/ide/coq.ml
@@ -110,27 +110,64 @@ let check_connection args =
List.iter Minilib.safe_prerr_endline lines;
exit 1
+(** Useful stuff *)
+
+let atomically mtx f arg =
+ Mutex.lock mtx;
+ try
+ let ans = f arg in
+ Mutex.unlock mtx;
+ ans
+ with err ->
+ Mutex.unlock mtx;
+ raise err
+
+let ignore_error f arg =
+ try ignore (f arg) with _ -> ()
+
(** * The structure describing a coqtop sub-process *)
+type handle = {
+ pid : int;
+ (* Unix process id *)
+ cout : in_channel;
+ cin : out_channel;
+ mutable alive : bool;
+}
+
type coqtop = {
- pid : int; (* Unix process id *)
- cout : in_channel ;
- cin : out_channel ;
+ (* lock managing coqtop access *)
+ lock : Mutex.t;
+ (* non quoted command-line arguments of coqtop *)
sup_args : string list;
+ (* actual coqtop process *)
+ mutable handle : handle;
+ (* trigger called whenever coqtop dies abruptly *)
+ trigger : handle -> unit;
+ (* whether coqtop may be relaunched *)
+ mutable is_closed : bool;
+ (* whether coqtop is computing *)
+ mutable is_computing : bool;
+ (* whether coqtop is waiting to be resetted *)
+ mutable is_to_reset : bool;
}
+(** Invariants:
+ - any outside request takes the coqtop.lock and is discarded when
+ [is_closed = true].
+ - coqtop.handle may be written ONLY when toplvl_ctr_mtx AND coqtop.lock is
+ taken.
+*)
+
+exception DeadCoqtop
+
(** * Count of all active coqtops *)
let toplvl_ctr = ref 0
let toplvl_ctr_mtx = Mutex.create ()
-let coqtop_zombies () =
- Mutex.lock toplvl_ctr_mtx;
- let res = !toplvl_ctr in
- Mutex.unlock toplvl_ctr_mtx;
- res
-
+let coqtop_zombies () = !toplvl_ctr
(** * Starting / signaling / ending a real coqtop sub-process *)
@@ -161,38 +198,102 @@ let open_process_pid prog args =
set_binary_mode_in ic true;
(pid,ic,oc)
-let spawn_coqtop sup_args =
- Mutex.lock toplvl_ctr_mtx;
- try
- let prog = coqtop_path () in
- let args = Array.of_list (prog :: "-ideslave" :: sup_args) in
- let (pid,ic,oc) = open_process_pid prog args in
- incr toplvl_ctr;
- Mutex.unlock toplvl_ctr_mtx;
- { pid = pid; cin = oc; cout = ic ; sup_args = sup_args }
- with e ->
- Mutex.unlock toplvl_ctr_mtx;
- raise e
-
-let respawn_coqtop coqtop = spawn_coqtop coqtop.sup_args
+(** This launches a fresh handle from its command line arguments. *)
+let unsafe_spawn_handle args =
+ let prog = coqtop_path () in
+ let args = Array.of_list (prog :: "-ideslave" :: args) in
+ let (pid, ic, oc) = open_process_pid prog args in
+ incr toplvl_ctr;
+ {
+ pid = pid;
+ cin = oc;
+ cout = ic;
+ alive = true;
+ }
+
+(** This clears any potentially remaining open garbage. *)
+let unsafe_clear_handle coqtop =
+ let handle = coqtop.handle in
+ if handle.alive then begin
+ (* invalidate the old handle *)
+ handle.alive <- false;
+ ignore_error close_out handle.cin;
+ ignore_error close_in handle.cout;
+ ignore_error (Unix.waitpid []) handle.pid;
+ decr toplvl_ctr
+ end
+
+let spawn_coqtop hook sup_args =
+ let handle =
+ atomically toplvl_ctr_mtx unsafe_spawn_handle sup_args
+ in
+ {
+ handle = handle;
+ lock = Mutex.create ();
+ sup_args = sup_args;
+ trigger = hook;
+ is_closed = false;
+ is_computing = false;
+ is_to_reset = false;
+ }
let interrupter = ref (fun pid -> Unix.kill pid Sys.sigint)
let killer = ref (fun pid -> Unix.kill pid Sys.sigkill)
+let is_computing coqtop = coqtop.is_computing
+
+let is_closed coqtop = coqtop.is_closed
+
+(** These are asynchronous signals *)
let break_coqtop coqtop =
- try !interrupter coqtop.pid
+ try !interrupter coqtop.handle.pid
with _ -> prerr_endline "Error while sending Ctrl-C"
let kill_coqtop coqtop =
- let pid = coqtop.pid in
- begin
- try !killer pid
- with _ -> prerr_endline "Kill -9 failed. Process already terminated ?"
- end;
+ try !killer coqtop.handle.pid
+ with _ -> prerr_endline "Kill -9 failed. Process already terminated ?"
+
+let unsafe_process coqtop f =
+ coqtop.is_computing <- true;
try
- ignore (Unix.waitpid [] pid);
- Mutex.lock toplvl_ctr_mtx; decr toplvl_ctr; Mutex.unlock toplvl_ctr_mtx
- with _ -> prerr_endline "Error while waiting for child"
+ f coqtop.handle;
+ coqtop.is_computing <- false;
+ Mutex.unlock coqtop.lock
+ with
+ | DeadCoqtop ->
+ coqtop.is_computing <- false;
+ Mutex.lock toplvl_ctr_mtx;
+ (* Coqtop died from an non natural cause, we have the lock, so it's our duty
+ to relaunch it here. *)
+ if not coqtop.is_closed && not coqtop.is_to_reset then begin
+ ignore_error unsafe_clear_handle coqtop;
+ let try_respawn () =
+ coqtop.handle <- unsafe_spawn_handle coqtop.sup_args;
+ in
+ ignore_error try_respawn ();
+ (* If respawning coqtop failed, there is not much we can do... *)
+ assert (coqtop.handle.alive = true);
+ (* Process the reset callback *)
+ ignore_error coqtop.trigger coqtop.handle;
+ end;
+ Mutex.unlock toplvl_ctr_mtx;
+ Mutex.unlock coqtop.lock;
+ | err ->
+ (* Another error occured, we propagate it. *)
+ coqtop.is_computing <- false;
+ Mutex.unlock coqtop.lock;
+ raise err
+
+let grab coqtop f =
+ Mutex.lock coqtop.lock;
+ if not coqtop.is_closed && not coqtop.is_to_reset then unsafe_process coqtop f
+ else Mutex.unlock coqtop.lock
+
+let try_grab coqtop f g =
+ if Mutex.try_lock coqtop.lock then
+ if not coqtop.is_closed && not coqtop.is_to_reset then unsafe_process coqtop f
+ else Mutex.unlock coqtop.lock
+ else g ()
(** * Calls to coqtop *)
@@ -202,10 +303,22 @@ let p = Xml_parser.make ()
let () = Xml_parser.check_eof p false
let eval_call coqtop (c:'a Serialize.call) =
- Xml_utils.print_xml coqtop.cin (Serialize.of_call c);
- flush coqtop.cin;
- let xml = Xml_parser.parse p (Xml_parser.SChannel coqtop.cout) in
- (Serialize.to_answer xml : 'a Interface.value)
+ try
+ Xml_utils.print_xml coqtop.cin (Serialize.of_call c);
+ flush coqtop.cin;
+ let xml = Xml_parser.parse p (Xml_parser.SChannel coqtop.cout) in
+ (Serialize.to_answer xml : 'a Interface.value)
+ with
+ | Serialize.Marshal_error ->
+ (* the protocol was not respected... *)
+ raise Serialize.Marshal_error
+ | err ->
+ (* if anything else happens here, coqtop is most likely dead *)
+ let msg = Printf.sprintf "Error communicating with pid [%i]: %s"
+ coqtop.pid (Printexc.to_string err)
+ in
+ prerr_endline msg;
+ raise DeadCoqtop
let interp coqtop ?(raw=false) ?(verbose=true) s =
eval_call coqtop (Serialize.interp (raw,verbose,s))
@@ -215,6 +328,55 @@ let mkcases coqtop s = eval_call coqtop (Serialize.mkcases s)
let status coqtop = eval_call coqtop Serialize.status
let hints coqtop = eval_call coqtop Serialize.hints
+let unsafe_close coqtop =
+ if Mutex.try_lock coqtop.lock then begin
+ let () =
+ try
+ match eval_call coqtop.handle Serialize.quit with
+ | Interface.Good _ -> ()
+ | _ -> raise Exit
+ with err -> kill_coqtop coqtop
+ in
+ Mutex.unlock coqtop.lock
+ end else begin
+ (* bring me the chainsaw! *)
+ kill_coqtop coqtop
+ end
+
+let close_coqtop coqtop =
+ (* wait for acquiring the process management lock *)
+ atomically toplvl_ctr_mtx (fun _ -> coqtop.is_closed <- true) ();
+ (* try to quit coqtop gently *)
+ unsafe_close coqtop;
+ (* Finalize the handle *)
+ Mutex.lock coqtop.lock;
+ Mutex.lock toplvl_ctr_mtx;
+ ignore_error unsafe_clear_handle coqtop;
+ Mutex.unlock toplvl_ctr_mtx;
+ Mutex.unlock coqtop.lock
+
+let reset_coqtop coqtop hook =
+ (* wait for acquiring the process management lock *)
+ atomically toplvl_ctr_mtx (fun _ -> coqtop.is_to_reset <- true) ();
+ (* try to quit coqtop gently *)
+ unsafe_close coqtop;
+ (* Reset the handle *)
+ Mutex.lock coqtop.lock;
+ Mutex.lock toplvl_ctr_mtx;
+ ignore_error unsafe_clear_handle coqtop;
+ let try_respawn () =
+ coqtop.handle <- unsafe_spawn_handle coqtop.sup_args;
+ in
+ ignore_error try_respawn ();
+ (* If respawning coqtop failed, there is not much we can do... *)
+ assert (coqtop.handle.alive = true);
+ (* Reset done *)
+ coqtop.is_to_reset <- false;
+ (* Process the reset callback with the given hook *)
+ ignore_error hook coqtop.handle;
+ Mutex.unlock toplvl_ctr_mtx;
+ Mutex.unlock coqtop.lock
+
module PrintOpt =
struct
type t = string list
diff --git a/ide/coq.mli b/ide/coq.mli
index f2aa4abad5..454610b9b5 100644
--- a/ide/coq.mli
+++ b/ide/coq.mli
@@ -24,19 +24,60 @@ val check_connection : string list -> unit
(** * The structure describing a coqtop sub-process *)
-type coqtop
+(** Liveness management of coqtop is automatic. Whenever a Coqtop dies abruptly,
+ this module is responsible for relaunching the whole process. The hook
+ passed as an argument in coqtop construction will be called after such an
+ abrupt failure. In particular, it is NOT called when explicitely requesting
+ coqtop to close or to reset. *)
-(** * Count of all active coqtops *)
+type coqtop
+type handle
+(** Count of all active coqtops *)
val coqtop_zombies : unit -> int
(** * Starting / signaling / ending a real coqtop sub-process *)
-val spawn_coqtop : string list -> coqtop
-val respawn_coqtop : coqtop -> coqtop
-val kill_coqtop : coqtop -> unit
+(** Create a coqtop process out of a hook and some command-line arguments.
+ The hook SHALL NOT use [grab] or its variants, otherwise you'll deadlock! *)
+val spawn_coqtop : (handle -> unit) -> string list -> coqtop
+
+(** Interrupt the current computation of coqtop. Asynchronous. *)
val break_coqtop : coqtop -> unit
+(** Close coqtop. Subsequent requests will be discarded. Hook ignored. *)
+val close_coqtop : coqtop -> unit
+
+(** Reset coqtop. Pending requests will be discarded. Default hook ignored,
+ provided one used instead. *)
+val reset_coqtop : coqtop -> (handle -> unit) -> unit
+
+(** Last resort against a reluctant coqtop (a.k.a. chainsaw massacre).
+ Asynchronous. *)
+val kill_coqtop : coqtop -> unit
+
+(** * Coqtop commmunication *)
+
+(** Start a coqtop dialogue and ensure that there is no interfering process.
+ - If coqtop ever dies during the computation, this function restarts coqtop
+ and calls the restart hook with the fresh coqtop.
+ - If the argument function raises an exception, it is propagated.
+ - The request may be discarded if a [close_coqtop] or [reset_coqtop] occurs
+ before its completion.
+*)
+val grab : coqtop -> (handle -> unit) -> unit
+
+(** As [grab], but applies the second callback if coqtop is already busy. Please
+ consider using [try_grab] instead of [grab] except if you REALLY want
+ something to happen. *)
+val try_grab : coqtop -> (handle -> unit) -> (unit -> unit) -> unit
+
+(** Check if coqtop is computing. Does not take any lock. *)
+val is_computing : coqtop -> bool
+
+(** Check if coqtop is closed. Does not take any lock. *)
+val is_closed : coqtop -> bool
+
(** In win32, we'll use a different kill function than Unix.kill *)
val killer : (int -> unit) ref
@@ -45,14 +86,14 @@ val interrupter : (int -> unit) ref
(** * Calls to Coqtop, cf [Ide_intf] for more details *)
val interp :
- coqtop -> ?raw:bool -> ?verbose:bool -> string -> string Interface.value
-val rewind : coqtop -> int -> int Interface.value
-val status : coqtop -> Interface.status Interface.value
-val goals : coqtop -> Interface.goals option Interface.value
-val evars : coqtop -> Interface.evar list option Interface.value
-val hints : coqtop -> (Interface.hint list * Interface.hint) option Interface.value
-val inloadpath : coqtop -> string -> bool Interface.value
-val mkcases : coqtop -> string -> string list list Interface.value
+ handle -> ?raw:bool -> ?verbose:bool -> string -> string Interface.value
+val rewind : handle -> int -> int Interface.value
+val status : handle -> Interface.status Interface.value
+val goals : handle -> Interface.goals option Interface.value
+val evars : handle -> Interface.evar list option Interface.value
+val hints : handle -> (Interface.hint list * Interface.hint) option Interface.value
+val inloadpath : handle -> string -> bool Interface.value
+val mkcases : handle -> string -> string list list Interface.value
(** A specialized version of [raw_interp] dedicated to
set/unset options. *)
@@ -68,5 +109,5 @@ sig
val existential : t
val universes : t
- val set : coqtop -> (t * bool) list -> unit
+ val set : handle -> (t * bool) list -> unit
end
diff --git a/ide/coqide.ml b/ide/coqide.ml
index de70b94c9d..5b41e18283 100644
--- a/ide/coqide.ml
+++ b/ide/coqide.ml
@@ -44,38 +44,38 @@ object
method clear_message : unit
method get_insert : GText.iter
method get_start_of_input : GText.iter
- method go_to_insert : unit
+ method go_to_insert : Coq.handle -> unit
method go_to_next_occ_of_cur_word : unit
method go_to_prev_occ_of_cur_word : unit
- method tactic_wizard : string list -> unit
+ method tactic_wizard : Coq.handle -> string list -> unit
method insert_message : string -> unit
- method process_next_phrase : bool -> unit
- method process_until_end_or_error : unit
+ method process_next_phrase : Coq.handle -> bool -> unit
+ method process_until_end_or_error : Coq.handle -> unit
method recenter_insert : unit
- method reset_initial : unit
+ method reset_initial : Coq.handle -> unit
method force_reset_initial : unit
method set_message : string -> unit
- method raw_coq_query : string -> unit
- method show_goals : unit
- method undo_last_step : unit
+ method raw_coq_query : Coq.handle -> string -> unit
+ method show_goals : Coq.handle -> unit
+ method undo_last_step : Coq.handle -> unit
method help_for_keyword : unit -> unit
end
-type viewable_script =
- {script : Wg_ScriptView.script_view;
- tab_label : GMisc.label;
- proof_view : GText.view;
- message_view : GText.view;
- analyzed_view : _analyzed_view;
- toplvl : Coq.coqtop ref;
- command : Wg_Command.command_window;
- finder : Wg_Find.finder;
- }
+type viewable_script = {
+ script : Wg_ScriptView.script_view;
+ tab_label : GMisc.label;
+ proof_view : GText.view;
+ message_view : GText.view;
+ analyzed_view : _analyzed_view;
+ toplvl : Coq.coqtop;
+ command : Wg_Command.command_window;
+ finder : Wg_Find.finder;
+}
let kill_session s =
s.analyzed_view#kill_detached_views ();
- Coq.kill_coqtop !(s.toplvl)
+ Coq.close_coqtop s.toplvl
let build_session s =
let session_paned = GPack.paned `VERTICAL () in
@@ -188,16 +188,9 @@ let ignore_break () =
(** * Locks *)
-(* Locking machinery for Coq kernel *)
-let coq_computing = Mutex.create ()
-
(* To prevent Coq from interrupting during undoing...*)
let coq_may_stop = Mutex.create ()
-(* To prevent a force_reset_initial during a force_reset_initial *)
-let resetting = Mutex.create ()
-
-exception RestartCoqtop
exception Unsuccessful
let force_reset_initial () =
@@ -206,37 +199,18 @@ let force_reset_initial () =
let break () =
prerr_endline "User break received";
- Coq.break_coqtop !(session_notebook#current_term.toplvl)
+ Coq.break_coqtop session_notebook#current_term.toplvl
-let do_if_not_computing text f x =
+let do_if_not_computing term text f =
let threaded_task () =
- if Mutex.try_lock coq_computing then
- begin
- prerr_endline "Getting lock";
- List.iter
- (fun elt -> try f elt with
- | RestartCoqtop -> elt.analyzed_view#reset_initial
- | Sys_error str ->
- elt.analyzed_view#reset_initial;
- elt.analyzed_view#set_message
- ("Unable to communicate with coqtop, restarting coqtop.\n"^
- "Error was: "^str)
- | e ->
- Mutex.unlock coq_computing;
- elt.analyzed_view#set_message
- ("Unknown error, please report:\n"^(Printexc.to_string e)))
- x;
- prerr_endline "Releasing lock";
- Mutex.unlock coq_computing;
- end
- else
- prerr_endline "Discarded order (computations are ongoing)"
+ let info () = prerr_endline ("Discarded query:" ^ text) in
+ try Coq.try_grab term.toplvl f info
+ with
+ | e ->
+ let msg = "Unknown error, please report:\n" ^ (Printexc.to_string e) in
+ term.analyzed_view#set_message msg
in
prerr_endline ("Launching thread " ^ text);
- ignore (Glib.Timeout.add ~ms:300 ~callback:
- (fun () -> if Mutex.try_lock coq_computing
- then (Mutex.unlock coq_computing; false)
- else (pbar#pulse (); true)));
ignore (Thread.create threaded_task ())
let warning msg =
@@ -345,7 +319,7 @@ exception Stop of int
May raise [Coq_lex.Unterminated] when the zone ends with
an unterminated sentence. *)
-let split_slice_lax (buffer:GText.buffer) from upto =
+let split_slice_lax (buffer: GText.buffer) from upto =
buffer#remove_tag ~start:from ~stop:upto Tags.Script.comment_sentence;
buffer#remove_tag ~start:from ~stop:upto Tags.Script.sentence;
let slice = buffer#get_text ~start:from ~stop:upto () in
@@ -467,7 +441,7 @@ let sup_args = ref []
class analyzed_view (_script:Wg_ScriptView.script_view) (_pv:GText.view) (_mv:GText.view) _ct _fn =
object(self)
val input_view = _script
- val input_buffer = _script#buffer
+ val input_buffer = _script#source_buffer
val proof_view = _pv
val proof_buffer = _pv#buffer
val message_view = _mv
@@ -514,7 +488,7 @@ object(self)
input_buffer#set_modified false;
pop_info ();
flash_info "Buffer reverted";
- force_retag input_buffer;
+ force_retag (input_buffer :> GText.buffer);
with _ ->
pop_info ();
flash_info "Warning: could not revert buffer";
@@ -655,25 +629,25 @@ object(self)
val mutable full_goal_done = true
- method show_goals =
+ method show_goals handle =
if not full_goal_done then
proof_view#buffer#set_text "";
begin
let menu_callback = if current.contextual_menus_on_goal then
- (fun s () -> ignore (self#insert_this_phrase_on_success
+ (fun s () -> ignore (self#insert_this_phrase_on_success handle
true true false ("progress "^s) s))
else
(fun _ _ -> ()) in
try
- begin match Coq.goals !mycoqtop with
+ begin match Coq.goals handle with
| Interface.Fail (l, str) ->
self#set_message ("Error in coqtop :\n"^str)
| Interface.Good goals ->
- begin match Coq.evars !mycoqtop with
+ begin match Coq.evars handle with
| Interface.Fail (l, str) ->
self#set_message ("Error in coqtop :\n"^str)
| Interface.Good evs ->
- let hints = match Coq.hints !mycoqtop with
+ let hints = match Coq.hints handle with
| Interface.Fail (_, _) -> None
| Interface.Good hints -> hints
in
@@ -686,7 +660,7 @@ object(self)
| e -> prerr_endline (Printexc.to_string e)
end
- method private send_to_coq ct verbose phrase show_output show_error localize =
+ method private send_to_coq handle verbose phrase show_output show_error localize =
let display_output msg =
self#insert_message (if show_output then msg else "") in
let display_error (loc,s) =
@@ -711,23 +685,18 @@ object(self)
input_buffer#place_cursor ~where:starti)
end
end in
- try
full_goal_done <- false;
prerr_endline "Send_to_coq starting now";
(* It's important here to work with [ct] and not [!mycoqtop], otherwise
we could miss a restart of coqtop and continue sending it orders. *)
- match Coq.interp ct ~verbose phrase with
+ match Coq.interp handle ~verbose phrase with
| Interface.Fail (l,str) -> sync display_error (l,str); None
| Interface.Good msg -> sync display_output msg; Some Safe
(* TODO: Restore someday the access to Decl_mode.get_damon_flag,
and also detect the use of admit, and then return Unsafe *)
- with
- | End_of_file -> (* Coqtop has died, let's trigger a reset_initial. *)
- raise RestartCoqtop
- | e -> sync display_error (None, Printexc.to_string e); None
(* This method is intended to perform stateless commands *)
- method raw_coq_query phrase =
+ method raw_coq_query handle phrase =
let () = prerr_endline "raw_coq_query starting now" in
let display_error s =
if not (Glib.Utf8.validate s) then
@@ -738,11 +707,10 @@ object(self)
end
in
try
- match Coq.interp !mycoqtop ~raw:true ~verbose:false phrase with
+ match Coq.interp handle ~raw:true ~verbose:false phrase with
| Interface.Fail (_, err) -> sync display_error err
| Interface.Good msg -> sync self#insert_message msg
with
- | End_of_file -> raise RestartCoqtop
| e -> sync display_error (Printexc.to_string e)
method private find_phrase_starting_at (start:GText.iter) =
@@ -783,7 +751,7 @@ object(self)
| Interface.Fail (l, str) -> sync display_error (l, str)
| Interface.Good msg -> mark_processed start stop*)
- method private process_one_phrase ct verbosely do_highlight =
+ method private process_one_phrase handle verbosely do_highlight =
let get_next_phrase () =
self#clear_message;
prerr_endline "process_one_phrase starting now";
@@ -834,19 +802,17 @@ object(self)
| Some ((_,stop) as loc,phrase) ->
if stop#backward_char#has_tag Tags.Script.comment_sentence
then sync mark_processed Safe loc
- else try match self#send_to_coq ct verbosely phrase true true true with
+ else match self#send_to_coq handle verbosely phrase true true true with
| Some safe -> sync mark_processed safe loc
| None -> sync remove_tag loc; raise Unsuccessful
- with
- | RestartCoqtop -> sync remove_tag loc; raise RestartCoqtop
- method process_next_phrase verbosely =
+ method process_next_phrase handle verbosely =
try
- self#process_one_phrase !mycoqtop verbosely true;
- self#show_goals;
+ self#process_one_phrase handle verbosely true;
+ self#show_goals handle
with Unsuccessful -> ()
- method private insert_this_phrase_on_success
+ method private insert_this_phrase_on_success handle
show_output show_msg localize coqphrase insertphrase =
let mark_processed safe =
let stop = self#get_start_of_input in
@@ -864,7 +830,7 @@ object(self)
flags = [];
} in
Stack.push ide_payload cmd_stack;
- self#show_goals;
+ self#show_goals handle;
(*Auto insert save on success...
try (match Coq.get_current_goals () with
| [] ->
@@ -890,15 +856,13 @@ object(self)
| None -> ())
| _ -> ())
with _ -> ()*) in
- match self#send_to_coq !mycoqtop false coqphrase show_output show_msg localize with
+ match self#send_to_coq handle false coqphrase show_output show_msg localize with
| Some safe -> sync mark_processed safe; true
| None ->
- sync
- (fun _ -> self#insert_message ("Unsuccessfully tried: "^coqphrase))
- ();
+ sync (fun _ -> self#insert_message ("Unsuccessfully tried: "^coqphrase)) ();
false
- method private process_until_iter_or_error stop =
+ method private process_until_iter_or_error handle stop =
let stop' = `OFFSET stop#offset in
let start = self#get_start_of_input#copy in
let start' = `OFFSET start#offset in
@@ -917,7 +881,7 @@ object(self)
in
let unlock () =
sync (fun _ ->
- self#show_goals;
+ self#show_goals handle;
(* Start and stop might be invalid if an eol was added at eof *)
let start = input_buffer#get_iter start' in
let stop = input_buffer#get_iter stop' in
@@ -928,56 +892,44 @@ object(self)
instead of accessing multiple time [mycoqtop]. Otherwise a restart of
coqtop could go unnoticed, and the new coqtop could receive strange
things. *)
- let ct = !mycoqtop in
(try
while stop#compare (get_current()) >= 0
- do self#process_one_phrase ct false false done
+ do self#process_one_phrase handle false false done
with
| Unsuccessful -> ()
- | RestartCoqtop -> unlock (); raise RestartCoqtop);
+ );
unlock ();
pop_info()
- method process_until_end_or_error =
- self#process_until_iter_or_error input_buffer#end_iter
-
- method reset_initial =
- mycoqtop := Coq.respawn_coqtop !mycoqtop;
- self#include_file_dir_in_path;
- sync (fun () ->
- Stack.iter
- (function inf ->
- let start = input_buffer#get_iter_at_mark inf.start in
- let stop = input_buffer#get_iter_at_mark inf.stop in
- input_buffer#move_mark ~where:start (`NAME "start_of_input");
- input_buffer#remove_tag Tags.Script.processed ~start ~stop;
- input_buffer#remove_tag Tags.Script.unjustified ~start ~stop;
- input_buffer#delete_mark inf.start;
- input_buffer#delete_mark inf.stop;
- )
- cmd_stack;
- Stack.clear cmd_stack;
- self#clear_message) ()
+ method process_until_end_or_error handle =
+ self#process_until_iter_or_error handle input_buffer#end_iter
+
+ method reset_initial handle =
+ let start = input_buffer#start_iter in
+ (* clear the stack *)
+ Stack.clear cmd_stack;
+ (* reset the buffer *)
+ input_buffer#move_mark ~where:start (`NAME "start_of_input");
+ input_buffer#remove_tag Tags.Script.processed start input_buffer#end_iter;
+ input_buffer#remove_tag Tags.Script.unjustified start input_buffer#end_iter;
+ input_buffer#remove_tag Tags.Script.to_process start input_buffer#end_iter;
+ tag_on_insert (input_buffer :> GText.buffer);
+ (* clear the message view *)
+ self#clear_message;
+ (* apply the initial commands to coq *)
+ self#include_file_dir_in_path handle;
+ (* warn the user *)
+ warning "Coqtop died badly. Resetting."
method force_reset_initial =
- (* Do nothing if a force_reset_initial is already ongoing *)
- if Mutex.try_lock resetting then begin
- Coq.kill_coqtop !mycoqtop;
- (* If a computation is ongoing, an exception will trigger
- the reset_initial in do_if_not_computing, not here. *)
- if Mutex.try_lock coq_computing then begin
- self#reset_initial;
- Mutex.unlock coq_computing
- end;
- Mutex.unlock resetting
- end
+ Coq.reset_coqtop mycoqtop self#reset_initial
(* Internal method for dialoging with coqtop about a backtrack.
The ide's cmd_stack has already been cleared up to the desired point.
The [finish] function is used to handle minor differences between
[go_to_insert] and [undo_last_step] *)
- method private do_backtrack finish n =
+ method private do_backtrack handle finish n =
(* pop n more commands if coqtop has said so (e.g. for undoing a proof) *)
let rec n_pop n =
if n = 0 then ()
@@ -988,7 +940,7 @@ object(self)
then n_pop n
else n_pop (pred n)
in
- match Coq.rewind !mycoqtop n with
+ match Coq.rewind handle n with
| Interface.Good n ->
n_pop n;
sync (fun _ ->
@@ -999,7 +951,7 @@ object(self)
input_buffer#remove_tag Tags.Script.processed ~start ~stop;
input_buffer#remove_tag Tags.Script.unjustified ~start ~stop;
input_buffer#move_mark ~where:start (`NAME "start_of_input");
- self#show_goals;
+ self#show_goals handle;
self#clear_message;
finish start) ()
| Interface.Fail (l,str) ->
@@ -1008,7 +960,7 @@ object(self)
"CoqIDE and coqtop may be out of sync, you may want to use Restart.")
(* backtrack Coq to the phrase preceding iterator [i] *)
- method private backtrack_to_no_lock i =
+ method private backtrack_to_no_lock handle i =
prerr_endline "Backtracking_to iter starts now.";
full_goal_done <- false;
(* pop Coq commands until we reach iterator [i] *)
@@ -1026,9 +978,9 @@ object(self)
in
begin
try
- self#do_backtrack (fun _ -> ()) (n_step 0);
+ self#do_backtrack handle (fun _ -> ()) (n_step 0);
(* We may have backtracked too much: let's replay *)
- self#process_until_iter_or_error i
+ self#process_until_iter_or_error handle i
with _ ->
push_info
("WARNING: undo failed badly.\n" ^
@@ -1036,20 +988,20 @@ object(self)
"Please restart and report.");
end
- method private backtrack_to i =
+ method private backtrack_to handle i =
if Mutex.try_lock coq_may_stop then
(push_info "Undoing...";
- self#backtrack_to_no_lock i; Mutex.unlock coq_may_stop;
+ self#backtrack_to_no_lock handle i; Mutex.unlock coq_may_stop;
pop_info ())
else prerr_endline "backtrack_to : discarded (lock is busy)"
- method go_to_insert =
+ method go_to_insert handle =
let point = self#get_insert in
if point#compare self#get_start_of_input>=0
- then self#process_until_iter_or_error point
- else self#backtrack_to point
+ then self#process_until_iter_or_error handle point
+ else self#backtrack_to handle point
- method undo_last_step =
+ method undo_last_step handle =
full_goal_done <- false;
if Mutex.try_lock coq_may_stop then
(push_info "Undoing last step...";
@@ -1063,7 +1015,7 @@ object(self)
input_buffer#place_cursor ~where;
self#recenter_insert;
in
- self#do_backtrack finish count
+ self#do_backtrack handle finish count
with Stack.Empty -> ()
);
pop_info ();
@@ -1071,28 +1023,27 @@ object(self)
else prerr_endline "undo_last_step discarded"
- method tactic_wizard l =
- async(fun _ -> self#clear_message)();
+ method tactic_wizard handle l =
+ async(fun _ -> self#clear_message) ();
ignore
(List.exists
(fun p ->
- self#insert_this_phrase_on_success true false false
+ self#insert_this_phrase_on_success handle true false false
("progress "^p^".\n") (p^".\n")) l)
- method private include_file_dir_in_path =
+ method private include_file_dir_in_path handle =
match filename with
| None -> ()
| Some f ->
let dir = Filename.dirname f in
- let ct = !mycoqtop in
- match Coq.inloadpath ct dir with
+ match Coq.inloadpath handle dir with
| Interface.Fail (_,str) ->
self#set_message
("Could not determine lodpath, this might lead to problems:\n"^str)
| Interface.Good true -> ()
| Interface.Good false ->
let cmd = Printf.sprintf "Add LoadPath \"%s\". " dir in
- match Coq.interp ct cmd with
+ match Coq.interp handle cmd with
| Interface.Fail (l,str) ->
self#set_message ("Couln't add loadpath:\n"^str)
| Interface.Good _ -> ()
@@ -1178,7 +1129,7 @@ object(self)
Tags.Script.error
~start:self#get_start_of_input
~stop;
- tag_on_insert input_buffer
+ tag_on_insert (input_buffer :> GText.buffer)
)
);
ignore (input_buffer#add_selection_clipboard cb);
@@ -1203,7 +1154,12 @@ object(self)
prerr_endline "Should recenter : yes";
self#recenter_insert
end));
- self#include_file_dir_in_path;
+ let callback () =
+ if Coq.is_computing mycoqtop then pbar#pulse ();
+ not (Coq.is_closed mycoqtop);
+ in
+ ignore (Glib.Timeout.add ~ms:300 ~callback);
+ Coq.grab mycoqtop self#include_file_dir_in_path;
end
let last_make = ref "";;
@@ -1264,10 +1220,13 @@ let create_session file =
@(!sup_args)
|Subst_args -> Project_file.args_from_project the_file !custom_project_files current.project_file_name
in
- let ct = ref (Coq.spawn_coqtop coqtop_args) in
+ let reset = ref (fun _ -> ()) in
+ let trigger handle = !reset handle in
+ let ct = Coq.spawn_coqtop trigger coqtop_args in
let command = new Wg_Command.command_window ct in
let finder = new Wg_Find.finder (script :> GText.view) in
let legacy_av = new analyzed_view script proof message ct file in
+ let () = reset := legacy_av#reset_initial in
let () = legacy_av#update_stats in
let _ =
script#buffer#create_mark ~name:"start_of_input" script#buffer#start_iter in
@@ -1278,7 +1237,8 @@ let create_session file =
let _ =
GtkBase.Widget.add_events proof#as_widget [`ENTER_NOTIFY;`POINTER_MOTION] in
let () =
- List.iter (fun (opts,_,_,_,dflt) -> setopts !ct opts dflt) print_items in
+ Coq.grab ct (fun handle ->
+ List.iter (fun (opts,_,_,_,dflt) -> setopts handle opts dflt) print_items) in
let _ =
proof#event#connect#motion_notify ~callback:
(fun e ->
@@ -1507,7 +1467,7 @@ let load_file handler f =
with
| e -> handler ("Load failed: "^(Printexc.to_string e))
-let do_load = load_file flash_info
+let do_load file = load_file flash_info file
let saveall_f () =
List.iter
@@ -1712,7 +1672,7 @@ let main files =
(GMain.Timeout.add ~ms:current.global_auto_revert_delay
~callback:
(fun () ->
- do_if_not_computing "revert" (sync revert_f) session_notebook#pages;
+ List.iter (fun p -> do_if_not_computing p "revert" (fun _ -> sync revert_f p)) session_notebook#pages;
true))
in reset_revert_timer (); (* to enable statup preferences timer *)
(* XXX *)
@@ -1729,18 +1689,19 @@ let main files =
(GMain.Timeout.add ~ms:current.auto_save_delay
~callback:
(fun () ->
- do_if_not_computing "autosave" (sync auto_save_f) session_notebook#pages;
+ List.iter (fun p -> do_if_not_computing p "autosave" (fun _ -> sync auto_save_f p)) session_notebook#pages;
true))
in reset_auto_save_timer (); (* to enable statup preferences timer *)
(* end Preferences *)
let do_or_activate f () =
- do_if_not_computing "do_or_activate"
- (fun current ->
- let av = current.analyzed_view in
- ignore (f av);
+ let p = session_notebook#current_term in
+ do_if_not_computing p "do_or_activate"
+ (fun handle ->
+ let av = p.analyzed_view in
+ ignore (f handle av);
pop_info ();
- let msg = match Coq.status !(current.toplvl) with
+ let msg = match Coq.status handle with
| Interface.Fail (l, str) ->
"Oops, problem while fetching coq status."
| Interface.Good status ->
@@ -1756,17 +1717,17 @@ let main files =
in
push_info msg
)
- [session_notebook#current_term]
in
- let do_if_active f _ =
- do_if_not_computing "do_if_active"
- (fun sess -> ignore (f sess.analyzed_view))
- [session_notebook#current_term] in
+ let do_if_active f () =
+ let p = session_notebook#current_term in
+ do_if_not_computing p "do_if_active"
+ (fun handle -> ignore (f handle p.analyzed_view))
+ in
let match_callback _ =
let w = get_current_word () in
- let cur_ct = !(session_notebook#current_term.toplvl) in
+ let coqtop = session_notebook#current_term.toplvl in
try
- match Coq.mkcases cur_ct w with
+ Coq.try_grab coqtop begin fun handle -> match Coq.mkcases handle w with
| Interface.Fail _ -> raise Not_found
| Interface.Good cases ->
let print_branch c l =
@@ -1790,6 +1751,7 @@ let main files =
view#buffer#place_cursor ~where:i;
view#buffer#move_mark ~where:(i#backward_chars 3)
`SEL_BOUND
+ end ignore
with Not_found -> flash_info "Not an inductive type"
in
(* External command callback *)
@@ -1809,7 +1771,7 @@ let main files =
flash_info (f ^ " successfully compiled")
else begin
flash_info (f ^ " failed to compile");
- av#process_until_end_or_error;
+ Coq.try_grab v.toplvl av#process_until_end_or_error ignore;
av#insert_message "Compilation output:\n";
av#insert_message res
end
@@ -1913,14 +1875,16 @@ let main files =
in
let tactic_shortcut s sc = GAction.add_action s ~label:("_"^s)
~accel:(current.modifier_for_tactics^sc)
- ~callback:(do_if_active (fun a -> a#tactic_wizard [s])) in
+ ~callback:(fun _ -> do_if_active (fun handle a -> a#tactic_wizard handle [s]) ()) in
let query_callback command _ =
let word = get_current_word () in
if not (word = "") then
let term = session_notebook#current_term in
let query = command ^ " " ^ word ^ "." in
term.message_view#buffer#set_text "";
- term.analyzed_view#raw_coq_query query
+ Coq.try_grab term.toplvl
+ (fun handle -> term.analyzed_view#raw_coq_query handle query)
+ ignore
in
let query_shortcut s accel =
GAction.add_action s ~label:("_"^s) ?accel ~callback:(query_callback s)
@@ -1968,10 +1932,11 @@ let main files =
GAction.add_actions edit_actions [
GAction.add_action "Edit" ~label:"_Edit";
GAction.add_action "Undo" ~accel:"<Ctrl>u"
- ~callback:(fun _ -> do_if_not_computing "undo"
- (fun sess ->
- ignore (session_notebook#current_term.script#undo ()))
- [session_notebook#current_term]) ~stock:`UNDO;
+ ~callback:(fun _ ->
+ let term = session_notebook#current_term in
+ do_if_not_computing term "undo"
+ (fun handle ->
+ ignore (term.script#undo ()))) ~stock:`UNDO;
GAction.add_action "Redo" ~stock:`REDO
~callback:(fun _ -> ignore (session_notebook#current_term.script#redo ()));
GAction.add_action "Cut" ~callback:(fun _ -> GtkSignal.emit_unit
@@ -2038,26 +2003,26 @@ let main files =
(fun (opts,name,label,key,dflt) ->
GAction.add_toggle_action name ~active:dflt ~label
~accel:(current.modifier_for_display^key)
- ~callback:(fun v -> do_or_activate (fun a ->
- let () = setopts !(session_notebook#current_term.toplvl) opts v#get_active in
- a#show_goals) ()) view_actions)
+ ~callback:(fun v -> do_or_activate (fun handle av ->
+ let () = setopts handle opts v#get_active in
+ av#show_goals handle) ()) view_actions)
print_items;
GAction.add_actions navigation_actions [
GAction.add_action "Navigation" ~label:"_Navigation";
GAction.add_action "Forward" ~label:"_Forward" ~stock:`GO_DOWN
- ~callback:(fun _ -> do_or_activate (fun a -> a#process_next_phrase true) ())
+ ~callback:(fun _ -> do_or_activate (fun handle a -> a#process_next_phrase handle true) ())
~tooltip:"Forward one command" ~accel:(current.modifier_for_navigation^"Down");
GAction.add_action "Backward" ~label:"_Backward" ~stock:`GO_UP
- ~callback:(fun _ -> do_or_activate (fun a -> a#undo_last_step) ())
+ ~callback:(fun _ -> do_or_activate (fun handle a -> a#undo_last_step handle) ())
~tooltip:"Backward one command" ~accel:(current.modifier_for_navigation^"Up");
GAction.add_action "Go to" ~label:"_Go to" ~stock:`JUMP_TO
- ~callback:(fun _ -> do_or_activate (fun a -> a#go_to_insert) ())
+ ~callback:(fun _ -> do_or_activate (fun handle a -> a#go_to_insert handle) ())
~tooltip:"Go to cursor" ~accel:(current.modifier_for_navigation^"Right");
GAction.add_action "Start" ~label:"_Start" ~stock:`GOTO_TOP
~callback:(fun _ -> force_reset_initial ())
~tooltip:"Restart coq" ~accel:(current.modifier_for_navigation^"Home");
GAction.add_action "End" ~label:"_End" ~stock:`GOTO_BOTTOM
- ~callback:(fun _ -> do_or_activate (fun a -> a#process_until_end_or_error) ())
+ ~callback:(fun _ -> do_or_activate (fun handle a -> a#process_until_end_or_error handle) ())
~tooltip:"Go to end" ~accel:(current.modifier_for_navigation^"End");
GAction.add_action "Interrupt" ~label:"_Interrupt" ~stock:`STOP
~callback:(fun _ -> break ()) ~tooltip:"Interrupt computations"
@@ -2069,17 +2034,17 @@ let main files =
sess.analyzed_view#get_insert) ~tooltip:"Hide proof"
~accel:(current.modifier_for_navigation^"h");*)
GAction.add_action "Previous" ~label:"_Previous" ~stock:`GO_BACK
- ~callback:(fun _ -> do_or_activate (fun a -> a#go_to_prev_occ_of_cur_word) ())
+ ~callback:(fun _ -> do_or_activate (fun _ a -> a#go_to_prev_occ_of_cur_word) ())
~tooltip:"Previous occurence" ~accel:(current.modifier_for_navigation^"less");
GAction.add_action "Next" ~label:"_Next" ~stock:`GO_FORWARD
- ~callback:(fun _ -> do_or_activate (fun a -> a#go_to_next_occ_of_cur_word) ())
+ ~callback:(fun _ -> do_or_activate (fun _ a -> a#go_to_next_occ_of_cur_word) ())
~tooltip:"Next occurence" ~accel:(current.modifier_for_navigation^"greater");
];
GAction.add_actions tactics_actions [
GAction.add_action "Try Tactics" ~label:"_Try Tactics";
GAction.add_action "Wizard" ~tooltip:"Proof Wizard" ~label:"<Proof Wizard>"
- ~stock:`DIALOG_INFO ~callback:(do_if_active (fun a -> a#tactic_wizard
- current.automatic_tactics))
+ ~stock:`DIALOG_INFO ~callback:(fun _ -> do_if_active (fun handle a -> a#tactic_wizard handle
+ current.automatic_tactics) ())
~accel:(current.modifier_for_tactics^"dollar");
tactic_shortcut "auto" "a";
tactic_shortcut "auto with *" "asterisk";
@@ -2136,13 +2101,14 @@ let main files =
GAction.add_actions windows_actions [
GAction.add_action "Windows" ~label:"_Windows";
GAction.add_action "Detach View" ~label:"Detach _View"
- ~callback:(fun _ -> do_if_not_computing "detach view"
- (function {script=v;analyzed_view=av} ->
+ ~callback:(fun _ -> let p = session_notebook#current_term in
+ do_if_not_computing p "detach view"
+ (function handle ->
let w = GWindow.window ~show:true
~width:(current.window_width*2/3)
~height:(current.window_height*2/3)
~position:`CENTER
- ~title:(match av#filename with
+ ~title:(match p.analyzed_view#filename with
| None -> "*Unnamed*"
| Some f -> f)
()
@@ -2151,7 +2117,7 @@ let main files =
~packing:w#add ()
in
let nv = GText.view
- ~buffer:v#buffer
+ ~buffer:p.script#buffer
~packing:sb#add
()
in
@@ -2159,9 +2125,8 @@ let main files =
current.text_font;
ignore (w#connect#destroy
~callback:
- (fun () -> av#remove_detached_view w));
- av#add_detached_view w)
- [session_notebook#current_term]);
+ (fun () -> p.analyzed_view#remove_detached_view w));
+ p.analyzed_view#add_detached_view w));
];
GAction.add_actions help_actions [
GAction.add_action "Help" ~label:"_Help";
diff --git a/ide/wg_Command.ml b/ide/wg_Command.ml
index d52be74cb7..e15e1960b9 100644
--- a/ide/wg_Command.ml
+++ b/ide/wg_Command.ml
@@ -111,17 +111,14 @@ object(self)
if String.get com (String.length com - 1) = '.'
then com ^ " " else com ^ " " ^ entry#text ^" . "
in
- try
- result#buffer#set_text
- (match Coq.interp !coqtop ~raw:true phrase with
- | Interface.Fail (l,str) ->
- ("Error while interpreting "^phrase^":\n"^str)
- | Interface.Good results ->
- ("Result for command " ^ phrase ^ ":\n" ^ results))
- with e ->
- let s = Printexc.to_string e in
- assert (Glib.Utf8.validate s);
- result#buffer#set_text s
+ Coq.try_grab coqtop begin fun handle ->
+ result#buffer#set_text
+ (match Coq.interp handle ~raw:true phrase with
+ | Interface.Fail (l,str) ->
+ ("Error while interpreting "^phrase^":\n"^str)
+ | Interface.Good results ->
+ ("Result for command " ^ phrase ^ ":\n" ^ results))
+ end ignore
in
ignore (combo#entry#connect#activate ~callback:(on_activate callback));
ignore (ok_b#connect#clicked ~callback:(on_activate callback));
diff --git a/ide/wg_Command.mli b/ide/wg_Command.mli
index c1af683230..5f337e776f 100644
--- a/ide/wg_Command.mli
+++ b/ide/wg_Command.mli
@@ -6,8 +6,7 @@
(* * GNU Lesser General Public License Version 2.1 *)
(************************************************************************)
-class command_window :
- Coq.coqtop ref ->
+class command_window : Coq.coqtop ->
object
method new_command : ?command:string -> ?term:string -> unit -> unit
method frame : GBin.frame