| author | wenzelm | 
| Tue, 09 Jan 2024 17:10:09 +0100 | |
| changeset 79452 | 664d0cec18fd | 
| parent 78876 | 4222955f3b69 | 
| child 79717 | da4e82434985 | 
| permissions | -rw-r--r-- | 
| 61276 | 1 | /* Title: Pure/System/progress.scala | 
| 2 | Author: Makarius | |
| 3 | ||
| 4 | Progress context for system processes. | |
| 5 | */ | |
| 6 | ||
| 7 | package isabelle | |
| 8 | ||
| 9 | ||
| 73897 | 10 | import java.util.{Map => JMap}
 | 
| 64201 | 11 | import java.io.{File => JFile}
 | 
| 12 | ||
| 78155 | 13 | import scala.collection.immutable.SortedMap | 
| 14 | ||
| 64201 | 15 | |
| 75393 | 16 | object Progress {
 | 
| 78503 | 17 | /* output */ | 
| 18 | ||
| 19 |   sealed abstract class Output { def message: Message }
 | |
| 78155 | 20 | |
| 78611 | 21 |   enum Kind { case writeln, warning, error_message }
 | 
| 78503 | 22 | sealed case class Message( | 
| 78611 | 23 | kind: Kind, | 
| 78503 | 24 | text: String, | 
| 25 | verbose: Boolean = false | |
| 26 |   ) extends Output {
 | |
| 27 | override def message: Message = this | |
| 28 | ||
| 77502 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 29 | def output_text: String = | 
| 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 30 |       kind match {
 | 
| 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 31 | case Kind.writeln => Output.writeln_text(text) | 
| 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 32 | case Kind.warning => Output.warning_text(text) | 
| 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 33 | case Kind.error_message => Output.error_message_text(text) | 
| 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 34 | } | 
| 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 35 | |
| 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 36 | override def toString: String = output_text | 
| 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 37 | } | 
| 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 38 | |
| 78503 | 39 | sealed case class Theory( | 
| 40 | theory: String, | |
| 41 | session: String = "", | |
| 42 | percentage: Option[Int] = None | |
| 43 |   ) extends Output {
 | |
| 77502 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 44 | def message: Message = | 
| 77509 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 45 | Message(Kind.writeln, print_session + print_theory + print_percentage, verbose = true) | 
| 68987 | 46 | |
| 77504 | 47 | def print_session: String = if_proper(session, session + ": ") | 
| 68987 | 48 | def print_theory: String = "theory " + theory | 
| 49 | def print_percentage: String = | |
| 50 |       percentage match { case None => "" case Some(p) => " " + p + "%" }
 | |
| 68957 | 51 | } | 
| 78155 | 52 | |
| 53 | ||
| 54 | /* SQL data model */ | |
| 55 | ||
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78389diff
changeset | 56 |   object private_data extends SQL.Data("isabelle_progress") {
 | 
| 78170 
c85eeff78b33
proper support for SQLite: avoid conflicts on transaction_lock;
 wenzelm parents: 
78156diff
changeset | 57 |     val database: Path = Path.explode("$ISABELLE_HOME_USER/progress.db")
 | 
| 
c85eeff78b33
proper support for SQLite: avoid conflicts on transaction_lock;
 wenzelm parents: 
78156diff
changeset | 58 | |
| 78187 
2df0f3604a67
clarified signature: more explicit class SQL.Data;
 wenzelm parents: 
78170diff
changeset | 59 | override lazy val tables = SQL.Tables(Base.table, Agents.table, Messages.table) | 
| 78155 | 60 | |
| 61 |     object Base {
 | |
| 62 |       val context_uuid = SQL.Column.string("context_uuid").make_primary_key
 | |
| 63 |       val context = SQL.Column.long("context").make_primary_key
 | |
| 64 |       val stopped = SQL.Column.bool("stopped")
 | |
| 65 | ||
| 78266 | 66 | val table = make_table(List(context_uuid, context, stopped)) | 
| 78155 | 67 | } | 
| 68 | ||
| 69 |     object Agents {
 | |
| 70 |       val agent_uuid = SQL.Column.string("agent_uuid").make_primary_key
 | |
| 71 |       val context_uuid = SQL.Column.string("context_uuid").make_primary_key
 | |
| 78240 | 72 |       val kind = SQL.Column.string("kind")
 | 
| 78155 | 73 |       val hostname = SQL.Column.string("hostname")
 | 
| 74 |       val java_pid = SQL.Column.long("java_pid")
 | |
| 75 |       val java_start = SQL.Column.date("java_start")
 | |
| 76 |       val start = SQL.Column.date("start")
 | |
| 77 |       val stamp = SQL.Column.date("stamp")
 | |
| 78 |       val stop = SQL.Column.date("stop")
 | |
| 79 |       val seen = SQL.Column.long("seen")
 | |
| 80 | ||
| 78266 | 81 | val table = | 
| 82 | make_table( | |
| 83 | List(agent_uuid, context_uuid, kind, hostname, java_pid, java_start, start, stamp, stop, seen), | |
| 84 | name = "agents") | |
| 78155 | 85 | } | 
| 86 | ||
| 87 |     object Messages {
 | |
| 88 | type T = SortedMap[Long, Message] | |
| 89 | val empty: T = SortedMap.empty | |
| 90 | ||
| 91 |       val context = SQL.Column.long("context").make_primary_key
 | |
| 92 |       val serial = SQL.Column.long("serial").make_primary_key
 | |
| 93 |       val kind = SQL.Column.int("kind")
 | |
| 94 |       val text = SQL.Column.string("text")
 | |
| 95 |       val verbose = SQL.Column.bool("verbose")
 | |
| 96 | ||
| 78266 | 97 | val table = make_table(List(context, serial, kind, text, verbose), name = "messages") | 
| 78155 | 98 | } | 
| 99 | ||
| 100 | def read_progress_context(db: SQL.Database, context_uuid: String): Option[Long] = | |
| 101 | db.execute_query_statementO( | |
| 102 | Base.table.select(List(Base.context), | |
| 103 | sql = Base.context_uuid.where_equal(context_uuid)), _.long(Base.context)) | |
| 104 | ||
| 105 | def next_progress_context(db: SQL.Database): Long = | |
| 106 | db.execute_query_statementO( | |
| 107 | Base.table.select(List(Base.context.max)), _.long(Base.context)).getOrElse(0L) + 1L | |
| 108 | ||
| 109 | def read_progress_stopped(db: SQL.Database, context: Long): Boolean = | |
| 110 | db.execute_query_statementO( | |
| 111 | Base.table.select(List(Base.stopped), sql = Base.context.where_equal(context)), | |
| 112 | _.bool(Base.stopped) | |
| 113 | ).getOrElse(false) | |
| 114 | ||
| 115 | def write_progress_stopped(db: SQL.Database, context: Long, stopped: Boolean): Unit = | |
| 116 | db.execute_statement( | |
| 117 | Base.table.update(List(Base.stopped), sql = Base.context.where_equal(context)), | |
| 118 |           body = { stmt => stmt.bool(1) = stopped })
 | |
| 119 | ||
| 120 | def update_agent( | |
| 121 | db: SQL.Database, | |
| 122 | agent_uuid: String, | |
| 123 | seen: Long, | |
| 78246 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 124 | stop_now: Boolean = false | 
| 78155 | 125 |     ): Unit = {
 | 
| 78246 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 126 | val sql = Agents.agent_uuid.where_equal(agent_uuid) | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 127 | |
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 128 | val stop = | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 129 | db.execute_query_statementO( | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 130 | Agents.table.select(List(Agents.stop), sql = sql), _.get_date(Agents.stop)).flatten | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 131 | |
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 132 | db.execute_statement( | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 133 | Agents.table.update(List(Agents.stamp, Agents.stop, Agents.seen), sql = sql), | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 134 |         body = { stmt =>
 | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 135 | val now = db.now() | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 136 | stmt.date(1) = now | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 137 | stmt.date(2) = if (stop_now) Some(now) else stop | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 138 | stmt.long(3) = seen | 
| 
76dd9b9cf624
more robust "stop": further "stamp" ticks may happen afterwards;
 wenzelm parents: 
78242diff
changeset | 139 | }) | 
| 78155 | 140 | } | 
| 141 | ||
| 78535 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 142 | def read_messages_serial(db: SQL.Database, context: Long): Long = | 
| 78155 | 143 | db.execute_query_statementO( | 
| 144 | Messages.table.select( | |
| 145 | List(Messages.serial.max), sql = Base.context.where_equal(context)), | |
| 146 | _.long(Messages.serial) | |
| 78535 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 147 | ).getOrElse(0L) | 
| 78155 | 148 | |
| 149 | def read_messages(db: SQL.Database, context: Long, seen: Long = 0): Messages.T = | |
| 150 | db.execute_query_statement( | |
| 151 | Messages.table.select( | |
| 152 | List(Messages.serial, Messages.kind, Messages.text, Messages.verbose), | |
| 153 | sql = | |
| 154 | SQL.where_and( | |
| 155 | Messages.context.ident + " = " + context, | |
| 156 | if (seen <= 0) "" else Messages.serial.ident + " > " + seen)), | |
| 157 | SortedMap.from[Long, Message], | |
| 158 |         { res =>
 | |
| 159 | val serial = res.long(Messages.serial) | |
| 78611 | 160 | val kind = Kind.fromOrdinal(res.int(Messages.kind)) | 
| 78155 | 161 | val text = res.string(Messages.text) | 
| 162 | val verbose = res.bool(Messages.verbose) | |
| 163 | serial -> Message(kind, text, verbose = verbose) | |
| 164 | } | |
| 165 | ) | |
| 166 | ||
| 167 | def write_messages( | |
| 168 | db: SQL.Database, | |
| 169 | context: Long, | |
| 78539 
15f55d02ba67
more scalable write_messages via db.execute_batch_statement;
 wenzelm parents: 
78537diff
changeset | 170 | messages: List[(Long, Message)] | 
| 78155 | 171 |     ): Unit = {
 | 
| 78551 
d0c9d277620e
clarified signature: more robust treatment of implicit state;
 wenzelm parents: 
78550diff
changeset | 172 | db.execute_batch_statement(Messages.table.insert(), batch = | 
| 
d0c9d277620e
clarified signature: more robust treatment of implicit state;
 wenzelm parents: 
78550diff
changeset | 173 |         for ((serial, message) <- messages) yield { (stmt: SQL.Statement) =>
 | 
| 78539 
15f55d02ba67
more scalable write_messages via db.execute_batch_statement;
 wenzelm parents: 
78537diff
changeset | 174 | stmt.long(1) = context | 
| 
15f55d02ba67
more scalable write_messages via db.execute_batch_statement;
 wenzelm parents: 
78537diff
changeset | 175 | stmt.long(2) = serial | 
| 78611 | 176 | stmt.int(3) = message.kind.ordinal | 
| 78539 
15f55d02ba67
more scalable write_messages via db.execute_batch_statement;
 wenzelm parents: 
78537diff
changeset | 177 | stmt.string(4) = message.text | 
| 
15f55d02ba67
more scalable write_messages via db.execute_batch_statement;
 wenzelm parents: 
78537diff
changeset | 178 | stmt.bool(5) = message.verbose | 
| 78551 
d0c9d277620e
clarified signature: more robust treatment of implicit state;
 wenzelm parents: 
78550diff
changeset | 179 | }) | 
| 78155 | 180 | } | 
| 181 | } | |
| 68410 
4e27f5c361d2
clarified signature: more uniform theory_message (see also d7920eb7de54);
 wenzelm parents: 
68330diff
changeset | 182 | } | 
| 
4e27f5c361d2
clarified signature: more uniform theory_message (see also d7920eb7de54);
 wenzelm parents: 
68330diff
changeset | 183 | |
| 75393 | 184 | class Progress {
 | 
| 78876 | 185 | def now(): Date = Date.now() | 
| 186 | val start: Date = now() | |
| 187 | ||
| 77509 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 188 | def verbose: Boolean = false | 
| 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 189 | final def do_output(message: Progress.Message): Boolean = !message.verbose || verbose | 
| 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 190 | |
| 78432 | 191 |   def output(message: Progress.Message): Unit = {}
 | 
| 77502 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 192 | |
| 77509 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 193 | final def echo(msg: String, verbose: Boolean = false): Unit = | 
| 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 194 | output(Progress.Message(Progress.Kind.writeln, msg, verbose = verbose)) | 
| 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 195 | final def echo_warning(msg: String, verbose: Boolean = false): Unit = | 
| 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 196 | output(Progress.Message(Progress.Kind.warning, msg, verbose = verbose)) | 
| 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 197 | final def echo_error_message(msg: String, verbose: Boolean = false): Unit = | 
| 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 198 | output(Progress.Message(Progress.Kind.error_message, msg, verbose = verbose)) | 
| 77502 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 199 | |
| 77506 
a8175b950173
more robust signature: avoid totally adhoc overriding (see also Build_Process.progress vs. build_progress);
 wenzelm parents: 
77504diff
changeset | 200 | final def echo_if(cond: Boolean, msg: String): Unit = if (cond) echo(msg) | 
| 77499 
8fc94efa954a
clarified signature: more uniform Progress.verbose, avoid adhoc "override def theory()";
 wenzelm parents: 
77498diff
changeset | 201 | |
| 77509 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 202 | def theory(theory: Progress.Theory): Unit = output(theory.message) | 
| 73340 | 203 |   def nodes_status(nodes_status: Document_Status.Nodes_Status): Unit = {}
 | 
| 64909 | 204 | |
| 78432 | 205 |   final def capture[A](e: => A, msg: String, err: Throwable => String): Exn.Result[A] = {
 | 
| 206 | echo(msg) | |
| 207 |     try { Exn.Res(e) }
 | |
| 208 |     catch { case exn: Throwable => echo_error_message(err(exn)); Exn.Exn[A](exn) }
 | |
| 209 | } | |
| 210 | ||
| 77506 
a8175b950173
more robust signature: avoid totally adhoc overriding (see also Build_Process.progress vs. build_progress);
 wenzelm parents: 
77504diff
changeset | 211 | final def timeit[A]( | 
| 
a8175b950173
more robust signature: avoid totally adhoc overriding (see also Build_Process.progress vs. build_progress);
 wenzelm parents: 
77504diff
changeset | 212 | body: => A, | 
| 
a8175b950173
more robust signature: avoid totally adhoc overriding (see also Build_Process.progress vs. build_progress);
 wenzelm parents: 
77504diff
changeset | 213 | message: Exn.Result[A] => String = null, | 
| 
a8175b950173
more robust signature: avoid totally adhoc overriding (see also Build_Process.progress vs. build_progress);
 wenzelm parents: 
77504diff
changeset | 214 | enabled: Boolean = true | 
| 77509 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 215 | ): A = Timing.timeit(body, message = message, enabled = enabled, output = echo(_)) | 
| 65921 | 216 | |
| 77524 | 217 | @volatile private var is_stopped = false | 
| 73367 | 218 |   def stop(): Unit = { is_stopped = true }
 | 
| 75393 | 219 |   def stopped: Boolean = {
 | 
| 73367 | 220 | if (Thread.interrupted()) is_stopped = true | 
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71601diff
changeset | 221 | is_stopped | 
| 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71601diff
changeset | 222 | } | 
| 78529 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78505diff
changeset | 223 | def stopped_local: Boolean = false | 
| 71726 
a5fda30edae2
clarified signature: more uniform treatment of stopped/interrupted state;
 wenzelm parents: 
71601diff
changeset | 224 | |
| 77506 
a8175b950173
more robust signature: avoid totally adhoc overriding (see also Build_Process.progress vs. build_progress);
 wenzelm parents: 
77504diff
changeset | 225 |   final def interrupt_handler[A](e: => A): A = POSIX_Interrupt.handler { stop() } { e }
 | 
| 
a8175b950173
more robust signature: avoid totally adhoc overriding (see also Build_Process.progress vs. build_progress);
 wenzelm parents: 
77504diff
changeset | 226 | final def expose_interrupt(): Unit = if (stopped) throw Exn.Interrupt() | 
| 61276 | 227 | override def toString: String = if (stopped) "Progress(stopped)" else "Progress" | 
| 64201 | 228 | |
| 77506 
a8175b950173
more robust signature: avoid totally adhoc overriding (see also Build_Process.progress vs. build_progress);
 wenzelm parents: 
77504diff
changeset | 229 | final def bash(script: String, | 
| 64201 | 230 | cwd: JFile = null, | 
| 73897 | 231 | env: JMap[String, String] = Isabelle_System.settings(), | 
| 64201 | 232 | redirect: Boolean = false, | 
| 65930 | 233 | echo: Boolean = false, | 
| 72599 | 234 | watchdog: Time = Time.zero, | 
| 75393 | 235 | strict: Boolean = true | 
| 236 |   ): Process_Result = {
 | |
| 72599 | 237 | val result = | 
| 238 | Isabelle_System.bash(script, cwd = cwd, env = env, redirect = redirect, | |
| 239 | progress_stdout = echo_if(echo, _), | |
| 240 | progress_stderr = echo_if(echo, _), | |
| 241 | watchdog = if (watchdog.is_zero) None else Some((watchdog, _ => stopped)), | |
| 242 | strict = strict) | |
| 243 | if (strict && stopped) throw Exn.Interrupt() else result | |
| 64201 | 244 | } | 
| 61276 | 245 | } | 
| 246 | ||
| 77499 
8fc94efa954a
clarified signature: more uniform Progress.verbose, avoid adhoc "override def theory()";
 wenzelm parents: 
77498diff
changeset | 247 | class Console_Progress(override val verbose: Boolean = false, stderr: Boolean = false) | 
| 
8fc94efa954a
clarified signature: more uniform Progress.verbose, avoid adhoc "override def theory()";
 wenzelm parents: 
77498diff
changeset | 248 | extends Progress {
 | 
| 78874 | 249 |   override def output(message: Progress.Message): Unit = synchronized {
 | 
| 77509 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 250 |     if (do_output(message)) {
 | 
| 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 251 | Output.output(message.output_text, stdout = !stderr, include_empty = true) | 
| 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 252 | } | 
| 78874 | 253 | } | 
| 77503 | 254 | |
| 255 | override def toString: String = super.toString + ": console" | |
| 61276 | 256 | } | 
| 65888 | 257 | |
| 77507 | 258 | class File_Progress(path: Path, override val verbose: Boolean = false) | 
| 259 | extends Progress {
 | |
| 78874 | 260 |   override def output(message: Progress.Message): Unit = synchronized {
 | 
| 77509 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 261 | if (do_output(message)) File.append(path, message.output_text + "\n") | 
| 78874 | 262 | } | 
| 65888 | 263 | |
| 77503 | 264 | override def toString: String = super.toString + ": " + path.toString | 
| 65888 | 265 | } | 
| 76994 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 266 | |
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 267 | |
| 78155 | 268 | /* database progress */ | 
| 269 | ||
| 270 | class Database_Progress( | |
| 78532 | 271 | db: SQL.Database, | 
| 272 | base_progress: Progress, | |
| 78536 
555bdac7c279
restrict input_messages to master build_process: avoid excessive db traffic for distributed build_workers;
 wenzelm parents: 
78535diff
changeset | 273 | input_messages: Boolean = false, | 
| 78532 | 274 | output_stopped: Boolean = false, | 
| 275 | kind: String = "progress", | |
| 276 | hostname: String = Isabelle_System.hostname(), | |
| 78535 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 277 | context_uuid: String = UUID.random().toString, | 
| 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 278 | timeout: Option[Time] = None) | 
| 78155 | 279 | extends Progress {
 | 
| 280 | database_progress => | |
| 281 | ||
| 78876 | 282 | override def now(): Date = db.now() | 
| 283 | override val start: Date = now() | |
| 284 | ||
| 78537 | 285 |   if (UUID.unapply(context_uuid).isEmpty) {
 | 
| 286 |     error("Bad Database_Progress.context_uuid: " + quote(context_uuid))
 | |
| 287 | } | |
| 288 | ||
| 78155 | 289 | private var _agent_uuid: String = "" | 
| 78382 | 290 | private var _context: Long = -1 | 
| 78504 | 291 | private var _serial: Long = 0 | 
| 78529 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78505diff
changeset | 292 | private var _stopped_db: Boolean = false | 
| 78535 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 293 | private var _consumer: Consumer_Thread[Progress.Output] = null | 
| 78155 | 294 | |
| 295 |   def agent_uuid: String = synchronized { _agent_uuid }
 | |
| 296 | ||
| 297 |   private def init(): Unit = synchronized {
 | |
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78389diff
changeset | 298 |     Progress.private_data.transaction_lock(db, create = true, label = "Database_Progress.init") {
 | 
| 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78389diff
changeset | 299 |       Progress.private_data.read_progress_context(db, context_uuid) match {
 | 
| 78155 | 300 | case Some(context) => | 
| 301 | _context = context | |
| 302 | _agent_uuid = UUID.random().toString | |
| 303 | case None => | |
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78389diff
changeset | 304 | _context = Progress.private_data.next_progress_context(db) | 
| 78155 | 305 | _agent_uuid = context_uuid | 
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78389diff
changeset | 306 |           db.execute_statement(Progress.private_data.Base.table.insert(), { stmt =>
 | 
| 78155 | 307 | stmt.string(1) = context_uuid | 
| 308 | stmt.long(2) = _context | |
| 309 | stmt.bool(3) = false | |
| 310 | }) | |
| 311 | } | |
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78389diff
changeset | 312 |       db.execute_statement(Progress.private_data.Agents.table.insert(), { stmt =>
 | 
| 78155 | 313 | val java = ProcessHandle.current() | 
| 314 | val java_pid = java.pid | |
| 315 | val java_start = Date.instant(java.info.startInstant.get) | |
| 316 | ||
| 317 | stmt.string(1) = _agent_uuid | |
| 318 | stmt.string(2) = context_uuid | |
| 78240 | 319 | stmt.string(3) = kind | 
| 320 | stmt.string(4) = hostname | |
| 321 | stmt.long(5) = java_pid | |
| 322 | stmt.date(6) = java_start | |
| 78876 | 323 | stmt.date(7) = start | 
| 324 | stmt.date(8) = start | |
| 78240 | 325 | stmt.date(9) = None | 
| 326 | stmt.long(10) = 0L | |
| 78155 | 327 | }) | 
| 78187 
2df0f3604a67
clarified signature: more explicit class SQL.Data;
 wenzelm parents: 
78170diff
changeset | 328 | } | 
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78389diff
changeset | 329 | if (context_uuid == _agent_uuid) db.vacuum(Progress.private_data.tables.list) | 
| 78535 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 330 | |
| 78564 | 331 |     def consume(bulk_output: List[Progress.Output]): List[Exn.Result[Unit]] = sync_database {
 | 
| 332 |       if (bulk_output.nonEmpty) {
 | |
| 333 |         for (out <- bulk_output) {
 | |
| 334 |           out match {
 | |
| 335 | case message: Progress.Message => | |
| 336 | if (do_output(message)) base_progress.output(message) | |
| 337 | case theory: Progress.Theory => base_progress.theory(theory) | |
| 338 | } | |
| 339 | } | |
| 340 | ||
| 341 | val messages = | |
| 342 | for ((out, i) <- bulk_output.zipWithIndex) | |
| 343 | yield (_serial + i + 1) -> out.message | |
| 344 | ||
| 345 | Progress.private_data.write_messages(db, _context, messages) | |
| 346 | _serial = messages.last._1 | |
| 347 | } | |
| 348 | bulk_output.map(_ => Exn.Res(())) | |
| 349 | } | |
| 350 | ||
| 78535 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 351 | _consumer = Consumer_Thread.fork_bulk[Progress.Output](name = "Database_Progress.consumer")( | 
| 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 352 | bulk = _ => true, timeout = timeout, | 
| 78565 | 353 |       consume = { bulk_output =>
 | 
| 354 | val results = | |
| 355 | if (bulk_output.isEmpty) consume(Nil) | |
| 356 | else bulk_output.grouped(200).toList.flatMap(consume) | |
| 357 | (results, true) }) | |
| 78155 | 358 | } | 
| 359 | ||
| 78242 | 360 |   def exit(close: Boolean = false): Unit = synchronized {
 | 
| 78155 | 361 |     if (_context > 0) {
 | 
| 78535 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 362 | _consumer.shutdown() | 
| 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 363 | _consumer = null | 
| 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 364 | |
| 78396 
7853d9072d1b
renamed object Data to private_data, to emphasize its intended scope (but it is publicly accessible in the database);
 wenzelm parents: 
78389diff
changeset | 365 |       Progress.private_data.transaction_lock(db, label = "Database_Progress.exit") {
 | 
| 78504 | 366 | Progress.private_data.update_agent(db, _agent_uuid, _serial, stop_now = true) | 
| 78155 | 367 | } | 
| 368 | _context = 0 | |
| 369 | } | |
| 78242 | 370 | if (close) db.close() | 
| 78155 | 371 | } | 
| 372 | ||
| 78535 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 373 |   private def sync_context[A](body: => A): A = synchronized {
 | 
| 78382 | 374 |     if (_context < 0) throw new IllegalStateException("Database_Progress before init")
 | 
| 375 |     if (_context == 0) throw new IllegalStateException("Database_Progress after exit")
 | |
| 376 | ||
| 78535 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 377 | body | 
| 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 378 | } | 
| 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 379 | |
| 78586 | 380 |   private def sync_database[A](body: => A): A = synchronized {
 | 
| 78549 | 381 |     Progress.private_data.transaction_lock(db, label = "Database_Progress.sync_database") {
 | 
| 78529 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78505diff
changeset | 382 | _stopped_db = Progress.private_data.read_progress_stopped(db, _context) | 
| 78155 | 383 | |
| 78529 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78505diff
changeset | 384 | if (_stopped_db && !base_progress.stopped) base_progress.stop() | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78505diff
changeset | 385 |       if (!_stopped_db && base_progress.stopped && output_stopped) {
 | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78505diff
changeset | 386 | Progress.private_data.write_progress_stopped(db, _context, true) | 
| 
0e79fa88cab6
build_worker is stopped independently from master build_process;
 wenzelm parents: 
78505diff
changeset | 387 | } | 
| 78155 | 388 | |
| 78536 
555bdac7c279
restrict input_messages to master build_process: avoid excessive db traffic for distributed build_workers;
 wenzelm parents: 
78535diff
changeset | 389 | val serial0 = _serial | 
| 
555bdac7c279
restrict input_messages to master build_process: avoid excessive db traffic for distributed build_workers;
 wenzelm parents: 
78535diff
changeset | 390 |       if (input_messages) {
 | 
| 
555bdac7c279
restrict input_messages to master build_process: avoid excessive db traffic for distributed build_workers;
 wenzelm parents: 
78535diff
changeset | 391 | val messages = Progress.private_data.read_messages(db, _context, seen = _serial) | 
| 
555bdac7c279
restrict input_messages to master build_process: avoid excessive db traffic for distributed build_workers;
 wenzelm parents: 
78535diff
changeset | 392 |         for ((message_serial, message) <- messages) {
 | 
| 
555bdac7c279
restrict input_messages to master build_process: avoid excessive db traffic for distributed build_workers;
 wenzelm parents: 
78535diff
changeset | 393 | if (base_progress.do_output(message)) base_progress.output(message) | 
| 
555bdac7c279
restrict input_messages to master build_process: avoid excessive db traffic for distributed build_workers;
 wenzelm parents: 
78535diff
changeset | 394 | _serial = _serial max message_serial | 
| 
555bdac7c279
restrict input_messages to master build_process: avoid excessive db traffic for distributed build_workers;
 wenzelm parents: 
78535diff
changeset | 395 | } | 
| 78155 | 396 | } | 
| 78536 
555bdac7c279
restrict input_messages to master build_process: avoid excessive db traffic for distributed build_workers;
 wenzelm parents: 
78535diff
changeset | 397 |       else {
 | 
| 
555bdac7c279
restrict input_messages to master build_process: avoid excessive db traffic for distributed build_workers;
 wenzelm parents: 
78535diff
changeset | 398 | _serial = _serial max Progress.private_data.read_messages_serial(db, _context) | 
| 
555bdac7c279
restrict input_messages to master build_process: avoid excessive db traffic for distributed build_workers;
 wenzelm parents: 
78535diff
changeset | 399 | } | 
| 
555bdac7c279
restrict input_messages to master build_process: avoid excessive db traffic for distributed build_workers;
 wenzelm parents: 
78535diff
changeset | 400 | |
| 78550 
d8cc0f625b52
proper sync_database for Database_Progress consumer;
 wenzelm parents: 
78549diff
changeset | 401 | val res = body | 
| 
d8cc0f625b52
proper sync_database for Database_Progress consumer;
 wenzelm parents: 
78549diff
changeset | 402 | |
| 78536 
555bdac7c279
restrict input_messages to master build_process: avoid excessive db traffic for distributed build_workers;
 wenzelm parents: 
78535diff
changeset | 403 | if (_serial != serial0) Progress.private_data.update_agent(db, _agent_uuid, _serial) | 
| 78155 | 404 | |
| 78550 
d8cc0f625b52
proper sync_database for Database_Progress consumer;
 wenzelm parents: 
78549diff
changeset | 405 | res | 
| 78155 | 406 | } | 
| 407 | } | |
| 408 | ||
| 78535 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 409 |   private def sync(): Unit = sync_database {}
 | 
| 78155 | 410 | |
| 78535 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 411 |   override def output(message: Progress.Message): Unit = sync_context { _consumer.send(message) }
 | 
| 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 412 |   override def theory(theory: Progress.Theory): Unit = sync_context { _consumer.send(theory) }
 | 
| 78155 | 413 | |
| 414 | override def nodes_status(nodes_status: Document_Status.Nodes_Status): Unit = | |
| 415 | base_progress.nodes_status(nodes_status) | |
| 416 | ||
| 417 | override def verbose: Boolean = base_progress.verbose | |
| 418 | ||
| 78535 
af37e1b4dce0
more scalable Database_Progress via asynchronous Consumer_Thread.fork_bulk;
 wenzelm parents: 
78532diff
changeset | 419 |   override def stop(): Unit = sync_context { base_progress.stop(); sync() }
 | 
| 78550 
d8cc0f625b52
proper sync_database for Database_Progress consumer;
 wenzelm parents: 
78549diff
changeset | 420 |   override def stopped: Boolean = sync_context { base_progress.stopped }
 | 
| 
d8cc0f625b52
proper sync_database for Database_Progress consumer;
 wenzelm parents: 
78549diff
changeset | 421 |   override def stopped_local: Boolean = sync_context { base_progress.stopped && !_stopped_db }
 | 
| 78155 | 422 | |
| 423 | override def toString: String = super.toString + ": database " + db | |
| 424 | ||
| 425 | init() | |
| 426 | sync() | |
| 427 | } | |
| 428 | ||
| 429 | ||
| 76994 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 430 | /* structured program progress */ | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 431 | |
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 432 | object Program_Progress {
 | 
| 77174 | 433 |   class Program private[Program_Progress](heading: String, title: String) {
 | 
| 76994 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 434 | private val output_buffer = new StringBuffer(256) // synchronized | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 435 | |
| 77509 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 436 |     def output(message: Progress.Message): Unit = synchronized {
 | 
| 76994 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 437 |       if (output_buffer.length() > 0) output_buffer.append('\n')
 | 
| 77502 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 438 | output_buffer.append(message.output_text) | 
| 76994 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 439 | } | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 440 | |
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 441 | val start_time: Time = Time.now() | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 442 | private var stop_time: Option[Time] = None | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 443 |     def stop_now(): Unit = synchronized { stop_time = Some(Time.now()) }
 | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 444 | |
| 77174 | 445 |     def output(): (Command.Results, XML.Body) = synchronized {
 | 
| 76994 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 446 | val output_text = output_buffer.toString | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 447 | val elapsed_time = stop_time.map(t => t - start_time) | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 448 | |
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 449 | val message_prefix = heading + " " | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 450 | val message_suffix = | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 451 |         elapsed_time match {
 | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 452 | case None => " ..." | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 453 |           case Some(t) => " ... (" + t.message + " elapsed time)"
 | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 454 | } | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 455 | |
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 456 | val (results, message) = | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 457 |         if (output_text.isEmpty) {
 | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 458 | (Command.Results.empty, XML.string(message_prefix + title + message_suffix)) | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 459 | } | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 460 |         else {
 | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 461 | val i = Document_ID.make() | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 462 | val results = Command.Results.make(List(i -> Protocol.writeln_message(output_text))) | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 463 | val message = | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 464 | XML.string(message_prefix) ::: | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 465 | List(XML.Elem(Markup(Markup.WRITELN, Markup.Serial(i)), XML.string(title))) ::: | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 466 | XML.string(message_suffix) | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 467 | (results, message) | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 468 | } | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 469 | |
| 76997 | 470 | (results, List(XML.elem(Markup.TRACING_MESSAGE, message))) | 
| 76994 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 471 | } | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 472 | } | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 473 | } | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 474 | |
| 77174 | 475 | abstract class Program_Progress( | 
| 476 | default_heading: String = "Running", | |
| 77499 
8fc94efa954a
clarified signature: more uniform Progress.verbose, avoid adhoc "override def theory()";
 wenzelm parents: 
77498diff
changeset | 477 | default_title: String = "program", | 
| 
8fc94efa954a
clarified signature: more uniform Progress.verbose, avoid adhoc "override def theory()";
 wenzelm parents: 
77498diff
changeset | 478 | override val verbose: Boolean = false | 
| 77174 | 479 | ) extends Progress {
 | 
| 76994 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 480 | private var _finished_programs: List[Program_Progress.Program] = Nil | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 481 | private var _running_program: Option[Program_Progress.Program] = None | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 482 | |
| 77174 | 483 |   def output(): (Command.Results, XML.Body) = synchronized {
 | 
| 76994 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 484 | val programs = (_running_program.toList ::: _finished_programs).reverse | 
| 77174 | 485 | val programs_output = programs.map(_.output()) | 
| 76994 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 486 | val results = Command.Results.merge(programs_output.map(_._1)) | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 487 | val body = Library.separate(Pretty.Separator, programs_output.map(_._2)).flatten | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 488 | (results, body) | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 489 | } | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 490 | |
| 77174 | 491 |   private def start_program(heading: String, title: String): Unit = synchronized {
 | 
| 492 | _running_program = Some(new Program_Progress.Program(heading, title)) | |
| 76994 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 493 | } | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 494 | |
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 495 |   def stop_program(): Unit = synchronized {
 | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 496 |     _running_program match {
 | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 497 | case Some(program) => | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 498 | program.stop_now() | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 499 | _finished_programs ::= program | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 500 | _running_program = None | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 501 | case None => | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 502 | } | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 503 | } | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 504 | |
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 505 | def detect_program(s: String): Option[String] | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 506 | |
| 77509 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 507 |   override def output(message: Progress.Message): Unit = synchronized {
 | 
| 77502 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 508 | val writeln_msg = if (message.kind == Progress.Kind.writeln) message.text else "" | 
| 
2e2b2bd6b2d2
clarified signature: require just one "override def echo(message: Progress.Message): Unit";
 wenzelm parents: 
77499diff
changeset | 509 |     detect_program(writeln_msg).map(Word.explode) match {
 | 
| 77174 | 510 | case Some(a :: bs) => | 
| 76994 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 511 | stop_program() | 
| 77174 | 512 | start_program(a, Word.implode(bs)) | 
| 513 | case _ => | |
| 514 | if (_running_program.isEmpty) start_program(default_heading, default_title) | |
| 77509 
3bc49507bae5
clarified treatment of "verbose" messages, e.g. Progress.theory();
 wenzelm parents: 
77507diff
changeset | 515 | if (do_output(message)) _running_program.get.output(message) | 
| 76994 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 516 | } | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 517 | } | 
| 
7c23db6b857b
more detailed Program_Progress / Log_Progress: each program gets its own log output, which is attached to the document via markup;
 wenzelm parents: 
76593diff
changeset | 518 | } |