1 (* Title: Pure/Thy/thy_info.ML
2 Author: Markus Wenzel, TU Muenchen
4 Global theory info database, with auto-loading according to theory and
10 datatype action = Update | Remove
11 val add_hook: (action -> string -> unit) -> unit
12 val get_names: unit -> string list
13 val lookup_theory: string -> theory option
14 val get_theory: string -> theory
15 val is_finished: string -> bool
16 val master_directory: string -> Path.T
17 val loaded_files: string -> Path.T list
18 val remove_thy: string -> unit
19 val kill_thy: string -> unit
20 val use_thys_wrt: Path.T -> string list -> unit
21 val use_thys: string list -> unit
22 val use_thy: string -> unit
23 val toplevel_begin_theory: string -> string list -> (Path.T * bool) list -> theory
24 val register_thy: theory -> unit
25 val finish: unit -> unit
28 structure Thy_Info: THY_INFO =
31 (** theory loader actions and hooks **)
33 datatype action = Update | Remove;
36 val hooks = Unsynchronized.ref ([]: (action -> string -> unit) list);
38 fun add_hook f = NAMED_CRITICAL "Thy_Info" (fn () => Unsynchronized.change hooks (cons f));
39 fun perform action name = List.app (fn f => (try (fn () => f action name) (); ())) (! hooks);
48 fun loader_msg txt [] = "Theory loader: " ^ txt
49 | loader_msg txt names = "Theory loader: " ^ txt ^ " " ^ commas_quote names;
51 val show_path = space_implode " via " o map quote;
52 fun cycle_msg names = loader_msg ("cyclic dependency of " ^ show_path names) [];
55 (* derived graph operations *)
57 fun add_deps name parents G = Graph.add_deps_acyclic (name, parents) G
58 handle Graph.CYCLES namess => error (cat_lines (map cycle_msg namess));
60 fun new_entry name parents entry =
61 Graph.new_node (name, entry) #> add_deps name parents;
67 {master: (Path.T * File.ident), (*master dependencies for thy file*)
68 parents: string list}; (*source specification of parents (partially qualified)*)
70 fun make_deps master parents : deps = {master = master, parents = parents};
72 fun master_dir (d: deps option) = the_default Path.current (Option.map (Path.dir o #1 o #master) d);
73 fun base_name s = Path.implode (Path.base (Path.explode s));
76 val database = Unsynchronized.ref (Graph.empty: (deps option * theory option) Graph.T);
78 fun get_thys () = ! database;
79 fun change_thys f = NAMED_CRITICAL "Thy_Info" (fn () => Unsynchronized.change database f);
83 (* access thy graph *)
85 fun thy_graph f x = f (get_thys ()) x;
87 fun get_names () = Graph.topological_order (get_thys ());
93 SOME (thy_graph Graph.get_node name) handle Graph.UNDEF _ => NONE;
95 val known_thy = is_some o lookup_thy;
98 (case lookup_thy name of
100 | NONE => error (loader_msg "nothing known about theory" [name]));
105 val lookup_deps = Option.map #1 o lookup_thy;
106 val get_deps = #1 o get_thy;
108 val is_finished = is_none o get_deps;
109 val master_directory = master_dir o get_deps;
111 fun get_parents name =
112 thy_graph Graph.imm_preds name handle Graph.UNDEF _ =>
113 error (loader_msg "nothing known about theory" [name]);
118 fun lookup_theory name =
119 (case lookup_thy name of
120 SOME (_, SOME theory) => SOME theory
123 fun get_theory name =
124 (case lookup_theory name of
125 SOME theory => theory
126 | _ => error (loader_msg "undefined theory entry for" [name]));
128 fun loaded_files name = NAMED_CRITICAL "Thy_Info" (fn () =>
129 (case get_deps name of
131 | SOME {master = (thy_path, _), ...} => thy_path :: Thy_Load.loaded_files (get_theory name)));
135 (** thy operations **)
139 fun remove_thy name = NAMED_CRITICAL "Thy_Info" (fn () =>
140 if is_finished name then error (loader_msg "attempt to change finished theory" [name])
143 val succs = thy_graph Graph.all_succs [name];
144 val _ = priority (loader_msg "removing" succs);
145 val _ = List.app (perform Remove) succs;
146 val _ = change_thys (Graph.del_nodes succs);
149 fun kill_thy name = NAMED_CRITICAL "Thy_Info" (fn () =>
150 if known_thy name then remove_thy name
154 (* scheduling loader tasks *)
157 Task of string list * (theory list -> (theory * (unit -> unit))) |
160 fun task_finished (Task _) = false
161 | task_finished (Finished _) = true;
165 fun schedule_seq task_graph =
166 ((Graph.topological_order task_graph, Symtab.empty) |-> fold (fn name => fn tab =>
167 (case Graph.get_node task_graph name of
168 Task (parents, body) =>
170 val (thy, after_load) = body (map (the o Symtab.lookup tab) parents);
171 val _ = after_load ();
172 in Symtab.update (name, thy) tab end
173 | Finished thy => Symtab.update (name, thy) tab))) |> ignore;
175 fun schedule_futures task_graph = uninterruptible (fn _ => fn () =>
177 val tasks = Graph.topological_order task_graph;
178 val futures = (tasks, Symtab.empty) |-> fold (fn name => fn tab =>
179 (case Graph.get_node task_graph name of
180 Task (parents, body) =>
182 val get = the o Symtab.lookup tab;
183 val deps = map (`get) (Graph.imm_preds task_graph name);
184 fun failed (future, parent) = if can Future.join future then NONE else SOME parent;
186 val future = Future.fork_deps (map #1 deps) (fn () =>
187 (case map_filter failed deps of
188 [] => body (map (#1 o Future.join o get) parents)
189 | bad => error (loader_msg
190 ("failed to load " ^ quote name ^ " (unresolved " ^ commas_quote bad ^ ")") [])));
191 in Symtab.update (name, future) tab end
192 | Finished thy => Symtab.update (name, Future.value (thy, I)) tab));
194 tasks |> maps (fn name =>
196 val (thy, after_load) = Future.join (the (Symtab.lookup futures name));
197 val _ = PureThy.join_proofs thy;
198 val _ = after_load ();
199 in [] end handle exn => [Exn.Exn exn])
200 |> rev |> Exn.release_all;
205 fun schedule_tasks tasks =
206 if not (Multithreading.enabled ()) then schedule_seq tasks
207 else if Multithreading.self_critical () then
208 (warning (loader_msg "no multithreading within critical section" []);
210 else schedule_futures tasks;
215 (* require_thy -- checking database entries wrt. the file-system *)
219 fun required_by _ [] = ""
220 | required_by s initiators = s ^ "(required by " ^ show_path (rev initiators) ^ ")";
222 fun load_thy initiators update_time (deps: deps) text name parent_thys =
224 val _ = kill_thy name;
225 val _ = priority ("Loading theory " ^ quote name ^ required_by " " initiators);
227 val {master = (thy_path, _), ...} = deps;
228 val dir = Path.dir thy_path;
229 val pos = Path.position thy_path;
230 val uses = map (apfst Path.explode) (#3 (Thy_Header.read pos text));
232 Thy_Load.begin_theory dir name parent_thys uses
233 |> Present.begin_theory update_time dir uses;
235 val (after_load, theory) = Outer_Syntax.load_thy name init pos text;
237 val parent_names = map Context.theory_name parent_thys;
240 NAMED_CRITICAL "Thy_Info" (fn () =>
241 (change_thys (new_entry name parent_names (SOME deps, SOME theory));
242 perform Update name)));
244 in (theory, after_load') end;
246 fun check_deps dir name =
247 (case lookup_deps name of
248 SOME NONE => (true, NONE, get_parents name, NONE)
250 let val {master, text, imports = parents, ...} = Thy_Load.deps_thy dir name
251 in (false, SOME (make_deps master parents), parents, SOME text) end
252 | SOME (SOME {master, parents}) =>
253 let val master' = Thy_Load.check_thy dir name in
254 if #2 master <> #2 master' then
255 let val {text = text', imports = parents', ...} = Thy_Load.deps_thy dir name;
256 in (false, SOME (make_deps master' parents'), parents', SOME text') end
260 (case lookup_theory name of
262 | SOME theory => Thy_Load.all_current theory);
263 val deps' = SOME (make_deps master' parents);
264 in (current, deps', parents, NONE) end
269 fun require_thys initiators dir strs tasks =
270 fold_map (require_thy initiators dir) strs tasks |>> forall I
271 and require_thy initiators dir str tasks =
273 val path = Path.expand (Path.explode str);
274 val name = Path.implode (Path.base path);
275 val dir' = Path.append dir (Path.dir path);
276 val _ = member (op =) initiators name andalso error (cycle_msg initiators);
278 (case try (Graph.get_node tasks) name of
279 SOME task => (task_finished task, tasks)
282 val (current, deps, parents, opt_text) = check_deps dir' name
283 handle ERROR msg => cat_error msg
284 (loader_msg "the error(s) above occurred while examining theory" [name] ^
285 required_by "\n" initiators);
287 val parent_names = map base_name parents;
288 val (parents_current, tasks') =
289 require_thys (name :: initiators) (Path.append dir (master_dir deps)) parents tasks;
291 val all_current = current andalso parents_current;
293 if all_current then Finished (get_theory name)
296 NONE => raise Fail "Malformed deps"
297 | SOME (dep as {master = (thy_path, _), ...}) =>
299 val text = (case opt_text of SOME text => text | NONE => File.read thy_path);
300 val update_time = serial ();
301 in Task (parent_names, load_thy initiators update_time dep text name) end);
302 in (all_current, new_entry name parent_names task tasks') end)
310 fun use_thys_wrt dir arg =
311 schedule_tasks (snd (require_thys [] dir arg Graph.empty));
313 val use_thys = use_thys_wrt Path.current;
314 val use_thy = use_thys o single;
317 (* toplevel begin theory -- without maintaining database *)
319 fun toplevel_begin_theory name imports uses =
321 val dir = Thy_Load.get_master_path ();
322 val _ = kill_thy name;
323 val _ = use_thys_wrt dir imports;
324 val parents = map (get_theory o base_name) imports;
325 in Thy_Load.begin_theory dir name parents uses end;
328 (* register theory *)
330 fun register_thy theory =
332 val name = Context.theory_name theory;
333 val master = Thy_Load.check_thy (Thy_Load.master_directory theory) name;
334 val parents = map Context.theory_name (Theory.parents_of theory);
335 val deps = make_deps master parents;
337 NAMED_CRITICAL "Thy_Info" (fn () =>
339 priority ("Registering theory " ^ quote name);
340 map get_theory parents;
341 change_thys (new_entry name parents (SOME deps, SOME theory));
342 perform Update name))
346 (* finish all theories *)
348 fun finish () = change_thys (Graph.map_nodes (fn (_, entry) => (NONE, entry)));