| author | wenzelm | 
| Sun, 24 Aug 2025 20:26:02 +0200 | |
| changeset 83053 | c1ccd17fb70f | 
| parent 82920 | 75e7ca688f60 | 
| child 83205 | 99ce7933db6d | 
| permissions | -rw-r--r-- | 
| 69012 | 1 | /* Title: Pure/PIDE/headless.scala | 
| 67054 | 2 | Author: Makarius | 
| 3 | ||
| 69012 | 4 | Headless PIDE session and resources from file-system. | 
| 67054 | 5 | */ | 
| 6 | ||
| 7 | package isabelle | |
| 8 | ||
| 9 | ||
| 67925 | 10 | import java.io.{File => JFile}
 | 
| 11 | ||
| 68936 
90c08c7bab9c
continuously clean frontier of already committed theories: much less resource requirements;
 wenzelm parents: 
68935diff
changeset | 12 | import scala.annotation.tailrec | 
| 
90c08c7bab9c
continuously clean frontier of already committed theories: much less resource requirements;
 wenzelm parents: 
68935diff
changeset | 13 | |
| 67925 | 14 | |
| 75393 | 15 | object Headless {
 | 
| 69012 | 16 | /** session **/ | 
| 67061 
2efa25302f34
synchronous session start (similar to isabelle.vscode.Server);
 wenzelm parents: 
67059diff
changeset | 17 | |
| 68916 | 18 | private def stable_snapshot( | 
| 75393 | 19 | state: Document.State, | 
| 20 | version: Document.Version, | |
| 21 | name: Document.Node.Name | |
| 22 |   ): Document.Snapshot = {
 | |
| 68916 | 23 | val snapshot = state.snapshot(name) | 
| 24 | assert(version.id == snapshot.version.id) | |
| 25 | snapshot | |
| 26 | } | |
| 27 | ||
| 69013 | 28 | class Use_Theories_Result private[Headless]( | 
| 67883 | 29 | val state: Document.State, | 
| 67889 | 30 | val version: Document.Version, | 
| 68925 | 31 | val nodes: List[(Document.Node.Name, Document_Status.Node_Status)], | 
| 75393 | 32 | val nodes_committed: List[(Document.Node.Name, Document_Status.Node_Status)] | 
| 33 |   ) {
 | |
| 34 |     def nodes_pending: List[(Document.Node.Name, Document_Status.Node_Status)] = {
 | |
| 69032 
90bb4cabe1e8
clarified errors: no result from forced session.stop, check pending theories;
 wenzelm parents: 
69013diff
changeset | 35 | val committed = nodes_committed.iterator.map(_._1).toSet | 
| 
90bb4cabe1e8
clarified errors: no result from forced session.stop, check pending theories;
 wenzelm parents: 
69013diff
changeset | 36 | nodes.filter(p => !committed(p._1)) | 
| 
90bb4cabe1e8
clarified errors: no result from forced session.stop, check pending theories;
 wenzelm parents: 
69013diff
changeset | 37 | } | 
| 
90bb4cabe1e8
clarified errors: no result from forced session.stop, check pending theories;
 wenzelm parents: 
69013diff
changeset | 38 | |
| 68925 | 39 | def snapshot(name: Document.Node.Name): Document.Snapshot = | 
| 40 | stable_snapshot(state, version, name) | |
| 41 | ||
| 42 | def ok: Boolean = | |
| 43 |       (nodes.iterator ++ nodes_committed.iterator).forall({ case (_, st) => st.ok })
 | |
| 67879 | 44 | } | 
| 45 | ||
| 69520 | 46 | class Session private[Headless]( | 
| 47 | session_name: String, | |
| 48 | _session_options: => Options, | |
| 82744 
0ca8b1861fa3
clarified signature: more explicit subtypes of Session, with corresponding subtypes of Resources;
 wenzelm parents: 
82142diff
changeset | 49 | _resources: Headless.Resources) | 
| 82780 | 50 |   extends isabelle.Session {
 | 
| 69520 | 51 | session => | 
| 68694 
03e104be99af
added check_delay / check_limit for more robust treatment of structurally broken theory sources (or genuine non-termination);
 wenzelm parents: 
68365diff
changeset | 52 | |
| 82780 | 53 | override def session_options: Options = _session_options | 
| 54 | ||
| 82744 
0ca8b1861fa3
clarified signature: more explicit subtypes of Session, with corresponding subtypes of Resources;
 wenzelm parents: 
82142diff
changeset | 55 | override def resources: Headless.Resources = _resources | 
| 68694 
03e104be99af
added check_delay / check_limit for more robust treatment of structurally broken theory sources (or genuine non-termination);
 wenzelm parents: 
68365diff
changeset | 56 | |
| 70653 
f7c5b30fc432
load theories in stages, to reduce ML heap requirements;
 wenzelm parents: 
70649diff
changeset | 57 | |
| 69520 | 58 | /* options */ | 
| 59 | ||
| 70787 | 60 |     override def consolidate_delay: Time = session_options.seconds("headless_consolidate_delay")
 | 
| 61 |     override def prune_delay: Time = session_options.seconds("headless_prune_delay")
 | |
| 62 | ||
| 69520 | 63 |     def default_check_delay: Time = session_options.seconds("headless_check_delay")
 | 
| 64 |     def default_check_limit: Int = session_options.int("headless_check_limit")
 | |
| 65 |     def default_nodes_status_delay: Time = session_options.seconds("headless_nodes_status_delay")
 | |
| 66 |     def default_watchdog_timeout: Time = session_options.seconds("headless_watchdog_timeout")
 | |
| 67 |     def default_commit_cleanup_delay: Time = session_options.seconds("headless_commit_cleanup_delay")
 | |
| 67063 | 68 | |
| 77137 
79231a210f5d
observe option "show_states" in headless server (see also 951abf9db857);
 wenzelm parents: 
77112diff
changeset | 69 |     def show_states: Boolean = session_options.bool("show_states")
 | 
| 
79231a210f5d
observe option "show_states" in headless server (see also 951abf9db857);
 wenzelm parents: 
77112diff
changeset | 70 | |
| 68922 | 71 | |
| 72 | /* temporary directory */ | |
| 73 | ||
| 67925 | 74 |     val tmp_dir: JFile = Isabelle_System.tmp_dir("server_session")
 | 
| 67946 | 75 | val tmp_dir_name: String = File.path(tmp_dir).implode | 
| 67925 | 76 | |
| 68923 | 77 | def master_directory(master_dir: String): String = | 
| 78 | proper_string(master_dir) getOrElse tmp_dir_name | |
| 79 | ||
| 67945 | 80 | override def toString: String = session_name | 
| 81 | ||
| 75393 | 82 |     override def stop(): Process_Result = {
 | 
| 67925 | 83 |       try { super.stop() }
 | 
| 84 |       finally { Isabelle_System.rm_tree(tmp_dir) }
 | |
| 85 | } | |
| 86 | ||
| 67936 | 87 | |
| 88 | /* theories */ | |
| 89 | ||
| 75393 | 90 |     private object Load_State {
 | 
| 77709 | 91 | def finished: Load_State = Load_State(Nil, Nil, Space.zero) | 
| 70872 | 92 | |
| 93 | def count_file(name: Document.Node.Name): Long = | |
| 82920 | 94 | if (resources.loaded_theory(name)) 0 else File.size(name.path) | 
| 70872 | 95 | } | 
| 96 | ||
| 97 | private case class Load_State( | |
| 75393 | 98 | pending: List[Document.Node.Name], | 
| 99 | rest: List[Document.Node.Name], | |
| 77709 | 100 | load_limit: Space | 
| 75393 | 101 |     ) {
 | 
| 70872 | 102 | def next( | 
| 103 | dep_graph: Document.Node.Name.Graph[Unit], | |
| 76310 
c5c747ce46d2
proper update of non-committed theories (see also 2bf1d0e57695, 2a1583baaaa0);
 wenzelm parents: 
76309diff
changeset | 104 | consolidated: Document.Node.Name => Boolean | 
| 75393 | 105 |       ): (List[Document.Node.Name], Load_State) = {
 | 
| 106 | def load_requirements( | |
| 107 | pending1: List[Document.Node.Name], | |
| 108 | rest1: List[Document.Node.Name] | |
| 109 |         ) : (List[Document.Node.Name], Load_State) = {
 | |
| 76310 
c5c747ce46d2
proper update of non-committed theories (see also 2bf1d0e57695, 2a1583baaaa0);
 wenzelm parents: 
76309diff
changeset | 110 | val load_theories = dep_graph.all_preds_rev(pending1) | 
| 70872 | 111 | (load_theories, Load_State(pending1, rest1, load_limit)) | 
| 112 | } | |
| 113 | ||
| 76310 
c5c747ce46d2
proper update of non-committed theories (see also 2bf1d0e57695, 2a1583baaaa0);
 wenzelm parents: 
76309diff
changeset | 114 | if (!pending.forall(consolidated)) (Nil, this) | 
| 70872 | 115 | else if (rest.isEmpty) (Nil, Load_State.finished) | 
| 77709 | 116 | else if (!load_limit.is_proper) load_requirements(rest, Nil) | 
| 70872 | 117 |         else {
 | 
| 118 | val reachable = | |
| 77709 | 119 | dep_graph.reachable_limit( | 
| 120 | load_limit.bytes, Load_State.count_file, dep_graph.imm_preds, rest) | |
| 70872 | 121 | val (pending1, rest1) = rest.partition(reachable) | 
| 122 | load_requirements(pending1, rest1) | |
| 123 | } | |
| 124 | } | |
| 125 | } | |
| 126 | ||
| 68914 | 127 | private sealed case class Use_Theories_State( | 
| 70697 | 128 | dep_graph: Document.Node.Name.Graph[Unit], | 
| 70765 | 129 | load_state: Load_State, | 
| 70653 
f7c5b30fc432
load theories in stages, to reduce ML heap requirements;
 wenzelm parents: 
70649diff
changeset | 130 | watchdog_timeout: Time, | 
| 
f7c5b30fc432
load theories in stages, to reduce ML heap requirements;
 wenzelm parents: 
70649diff
changeset | 131 | commit: Option[(Document.Snapshot, Document_Status.Node_Status) => Unit], | 
| 68914 | 132 | last_update: Time = Time.now(), | 
| 133 | nodes_status: Document_Status.Nodes_Status = Document_Status.Nodes_Status.empty, | |
| 68925 | 134 | already_committed: Map[Document.Node.Name, Document_Status.Node_Status] = Map.empty, | 
| 76320 | 135 | changed_nodes: Set[Document.Node.Name] = Set.empty, | 
| 136 | changed_assignment: Boolean = false, | |
| 75393 | 137 | result: Option[Exn.Result[Use_Theories_Result]] = None | 
| 138 |     ) {
 | |
| 76316 | 139 | def nodes_status_update(state: Document.State, version: Document.Version, | 
| 140 | domain: Option[Set[Document.Node.Name]] = None, | |
| 76319 | 141 | trim: Boolean = false | 
| 76316 | 142 |       ): (Boolean, Use_Theories_State) = {
 | 
| 143 | val (nodes_status_changed, nodes_status1) = | |
| 144 | nodes_status.update(resources, state, version, domain = domain, trim = trim) | |
| 76319 | 145 | val st1 = copy(last_update = Time.now(), nodes_status = nodes_status1) | 
| 76316 | 146 | (nodes_status_changed, st1) | 
| 147 | } | |
| 68914 | 148 | |
| 76320 | 149 | def changed( | 
| 150 | nodes: IterableOnce[Document.Node.Name], | |
| 151 | assignment: Boolean | |
| 152 |       ): Use_Theories_State = {
 | |
| 153 | copy( | |
| 154 | changed_nodes = changed_nodes ++ nodes, | |
| 155 | changed_assignment = changed_assignment || assignment) | |
| 156 | } | |
| 157 | ||
| 158 | def reset_changed: Use_Theories_State = | |
| 159 | if (changed_nodes.isEmpty && !changed_assignment) this | |
| 160 | else copy(changed_nodes = Set.empty, changed_assignment = false) | |
| 161 | ||
| 70653 
f7c5b30fc432
load theories in stages, to reduce ML heap requirements;
 wenzelm parents: 
70649diff
changeset | 162 | def watchdog: Boolean = | 
| 68914 | 163 | watchdog_timeout > Time.zero && Time.now() - last_update > watchdog_timeout | 
| 164 | ||
| 70644 
b23a6dfcfd57
clarified state variable: avoid extra mutability via Promise;
 wenzelm parents: 
70640diff
changeset | 165 | def finished_result: Boolean = result.isDefined | 
| 
b23a6dfcfd57
clarified state variable: avoid extra mutability via Promise;
 wenzelm parents: 
70640diff
changeset | 166 | |
| 
b23a6dfcfd57
clarified state variable: avoid extra mutability via Promise;
 wenzelm parents: 
70640diff
changeset | 167 | def join_result: Option[(Exn.Result[Use_Theories_Result], Use_Theories_State)] = | 
| 
b23a6dfcfd57
clarified state variable: avoid extra mutability via Promise;
 wenzelm parents: 
70640diff
changeset | 168 | if (finished_result) Some((result.get, this)) else None | 
| 
b23a6dfcfd57
clarified state variable: avoid extra mutability via Promise;
 wenzelm parents: 
70640diff
changeset | 169 | |
| 
b23a6dfcfd57
clarified state variable: avoid extra mutability via Promise;
 wenzelm parents: 
70640diff
changeset | 170 | def cancel_result: Use_Theories_State = | 
| 
b23a6dfcfd57
clarified state variable: avoid extra mutability via Promise;
 wenzelm parents: 
70640diff
changeset | 171 | if (finished_result) this else copy(result = Some(Exn.Exn(Exn.Interrupt()))) | 
| 
b23a6dfcfd57
clarified state variable: avoid extra mutability via Promise;
 wenzelm parents: 
70640diff
changeset | 172 | |
| 75393 | 173 |       def clean_theories: (List[Document.Node.Name], Use_Theories_State) = {
 | 
| 174 | @tailrec def frontier( | |
| 175 | base: List[Document.Node.Name], | |
| 176 | front: Set[Document.Node.Name] | |
| 177 |         ) : Set[Document.Node.Name] = {
 | |
| 70698 | 178 | val add = base.filter(name => dep_graph.imm_succs(name).forall(front)) | 
| 179 | if (add.isEmpty) front | |
| 180 |           else {
 | |
| 181 | val preds = add.map(dep_graph.imm_preds) | |
| 73359 | 182 | val base1 = | 
| 183 | preds.tail.foldLeft(preds.head)(_ ++ _).toList.filter(already_committed.keySet) | |
| 70698 | 184 | frontier(base1, front ++ add) | 
| 185 | } | |
| 186 | } | |
| 187 | ||
| 70763 
5fae55752c70
tuned messages (again) -- avoid confusion wrt. total remaining size;
 wenzelm parents: 
70710diff
changeset | 188 | if (already_committed.isEmpty) (Nil, this) | 
| 70698 | 189 |         else {
 | 
| 70705 | 190 | val base = | 
| 191 |             (for {
 | |
| 192 | (name, (_, (_, succs))) <- dep_graph.iterator | |
| 193 | if succs.isEmpty && already_committed.isDefinedAt(name) | |
| 194 | } yield name).toList | |
| 195 | val clean = frontier(base, Set.empty) | |
| 70763 
5fae55752c70
tuned messages (again) -- avoid confusion wrt. total remaining size;
 wenzelm parents: 
70710diff
changeset | 196 | if (clean.isEmpty) (Nil, this) | 
| 70698 | 197 |           else {
 | 
| 70763 
5fae55752c70
tuned messages (again) -- avoid confusion wrt. total remaining size;
 wenzelm parents: 
70710diff
changeset | 198 | (dep_graph.topological_order.filter(clean), | 
| 70699 | 199 | copy(dep_graph = dep_graph.exclude(clean))) | 
| 70698 | 200 | } | 
| 201 | } | |
| 202 | } | |
| 70653 
f7c5b30fc432
load theories in stages, to reduce ML heap requirements;
 wenzelm parents: 
70649diff
changeset | 203 | |
| 76315 | 204 | private def consolidated( | 
| 205 | state: Document.State, | |
| 206 | version: Document.Version, | |
| 207 | name: Document.Node.Name | |
| 208 |       ): Boolean = {
 | |
| 82920 | 209 | resources.loaded_theory(name) || | 
| 76315 | 210 | nodes_status.quasi_consolidated(name) || | 
| 211 | (if (commit.isDefined) already_committed.isDefinedAt(name) | |
| 212 | else state.node_consolidated(version, name)) | |
| 213 | } | |
| 214 | ||
| 75393 | 215 | def check( | 
| 216 | state: Document.State, | |
| 217 | version: Document.Version, | |
| 218 | beyond_limit: Boolean | |
| 219 |       ) : (List[Document.Node.Name], Use_Theories_State) = {
 | |
| 76315 | 220 | val st1 = | 
| 70653 
f7c5b30fc432
load theories in stages, to reduce ML heap requirements;
 wenzelm parents: 
70649diff
changeset | 221 |           commit match {
 | 
| 76315 | 222 | case None => this | 
| 70653 
f7c5b30fc432
load theories in stages, to reduce ML heap requirements;
 wenzelm parents: 
70649diff
changeset | 223 | case Some(commit_fn) => | 
| 76315 | 224 | copy(already_committed = | 
| 225 |                 dep_graph.topological_order.foldLeft(already_committed) {
 | |
| 226 | case (committed, name) => | |
| 227 | def parents_committed: Boolean = | |
| 228 | version.nodes(name).header.imports.forall(parent => | |
| 82920 | 229 | resources.loaded_theory(parent) || committed.isDefinedAt(parent)) | 
| 76315 | 230 | if (!committed.isDefinedAt(name) && parents_committed && | 
| 231 |                         state.node_consolidated(version, name)) {
 | |
| 232 | val snapshot = stable_snapshot(state, version, name) | |
| 233 | val status = Document_Status.Node_Status.make(state, version, name) | |
| 234 | commit_fn(snapshot, status) | |
| 235 | committed + (name -> status) | |
| 236 | } | |
| 237 | else committed | |
| 238 | }) | |
| 68916 | 239 | } | 
| 240 | ||
| 76310 
c5c747ce46d2
proper update of non-committed theories (see also 2bf1d0e57695, 2a1583baaaa0);
 wenzelm parents: 
76309diff
changeset | 241 | def committed(name: Document.Node.Name): Boolean = | 
| 82920 | 242 | resources.loaded_theory(name) || st1.already_committed.isDefinedAt(name) | 
| 76310 
c5c747ce46d2
proper update of non-committed theories (see also 2bf1d0e57695, 2a1583baaaa0);
 wenzelm parents: 
76309diff
changeset | 243 | |
| 76323 
3637a0d06fe1
avoid result based on outdated state, e.g. relevant for use_theories with changed files;
 wenzelm parents: 
76320diff
changeset | 244 | val (load_theories0, load_state1) = | 
| 
3637a0d06fe1
avoid result based on outdated state, e.g. relevant for use_theories with changed files;
 wenzelm parents: 
76320diff
changeset | 245 | load_state.next(dep_graph, consolidated(state, version, _)) | 
| 
3637a0d06fe1
avoid result based on outdated state, e.g. relevant for use_theories with changed files;
 wenzelm parents: 
76320diff
changeset | 246 | |
| 
3637a0d06fe1
avoid result based on outdated state, e.g. relevant for use_theories with changed files;
 wenzelm parents: 
76320diff
changeset | 247 | val load_theories = load_theories0.filterNot(committed) | 
| 
3637a0d06fe1
avoid result based on outdated state, e.g. relevant for use_theories with changed files;
 wenzelm parents: 
76320diff
changeset | 248 | |
| 76320 | 249 |         val result1 = {
 | 
| 250 | val stopped = beyond_limit || watchdog | |
| 76323 
3637a0d06fe1
avoid result based on outdated state, e.g. relevant for use_theories with changed files;
 wenzelm parents: 
76320diff
changeset | 251 | if (!finished_result && load_theories.isEmpty && | 
| 76320 | 252 | (stopped || dep_graph.keys_iterator.forall(consolidated(state, version, _))) | 
| 76315 | 253 |           ) {
 | 
| 76320 | 254 | @tailrec def make_nodes( | 
| 255 | input: List[Document.Node.Name], | |
| 256 | output: List[(Document.Node.Name, Document_Status.Node_Status)] | |
| 257 |             ): Option[List[(Document.Node.Name, Document_Status.Node_Status)]] = {
 | |
| 258 |               input match {
 | |
| 259 | case name :: rest => | |
| 82920 | 260 | if (resources.loaded_theory(name)) make_nodes(rest, output) | 
| 76320 | 261 |                   else {
 | 
| 262 | val status = Document_Status.Node_Status.make(state, version, name) | |
| 76328 | 263 | val ok = if (commit.isDefined) committed(name) else status.consolidated | 
| 264 | if (stopped || ok) make_nodes(rest, (name -> status) :: output) else None | |
| 76320 | 265 | } | 
| 266 | case Nil => Some(output) | |
| 267 | } | |
| 268 | } | |
| 269 | ||
| 270 |             for (nodes <- make_nodes(dep_graph.topological_order.reverse, Nil)) yield {
 | |
| 271 | val nodes_committed = | |
| 272 |                 (for {
 | |
| 273 | name <- dep_graph.keys_iterator | |
| 274 | status <- st1.already_committed.get(name) | |
| 275 | } yield name -> status).toList | |
| 276 | Exn.Res(new Use_Theories_Result(state, version, nodes, nodes_committed)) | |
| 277 | } | |
| 70644 
b23a6dfcfd57
clarified state variable: avoid extra mutability via Promise;
 wenzelm parents: 
70640diff
changeset | 278 | } | 
| 
b23a6dfcfd57
clarified state variable: avoid extra mutability via Promise;
 wenzelm parents: 
70640diff
changeset | 279 | else result | 
| 76320 | 280 | } | 
| 68925 | 281 | |
| 76323 
3637a0d06fe1
avoid result based on outdated state, e.g. relevant for use_theories with changed files;
 wenzelm parents: 
76320diff
changeset | 282 | (load_theories, st1.copy(result = result1, load_state = load_state1)) | 
| 68916 | 283 | } | 
| 68914 | 284 | } | 
| 285 | ||
| 67064 
fb487246ef4f
synchronous use_theories, based on consolidated_state;
 wenzelm parents: 
67063diff
changeset | 286 | def use_theories( | 
| 67940 
b4e80f062fbf
clarified signature -- eliminated somewhat pointless positions;
 wenzelm parents: 
67939diff
changeset | 287 | theories: List[String], | 
| 67064 
fb487246ef4f
synchronous use_theories, based on consolidated_state;
 wenzelm parents: 
67063diff
changeset | 288 | qualifier: String = Sessions.DRAFT, | 
| 67881 | 289 | master_dir: String = "", | 
| 69920 | 290 | unicode_symbols: Boolean = false, | 
| 68943 | 291 | check_delay: Time = default_check_delay, | 
| 69520 | 292 | check_limit: Int = default_check_limit, | 
| 68947 | 293 | watchdog_timeout: Time = default_watchdog_timeout, | 
| 68943 | 294 | nodes_status_delay: Time = default_nodes_status_delay, | 
| 69458 | 295 | id: UUID.T = UUID.random(), | 
| 68916 | 296 | // commit: must not block, must not fail | 
| 297 | commit: Option[(Document.Snapshot, Document_Status.Node_Status) => Unit] = None, | |
| 68981 | 298 | commit_cleanup_delay: Time = default_commit_cleanup_delay, | 
| 75393 | 299 | progress: Progress = new Progress | 
| 300 |     ): Use_Theories_Result = {
 | |
| 301 |       val dependencies = {
 | |
| 68894 
1dbdad1b57a5
more robust: load_theories after consumer is installed;
 wenzelm parents: 
68888diff
changeset | 302 | val import_names = | 
| 68923 | 303 | theories.map(thy => | 
| 304 | resources.import_name(qualifier, master_directory(master_dir), thy) -> Position.none) | |
| 69562 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 305 | resources.dependencies(import_names, progress = progress).check_errors | 
| 68894 
1dbdad1b57a5
more robust: load_theories after consumer is installed;
 wenzelm parents: 
68888diff
changeset | 306 | } | 
| 69562 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 307 | val dep_theories = dependencies.theories | 
| 70653 
f7c5b30fc432
load theories in stages, to reduce ML heap requirements;
 wenzelm parents: 
70649diff
changeset | 308 | val dep_theories_set = dep_theories.toSet | 
| 76886 
f405fcc3db33
clarified signature: avoid somewhat fragile Document.Node.Name.master_dir_path;
 wenzelm parents: 
76858diff
changeset | 309 | val dep_files = dependencies.loaded_files | 
| 67064 
fb487246ef4f
synchronous use_theories, based on consolidated_state;
 wenzelm parents: 
67063diff
changeset | 310 | |
| 75393 | 311 |       val use_theories_state = {
 | 
| 70797 | 312 | val dep_graph = dependencies.theory_graph | 
| 313 | ||
| 314 | val maximals = dep_graph.maximals | |
| 315 | val rest = | |
| 316 | if (maximals.isEmpty || maximals.tail.isEmpty) maximals | |
| 317 |           else {
 | |
| 70800 | 318 | val depth = dep_graph.node_depth(Load_State.count_file) | 
| 70797 | 319 | maximals.sortBy(node => - depth(node)) | 
| 320 | } | |
| 70800 | 321 | val load_limit = | 
| 77709 | 322 |           if (commit.isDefined) Space.MiB(session_options.real("headless_load_limit"))
 | 
| 323 | else Space.zero | |
| 70798 | 324 | val load_state = Load_State(Nil, rest, load_limit) | 
| 70797 | 325 | |
| 326 | Synchronized(Use_Theories_State(dep_graph, load_state, watchdog_timeout, commit)) | |
| 327 | } | |
| 67064 
fb487246ef4f
synchronous use_theories, based on consolidated_state;
 wenzelm parents: 
67063diff
changeset | 328 | |
| 76320 | 329 | def check_state( | 
| 330 | beyond_limit: Boolean = false, | |
| 331 | state: Document.State = session.get_state() | |
| 332 |       ): Unit = {
 | |
| 70797 | 333 |         for {
 | 
| 334 | version <- state.stable_tip_version | |
| 335 | load_theories = use_theories_state.change_result(_.check(state, version, beyond_limit)) | |
| 336 | if load_theories.nonEmpty | |
| 337 | } resources.load_theories(session, id, load_theories, dep_files, unicode_symbols, progress) | |
| 67064 
fb487246ef4f
synchronous use_theories, based on consolidated_state;
 wenzelm parents: 
67063diff
changeset | 338 | } | 
| 
fb487246ef4f
synchronous use_theories, based on consolidated_state;
 wenzelm parents: 
67063diff
changeset | 339 | |
| 76311 
fab6568b119d
more robust: active consumer for check_state/check_progress;
 wenzelm parents: 
76310diff
changeset | 340 |       lazy val check_progress = {
 | 
| 68694 
03e104be99af
added check_delay / check_limit for more robust treatment of structurally broken theory sources (or genuine non-termination);
 wenzelm parents: 
68365diff
changeset | 341 | var check_count = 0 | 
| 75393 | 342 |         Event_Timer.request(Time.now(), repeat = Some(check_delay)) {
 | 
| 343 | if (progress.stopped) use_theories_state.change(_.cancel_result) | |
| 344 |           else {
 | |
| 345 | check_count += 1 | |
| 346 | check_state(check_limit > 0 && check_count > check_limit) | |
| 68694 
03e104be99af
added check_delay / check_limit for more robust treatment of structurally broken theory sources (or genuine non-termination);
 wenzelm parents: 
68365diff
changeset | 347 | } | 
| 75393 | 348 | } | 
| 68694 
03e104be99af
added check_delay / check_limit for more robust treatment of structurally broken theory sources (or genuine non-termination);
 wenzelm parents: 
68365diff
changeset | 349 | } | 
| 67894 
fee080c4045f
more robust check_state loop, even without session activity (e.g. idempotent use_theories);
 wenzelm parents: 
67893diff
changeset | 350 | |
| 75393 | 351 |       val consumer = {
 | 
| 68906 | 352 | val delay_nodes_status = | 
| 71704 | 353 |           Delay.first(nodes_status_delay max Time.zero) {
 | 
| 69818 
60d0ee8f2ddb
more robust: avoid potentially unrelated snapshot for the sake of is_suppressed;
 wenzelm parents: 
69817diff
changeset | 354 | progress.nodes_status(use_theories_state.value.nodes_status) | 
| 68906 | 355 | } | 
| 68770 
add44e2b8cb0
optional notification of nodes_status (via progress);
 wenzelm parents: 
68758diff
changeset | 356 | |
| 68936 
90c08c7bab9c
continuously clean frontier of already committed theories: much less resource requirements;
 wenzelm parents: 
68935diff
changeset | 357 | val delay_commit_clean = | 
| 71704 | 358 |           Delay.first(commit_cleanup_delay max Time.zero) {
 | 
| 70763 
5fae55752c70
tuned messages (again) -- avoid confusion wrt. total remaining size;
 wenzelm parents: 
70710diff
changeset | 359 | val clean_theories = use_theories_state.change_result(_.clean_theories) | 
| 76330 | 360 |             if (clean_theories.nonEmpty && session.is_ready) {
 | 
| 70770 | 361 |               progress.echo("Removing " + clean_theories.length + " theories ...")
 | 
| 70702 | 362 | resources.clean_theories(session, id, clean_theories) | 
| 363 | } | |
| 68936 
90c08c7bab9c
continuously clean frontier of already committed theories: much less resource requirements;
 wenzelm parents: 
68935diff
changeset | 364 | } | 
| 
90c08c7bab9c
continuously clean frontier of already committed theories: much less resource requirements;
 wenzelm parents: 
68935diff
changeset | 365 | |
| 76320 | 366 |         isabelle.Session.Consumer[isabelle.Session.Commands_Changed](getClass.getName) { changed =>
 | 
| 367 | val state = session.get_state() | |
| 368 | ||
| 369 | def apply_changed(st: Use_Theories_State): Use_Theories_State = | |
| 370 | st.changed(changed.nodes.iterator.filter(dep_theories_set), changed.assignment) | |
| 68770 
add44e2b8cb0
optional notification of nodes_status (via progress);
 wenzelm parents: 
68758diff
changeset | 371 | |
| 76320 | 372 |           state.stable_tip_version match {
 | 
| 373 | case None => use_theories_state.change(apply_changed) | |
| 374 | case Some(version) => | |
| 76329 | 375 | val theory_progress = | 
| 75394 | 376 |                 use_theories_state.change_result { st =>
 | 
| 76320 | 377 | val changed_st = apply_changed(st) | 
| 378 | ||
| 75393 | 379 | val domain = | 
| 380 | if (st.nodes_status.is_empty) dep_theories_set | |
| 76320 | 381 | else changed_st.changed_nodes | 
| 68899 | 382 | |
| 76316 | 383 | val (nodes_status_changed, st1) = | 
| 76320 | 384 | st.reset_changed.nodes_status_update(state, version, | 
| 385 | domain = Some(domain), trim = changed_st.changed_assignment) | |
| 68899 | 386 | |
| 75393 | 387 |                   if (nodes_status_delay >= Time.zero && nodes_status_changed) {
 | 
| 388 | delay_nodes_status.invoke() | |
| 389 | } | |
| 68883 
3653b3ad729e
clarified Thy_Resources.Session.use_theories: "terminated" node status is sufficient;
 wenzelm parents: 
68771diff
changeset | 390 | |
| 75393 | 391 | val theory_progress = | 
| 392 |                     (for {
 | |
| 76716 
a7602257a825
clarified state document nodes for Theories_Status / Document_Dockable;
 wenzelm parents: 
76702diff
changeset | 393 | (name, node_status) <- st1.nodes_status.present().iterator | 
| 
a7602257a825
clarified state document nodes for Theories_Status / Document_Dockable;
 wenzelm parents: 
76702diff
changeset | 394 | if !node_status.is_empty && changed_st.changed_nodes(name) && | 
| 
a7602257a825
clarified state document nodes for Theories_Status / Document_Dockable;
 wenzelm parents: 
76702diff
changeset | 395 | !st.already_committed.isDefinedAt(name) | 
| 75393 | 396 | p1 = node_status.percentage | 
| 75742 | 397 | if p1 > 0 && !st.nodes_status.get(name).map(_.percentage).contains(p1) | 
| 75393 | 398 | } yield Progress.Theory(name.theory, percentage = Some(p1))).toList | 
| 68903 | 399 | |
| 76329 | 400 |                   if (commit.isDefined && commit_cleanup_delay > Time.zero) {
 | 
| 401 | if (st1.finished_result) delay_commit_clean.revoke() | |
| 402 | else delay_commit_clean.invoke() | |
| 403 | } | |
| 404 | ||
| 405 | (theory_progress, st1) | |
| 75394 | 406 | } | 
| 68330 | 407 | |
| 71601 | 408 | theory_progress.foreach(progress.theory) | 
| 68903 | 409 | |
| 76320 | 410 | check_state(state = state) | 
| 411 | } | |
| 67064 
fb487246ef4f
synchronous use_theories, based on consolidated_state;
 wenzelm parents: 
67063diff
changeset | 412 | } | 
| 68906 | 413 | } | 
| 67064 
fb487246ef4f
synchronous use_theories, based on consolidated_state;
 wenzelm parents: 
67063diff
changeset | 414 | |
| 67892 | 415 |       try {
 | 
| 416 | session.commands_changed += consumer | |
| 76311 
fab6568b119d
more robust: active consumer for check_state/check_progress;
 wenzelm parents: 
76310diff
changeset | 417 | check_progress | 
| 70644 
b23a6dfcfd57
clarified state variable: avoid extra mutability via Promise;
 wenzelm parents: 
70640diff
changeset | 418 | use_theories_state.guarded_access(_.join_result) | 
| 73367 | 419 | check_progress.cancel() | 
| 67892 | 420 | } | 
| 421 |       finally {
 | |
| 68907 | 422 | session.commands_changed -= consumer | 
| 67892 | 423 | resources.unload_theories(session, id, dep_theories) | 
| 424 | } | |
| 67884 
43af581d7d8e
unload_theories after consolidation -- reset node_required;
 wenzelm parents: 
67883diff
changeset | 425 | |
| 70644 
b23a6dfcfd57
clarified state variable: avoid extra mutability via Promise;
 wenzelm parents: 
70640diff
changeset | 426 | Exn.release(use_theories_state.guarded_access(_.join_result)) | 
| 67064 
fb487246ef4f
synchronous use_theories, based on consolidated_state;
 wenzelm parents: 
67063diff
changeset | 427 | } | 
| 67936 | 428 | |
| 67939 | 429 | def purge_theories( | 
| 68915 | 430 | theories: List[String], | 
| 431 | qualifier: String = Sessions.DRAFT, | |
| 432 | master_dir: String = "", | |
| 75393 | 433 | all: Boolean = false | 
| 434 |     ): (List[Document.Node.Name], List[Document.Node.Name]) = {
 | |
| 68923 | 435 | val nodes = | 
| 436 | if (all) None | |
| 437 | else Some(theories.map(resources.import_name(qualifier, master_directory(master_dir), _))) | |
| 75743 | 438 | resources.purge_theories(nodes) | 
| 68915 | 439 | } | 
| 67063 | 440 | } | 
| 441 | ||
| 67061 
2efa25302f34
synchronous session start (similar to isabelle.vscode.Server);
 wenzelm parents: 
67059diff
changeset | 442 | |
| 67054 | 443 | |
| 69012 | 444 | /** resources **/ | 
| 68922 | 445 | |
| 75393 | 446 |   object Resources {
 | 
| 76656 | 447 | def apply( | 
| 448 | options: Options, | |
| 76657 | 449 | session_background: Sessions.Background, | 
| 79777 | 450 | log: Logger = new Logger | 
| 76657 | 451 | ): Resources = new Resources(options, session_background, log = log) | 
| 69536 | 452 | |
| 453 | def make( | |
| 454 | options: Options, | |
| 455 | session_name: String, | |
| 456 | session_dirs: List[Path] = Nil, | |
| 457 | include_sessions: List[String] = Nil, | |
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71704diff
changeset | 458 | progress: Progress = new Progress, | 
| 79777 | 459 | log: Logger = new Logger | 
| 75393 | 460 |     ): Resources = {
 | 
| 76657 | 461 | val session_background = | 
| 76656 | 462 | Sessions.background(options, session_name, dirs = session_dirs, | 
| 69536 | 463 | include_sessions = include_sessions, progress = progress) | 
| 76657 | 464 | apply(options, session_background, log = log) | 
| 69536 | 465 | } | 
| 466 | ||
| 69012 | 467 | final class Theory private[Headless]( | 
| 468 | val node_name: Document.Node.Name, | |
| 469 | val node_header: Document.Node.Header, | |
| 470 | val text: String, | |
| 75393 | 471 | val node_required: Boolean | 
| 472 |     ) {
 | |
| 69012 | 473 | override def toString: String = node_name.toString | 
| 68922 | 474 | |
| 76702 | 475 | def node_perspective: Document.Node.Perspective_Text.T = | 
| 69012 | 476 | Document.Node.Perspective(node_required, Text.Perspective.empty, Document.Node.Overlays.empty) | 
| 68922 | 477 | |
| 69012 | 478 | def make_edits(text_edits: List[Text.Edit]): List[Document.Edit_Text] = | 
| 479 | List(node_name -> Document.Node.Deps(node_header), | |
| 480 | node_name -> Document.Node.Edits(text_edits), | |
| 481 | node_name -> node_perspective) | |
| 68922 | 482 | |
| 75393 | 483 |       def node_edits(old: Option[Theory]): List[Document.Edit_Text] = {
 | 
| 69012 | 484 | val (text_edits, old_required) = | 
| 485 | if (old.isEmpty) (Text.Edit.inserts(0, text), false) | |
| 486 | else (Text.Edit.replace(0, old.get.text, text), old.get.node_required) | |
| 67887 
a4d5342898b1
unload_theories: actually observe required state;
 wenzelm parents: 
67885diff
changeset | 487 | |
| 69012 | 488 | if (text_edits.isEmpty && node_required == old_required) Nil | 
| 489 | else make_edits(text_edits) | |
| 490 | } | |
| 67887 
a4d5342898b1
unload_theories: actually observe required state;
 wenzelm parents: 
67885diff
changeset | 491 | |
| 69012 | 492 | def purge_edits: List[Document.Edit_Text] = | 
| 493 | make_edits(Text.Edit.removes(0, text)) | |
| 67936 | 494 | |
| 76312 | 495 | def set_required(required: Boolean): Theory = | 
| 69012 | 496 | if (required == node_required) this | 
| 497 | else new Theory(node_name, node_header, text, required) | |
| 67936 | 498 | } | 
| 499 | ||
| 69012 | 500 | sealed case class State( | 
| 76904 
e27d097d7d15
tuned signature: avoid confusion with Document.Node.Blob and Command.Blob;
 wenzelm parents: 
76902diff
changeset | 501 | blobs: Map[Document.Node.Name, Document.Blobs.Item] = Map.empty, | 
| 69562 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 502 | theories: Map[Document.Node.Name, Theory] = Map.empty, | 
| 75393 | 503 | required: Multi_Map[Document.Node.Name, UUID.T] = Multi_Map.empty | 
| 504 |     ) {
 | |
| 69562 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 505 | /* blobs */ | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 506 | |
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 507 | def doc_blobs: Document.Blobs = Document.Blobs(blobs) | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 508 | |
| 75393 | 509 |       def update_blobs(names: List[Document.Node.Name]): (Document.Blobs, State) = {
 | 
| 69562 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 510 | val new_blobs = | 
| 75394 | 511 |           names.flatMap { name =>
 | 
| 69562 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 512 | val bytes = Bytes.read(name.path) | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 513 |             blobs.get(name) match {
 | 
| 76902 | 514 | case Some(blob) if blob.bytes == bytes => None | 
| 76906 | 515 | case _ => | 
| 516 | val text = bytes.text | |
| 517 | val blob = Document.Blobs.Item(bytes, text, Symbol.Text_Chunk(text), changed = true) | |
| 518 | Some(name -> blob) | |
| 69562 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 519 | } | 
| 75394 | 520 | } | 
| 73359 | 521 | val blobs1 = new_blobs.foldLeft(blobs)(_ + _) | 
| 522 |         val blobs2 = new_blobs.foldLeft(blobs) { case (map, (a, b)) => map + (a -> b.unchanged) }
 | |
| 69562 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 523 | (Document.Blobs(blobs1), copy(blobs = blobs2)) | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 524 | } | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 525 | |
| 75393 | 526 | def blob_edits( | 
| 527 | name: Document.Node.Name, | |
| 76904 
e27d097d7d15
tuned signature: avoid confusion with Document.Node.Blob and Command.Blob;
 wenzelm parents: 
76902diff
changeset | 528 | old_blob: Option[Document.Blobs.Item] | 
| 75393 | 529 |       ) : List[Document.Edit_Text] = {
 | 
| 69562 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 530 |         val blob = blobs.getOrElse(name, error("Missing blob " + quote(name.toString)))
 | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 531 | val text_edits = | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 532 |           old_blob match {
 | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 533 | case None => List(Text.Edit.insert(0, blob.source)) | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 534 | case Some(blob0) => Text.Edit.replace(0, blob0.source, blob.source) | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 535 | } | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 536 | if (text_edits.isEmpty) Nil | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 537 | else List(name -> Document.Node.Blob(blob), name -> Document.Node.Edits(text_edits)) | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 538 | } | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 539 | |
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 540 | |
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 541 | /* theories */ | 
| 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 542 | |
| 70674 
29bb1ebb188f
clarified signature: proper Document.Node.Ordering conforming to equality (e.g. required in situations where theory names are ambiguous due to overlapping session directories);
 wenzelm parents: 
70657diff
changeset | 543 | lazy val theory_graph: Document.Node.Name.Graph[Unit] = | 
| 
29bb1ebb188f
clarified signature: proper Document.Node.Ordering conforming to equality (e.g. required in situations where theory names are ambiguous due to overlapping session directories);
 wenzelm parents: 
70657diff
changeset | 544 | Document.Node.Name.make_graph( | 
| 69012 | 545 | for ((name, theory) <- theories.toList) | 
| 71601 | 546 | yield ((name, ()), theory.node_header.imports.filter(theories.isDefinedAt))) | 
| 67056 | 547 | |
| 69012 | 548 | def is_required(name: Document.Node.Name): Boolean = required.isDefinedAt(name) | 
| 549 | ||
| 69458 | 550 | def insert_required(id: UUID.T, names: List[Document.Node.Name]): State = | 
| 73359 | 551 | copy(required = names.foldLeft(required)(_.insert(_, id))) | 
| 69012 | 552 | |
| 69458 | 553 | def remove_required(id: UUID.T, names: List[Document.Node.Name]): State = | 
| 73359 | 554 | copy(required = names.foldLeft(required)(_.remove(_, id))) | 
| 68958 | 555 | |
| 76317 | 556 | def update_theories(update: List[Theory]): State = | 
| 69012 | 557 | copy(theories = | 
| 73359 | 558 |           update.foldLeft(theories) {
 | 
| 76317 | 559 | case (thys, thy) => | 
| 560 |               thys.get(thy.node_name) match {
 | |
| 73359 | 561 | case Some(thy1) if thy1 == thy => thys | 
| 76317 | 562 | case _ => thys + (thy.node_name -> thy) | 
| 73359 | 563 | } | 
| 564 | }) | |
| 69012 | 565 | |
| 75393 | 566 |       def remove_theories(remove: List[Document.Node.Name]): State = {
 | 
| 73120 
c3589f2dff31
more informative errors: simplify diagnosis of spurious failures reported by users;
 wenzelm parents: 
72816diff
changeset | 567 | require(remove.forall(name => !is_required(name)), "attempt to remove required nodes") | 
| 69012 | 568 | copy(theories = theories -- remove) | 
| 569 | } | |
| 570 | ||
| 75393 | 571 | def unload_theories( | 
| 572 | id: UUID.T, | |
| 573 | theories: List[Document.Node.Name] | |
| 574 |       ) : (List[Document.Edit_Text], State) = {
 | |
| 70649 | 575 | val st1 = remove_required(id, theories) | 
| 67893 | 576 | val theory_edits = | 
| 69012 | 577 |           for {
 | 
| 70649 | 578 | node_name <- theories | 
| 69012 | 579 | theory <- st1.theories.get(node_name) | 
| 580 | } | |
| 67893 | 581 |           yield {
 | 
| 76312 | 582 | val theory1 = theory.set_required(st1.is_required(node_name)) | 
| 69012 | 583 | val edits = theory1.node_edits(Some(theory)) | 
| 76317 | 584 | (theory1, edits) | 
| 67893 | 585 | } | 
| 76317 | 586 | (theory_edits.flatMap(_._2), st1.update_theories(theory_edits.map(_._1))) | 
| 69012 | 587 | } | 
| 588 | ||
| 75393 | 589 | def purge_theories( | 
| 590 | nodes: Option[List[Document.Node.Name]] | |
| 591 |       ) : ((List[Document.Node.Name], List[Document.Node.Name], List[Document.Edit_Text]), State) = {
 | |
| 69012 | 592 | val all_nodes = theory_graph.topological_order | 
| 71601 | 593 | val purge = nodes.getOrElse(all_nodes).filterNot(is_required).toSet | 
| 69012 | 594 | |
| 595 | val retain = theory_graph.all_preds(all_nodes.filterNot(purge)).toSet | |
| 596 | val (retained, purged) = all_nodes.partition(retain) | |
| 70783 | 597 | val purge_edits = purged.flatMap(name => theories(name).purge_edits) | 
| 69012 | 598 | |
| 70783 | 599 | ((purged, retained, purge_edits), remove_theories(purged)) | 
| 69012 | 600 | } | 
| 601 | } | |
| 68936 
90c08c7bab9c
continuously clean frontier of already committed theories: much less resource requirements;
 wenzelm parents: 
68935diff
changeset | 602 | } | 
| 
90c08c7bab9c
continuously clean frontier of already committed theories: much less resource requirements;
 wenzelm parents: 
68935diff
changeset | 603 | |
| 69536 | 604 | class Resources private[Headless]( | 
| 76665 | 605 | val options: Options, | 
| 606 | session_background: Sessions.Background, | |
| 79777 | 607 | log: Logger = new Logger) | 
| 76665 | 608 |   extends isabelle.Resources(session_background.check_errors, log = log) {
 | 
| 69012 | 609 | resources => | 
| 610 | ||
| 78178 | 611 | val store: Store = Store(options) | 
| 71600 | 612 | |
| 69536 | 613 | |
| 614 | /* session */ | |
| 615 | ||
| 75393 | 616 | def start_session( | 
| 617 | print_mode: List[String] = Nil, | |
| 618 | progress: Progress = new Progress | |
| 619 |     ): Session = {
 | |
| 76656 | 620 | val session_name = session_background.session_name | 
| 75921 
423021c98500
clarified signature: Sessions.Base_Info follows Sessions.Base;
 wenzelm parents: 
75920diff
changeset | 621 | val session = new Session(session_name, options, resources) | 
| 82744 
0ca8b1861fa3
clarified signature: more explicit subtypes of Session, with corresponding subtypes of Resources;
 wenzelm parents: 
82142diff
changeset | 622 | |
| 82752 | 623 | val session_heaps = store.session_heaps(session_background, logic = session_name) | 
| 69536 | 624 | |
| 75921 
423021c98500
clarified signature: Sessions.Base_Info follows Sessions.Base;
 wenzelm parents: 
75920diff
changeset | 625 |       progress.echo("Starting session " + session_name + " ...")
 | 
| 77414 
0d5994eef9e6
clarified signature: allow to provide session_heaps by different means, e.g. from tmp directory or alternative session structure;
 wenzelm parents: 
77168diff
changeset | 626 | Isabelle_Process.start( | 
| 
0d5994eef9e6
clarified signature: allow to provide session_heaps by different means, e.g. from tmp directory or alternative session structure;
 wenzelm parents: 
77168diff
changeset | 627 | options, session, session_background, session_heaps, modes = print_mode).await_startup() | 
| 69536 | 628 | |
| 71604 | 629 | session | 
| 69536 | 630 | } | 
| 631 | ||
| 632 | ||
| 633 | /* theories */ | |
| 634 | ||
| 69012 | 635 | private val state = Synchronized(Resources.State()) | 
| 636 | ||
| 637 | def load_theories( | |
| 638 | session: Session, | |
| 69458 | 639 | id: UUID.T, | 
| 70649 | 640 | theories: List[Document.Node.Name], | 
| 641 | files: List[Document.Node.Name], | |
| 69920 | 642 | unicode_symbols: Boolean, | 
| 75393 | 643 | progress: Progress | 
| 644 |     ): Unit = {
 | |
| 69012 | 645 | val loaded_theories = | 
| 70649 | 646 | for (node_name <- theories) | 
| 69012 | 647 |         yield {
 | 
| 648 | val path = node_name.path | |
| 649 |           if (!node_name.is_theory) error("Not a theory file: " + path)
 | |
| 650 | ||
| 651 | progress.expose_interrupt() | |
| 77168 
547d140f0780
more uniform use of Symbol.output, even in situations where its Symbol.encode is usually redundant;
 wenzelm parents: 
77137diff
changeset | 652 | val text = Symbol.output(unicode_symbols, File.read(path)) | 
| 72772 | 653 | val node_header = resources.check_thy(node_name, Scan.char_reader(text)) | 
| 69012 | 654 | new Resources.Theory(node_name, node_header, text, true) | 
| 68936 
90c08c7bab9c
continuously clean frontier of already committed theories: much less resource requirements;
 wenzelm parents: 
68935diff
changeset | 655 | } | 
| 69012 | 656 | |
| 657 | val loaded = loaded_theories.length | |
| 658 |       if (loaded > 1) progress.echo("Loading " + loaded + " theories ...")
 | |
| 659 | ||
| 75394 | 660 |       state.change { st =>
 | 
| 75393 | 661 | val (doc_blobs1, st1) = st.insert_required(id, theories).update_blobs(files) | 
| 662 | val theory_edits = | |
| 663 | for (theory <- loaded_theories) | |
| 664 |           yield {
 | |
| 665 | val node_name = theory.node_name | |
| 76313 
f67e8a557b7d
tuned: clarified old_theory (in contrast to 4d5342898b1);
 wenzelm parents: 
76312diff
changeset | 666 | val old_theory = st.theories.get(node_name) | 
| 76312 | 667 | val theory1 = theory.set_required(st1.is_required(node_name)) | 
| 76313 
f67e8a557b7d
tuned: clarified old_theory (in contrast to 4d5342898b1);
 wenzelm parents: 
76312diff
changeset | 668 | val edits = theory1.node_edits(old_theory) | 
| 76317 | 669 | (theory1, edits) | 
| 75393 | 670 | } | 
| 671 | val file_edits = | |
| 672 |           for { node_name <- files if doc_blobs1.changed(node_name) }
 | |
| 673 | yield st1.blob_edits(node_name, st.blobs.get(node_name)) | |
| 69562 
636b3c03a61a
include loaded_files as doc_blobs (without purging);
 wenzelm parents: 
69538diff
changeset | 674 | |
| 76317 | 675 | session.update(doc_blobs1, theory_edits.flatMap(_._2) ::: file_edits.flatten) | 
| 676 | st1.update_theories(theory_edits.map(_._1)) | |
| 75394 | 677 | } | 
| 69012 | 678 | } | 
| 67936 | 679 | |
| 75393 | 680 |     def unload_theories(session: Session, id: UUID.T, theories: List[Document.Node.Name]): Unit = {
 | 
| 75394 | 681 |       state.change { st =>
 | 
| 75743 | 682 | val (edits, st1) = st.unload_theories(id, theories) | 
| 70783 | 683 | session.update(st.doc_blobs, edits) | 
| 684 | st1 | |
| 75394 | 685 | } | 
| 69012 | 686 | } | 
| 687 | ||
| 75393 | 688 |     def clean_theories(session: Session, id: UUID.T, theories: List[Document.Node.Name]): Unit = {
 | 
| 75394 | 689 |       state.change { st =>
 | 
| 75743 | 690 | val (edits1, st1) = st.unload_theories(id, theories) | 
| 691 | val ((_, _, edits2), st2) = st1.purge_theories(None) | |
| 70783 | 692 | session.update(st.doc_blobs, edits1 ::: edits2) | 
| 693 | st2 | |
| 75394 | 694 | } | 
| 69012 | 695 | } | 
| 696 | ||
| 75393 | 697 | def purge_theories( | 
| 698 | nodes: Option[List[Document.Node.Name]] | |
| 699 |     ) : (List[Document.Node.Name], List[Document.Node.Name]) = {
 | |
| 75394 | 700 |       state.change_result { st =>
 | 
| 75743 | 701 | val ((purged, retained, _), st1) = st.purge_theories(nodes) | 
| 70783 | 702 | ((purged, retained), st1) | 
| 75394 | 703 | } | 
| 69012 | 704 | } | 
| 67936 | 705 | } | 
| 67054 | 706 | } |