| author | wenzelm | 
| Sat, 10 Oct 2020 21:45:58 +0200 | |
| changeset 72428 | b7351ffe0dbc | 
| parent 72112 | 3546dd4ade74 | 
| child 74166 | ff3dbb2be924 | 
| permissions | -rw-r--r-- | 
| 28156 | 1 | (* Title: Pure/Concurrent/future.ML | 
| 2 | Author: Makarius | |
| 3 | ||
| 57350 | 4 | Value-oriented parallel execution via futures and promises. | 
| 28156 | 5 | *) | 
| 6 | ||
| 7 | signature FUTURE = | |
| 8 | sig | |
| 44300 
349cc426d929
tuned signature -- treat structure Task_Queue as private to implementation;
 wenzelm parents: 
44299diff
changeset | 9 | type task = Task_Queue.task | 
| 
349cc426d929
tuned signature -- treat structure Task_Queue as private to implementation;
 wenzelm parents: 
44299diff
changeset | 10 | type group = Task_Queue.group | 
| 
349cc426d929
tuned signature -- treat structure Task_Queue as private to implementation;
 wenzelm parents: 
44299diff
changeset | 11 | val new_group: group option -> group | 
| 
349cc426d929
tuned signature -- treat structure Task_Queue as private to implementation;
 wenzelm parents: 
44299diff
changeset | 12 | val worker_task: unit -> task option | 
| 
349cc426d929
tuned signature -- treat structure Task_Queue as private to implementation;
 wenzelm parents: 
44299diff
changeset | 13 | val worker_group: unit -> group option | 
| 52603 | 14 | val the_worker_group: unit -> group | 
| 44300 
349cc426d929
tuned signature -- treat structure Task_Queue as private to implementation;
 wenzelm parents: 
44299diff
changeset | 15 | val worker_subgroup: unit -> group | 
| 28972 | 16 | type 'a future | 
| 44300 
349cc426d929
tuned signature -- treat structure Task_Queue as private to implementation;
 wenzelm parents: 
44299diff
changeset | 17 | val task_of: 'a future -> task | 
| 28972 | 18 | val peek: 'a future -> 'a Exn.result option | 
| 19 | val is_finished: 'a future -> bool | |
| 44301 | 20 |   val interruptible_task: ('a -> 'b) -> 'a -> 'b
 | 
| 47404 
e6e5750f1311
simplified Future.cancel/cancel_group (again) -- running threads only;
 wenzelm parents: 
45666diff
changeset | 21 | val cancel_group: group -> unit | 
| 
e6e5750f1311
simplified Future.cancel/cancel_group (again) -- running threads only;
 wenzelm parents: 
45666diff
changeset | 22 | val cancel: 'a future -> unit | 
| 56333 
38f1422ef473
support bulk messages consisting of small string segments, which are more healthy to the Poly/ML RTS and might prevent spurious GC crashes such as MTGCProcessMarkPointers::ScanAddressesInObject;
 wenzelm parents: 
54671diff
changeset | 23 | val error_message: Position.T -> (serial * string) * string option -> unit | 
| 50914 
fe4714886d92
identify future results more carefully, to avoid odd duplication of error messages, notably from forked goals;
 wenzelm parents: 
50911diff
changeset | 24 | val identify_result: Position.T -> 'a Exn.result -> 'a Exn.result | 
| 44427 | 25 |   type params = {name: string, group: group option, deps: task list, pri: int, interrupts: bool}
 | 
| 26 | val default_params: params | |
| 27 | val forks: params -> (unit -> 'a) list -> 'a future list | |
| 32724 | 28 | val fork: (unit -> 'a) -> 'a future | 
| 72085 | 29 | val get_result: 'a future -> 'a Exn.result | 
| 28972 | 30 | val join_results: 'a future list -> 'a Exn.result list | 
| 31 | val join_result: 'a future -> 'a Exn.result | |
| 44330 | 32 | val joins: 'a future list -> 'a list | 
| 28972 | 33 | val join: 'a future -> 'a | 
| 67658 | 34 |   val forked_results: {name: string, deps: Task_Queue.task list} ->
 | 
| 35 | (unit -> 'a) list -> 'a Exn.result list | |
| 54649 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 36 |   val task_context: string -> group -> ('a -> 'b) -> 'a -> 'b
 | 
| 68130 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 37 | val enabled: unit -> bool | 
| 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 38 | val relevant: 'a list -> bool | 
| 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 39 | val proofs_enabled: int -> bool | 
| 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 40 | val proofs_enabled_timing: Time.time -> bool | 
| 44294 
a0ddd5760444
clarified Future.cond_forks: more uniform handling of exceptional situations;
 wenzelm parents: 
44268diff
changeset | 41 | val value_result: 'a Exn.result -> 'a future | 
| 34277 
7325a5e3587f
added Future.promise/fulfill -- promised futures that are fulfilled by external means;
 wenzelm parents: 
33416diff
changeset | 42 | val value: 'a -> 'a future | 
| 44427 | 43 | val cond_forks: params -> (unit -> 'a) list -> 'a future list | 
| 28972 | 44 |   val map: ('a -> 'b) -> 'a future -> 'b future
 | 
| 66166 | 45 | val promise_name: string -> (unit -> unit) -> 'a future | 
| 44298 
b8f8488704e2
Future.promise: explicit abort operation (like uninterruptible future job);
 wenzelm parents: 
44295diff
changeset | 46 | val promise: (unit -> unit) -> 'a future | 
| 34277 
7325a5e3587f
added Future.promise/fulfill -- promised futures that are fulfilled by external means;
 wenzelm parents: 
33416diff
changeset | 47 | val fulfill_result: 'a future -> 'a Exn.result -> unit | 
| 
7325a5e3587f
added Future.promise/fulfill -- promised futures that are fulfilled by external means;
 wenzelm parents: 
33416diff
changeset | 48 | val fulfill: 'a future -> 'a -> unit | 
| 66378 | 49 | val snapshot: group list -> task list | 
| 28203 | 50 | val shutdown: unit -> unit | 
| 28156 | 51 | end; | 
| 52 | ||
| 53 | structure Future: FUTURE = | |
| 54 | struct | |
| 55 | ||
| 28177 
8c0335bc9336
inherit group from running thread, or create a new one -- make it harder to re-use canceled groups;
 wenzelm parents: 
28170diff
changeset | 56 | (** future values **) | 
| 
8c0335bc9336
inherit group from running thread, or create a new one -- make it harder to re-use canceled groups;
 wenzelm parents: 
28170diff
changeset | 57 | |
| 44300 
349cc426d929
tuned signature -- treat structure Task_Queue as private to implementation;
 wenzelm parents: 
44299diff
changeset | 58 | type task = Task_Queue.task; | 
| 
349cc426d929
tuned signature -- treat structure Task_Queue as private to implementation;
 wenzelm parents: 
44299diff
changeset | 59 | type group = Task_Queue.group; | 
| 
349cc426d929
tuned signature -- treat structure Task_Queue as private to implementation;
 wenzelm parents: 
44299diff
changeset | 60 | val new_group = Task_Queue.new_group; | 
| 
349cc426d929
tuned signature -- treat structure Task_Queue as private to implementation;
 wenzelm parents: 
44299diff
changeset | 61 | |
| 
349cc426d929
tuned signature -- treat structure Task_Queue as private to implementation;
 wenzelm parents: 
44299diff
changeset | 62 | |
| 28167 | 63 | (* identifiers *) | 
| 64 | ||
| 32058 | 65 | local | 
| 62889 | 66 | val worker_task_var = Thread_Data.var () : task Thread_Data.var; | 
| 32058 | 67 | in | 
| 62889 | 68 | fun worker_task () = Thread_Data.get worker_task_var; | 
| 69 | fun setmp_worker_task task f x = Thread_Data.setmp worker_task_var (SOME task) f x; | |
| 28167 | 70 | end; | 
| 71 | ||
| 41683 
73dde8006820
maintain Task_Queue.group within Task_Queue.task;
 wenzelm parents: 
41681diff
changeset | 72 | val worker_group = Option.map Task_Queue.group_of_task o worker_task; | 
| 52603 | 73 | |
| 74 | fun the_worker_group () = | |
| 75 | (case worker_group () of | |
| 76 | SOME group => group | |
| 77 | | NONE => raise Fail "Missing worker thread context"); | |
| 78 | ||
| 44300 
349cc426d929
tuned signature -- treat structure Task_Queue as private to implementation;
 wenzelm parents: 
44299diff
changeset | 79 | fun worker_subgroup () = new_group (worker_group ()); | 
| 34277 
7325a5e3587f
added Future.promise/fulfill -- promised futures that are fulfilled by external means;
 wenzelm parents: 
33416diff
changeset | 80 | |
| 41679 | 81 | fun worker_joining e = | 
| 82 | (case worker_task () of | |
| 83 | NONE => e () | |
| 84 | | SOME task => Task_Queue.joining task e); | |
| 85 | ||
| 41680 
a4c822915eaa
more informative task timing: some dependency tracking;
 wenzelm parents: 
41679diff
changeset | 86 | fun worker_waiting deps e = | 
| 41670 | 87 | (case worker_task () of | 
| 88 | NONE => e () | |
| 41680 
a4c822915eaa
more informative task timing: some dependency tracking;
 wenzelm parents: 
41679diff
changeset | 89 | | SOME task => Task_Queue.waiting task deps e); | 
| 41670 | 90 | |
| 28167 | 91 | |
| 92 | (* datatype future *) | |
| 93 | ||
| 35016 | 94 | type 'a result = 'a Exn.result Single_Assignment.var; | 
| 95 | ||
| 66958 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 96 | datatype 'a future = | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 97 | Value of 'a Exn.result | | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 98 | Future of | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 99 |    {promised: bool,
 | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 100 | task: task, | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 101 | result: 'a result}; | 
| 28167 | 102 | |
| 66958 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 103 | fun task_of (Value _) = Task_Queue.dummy_task | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 104 |   | task_of (Future {task, ...}) = task;
 | 
| 28167 | 105 | |
| 66958 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 106 | fun peek (Value res) = SOME res | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 107 |   | peek (Future {result, ...}) = Single_Assignment.peek result;
 | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 108 | |
| 28558 | 109 | fun is_finished x = is_some (peek x); | 
| 28320 | 110 | |
| 62663 | 111 | val _ = | 
| 62819 
d3ff367a16a0
careful export of type-dependent functions, without losing their special status;
 wenzelm parents: 
62663diff
changeset | 112 | ML_system_pp (fn depth => fn pretty => fn x => | 
| 62663 | 113 | (case peek x of | 
| 68918 
3a0db30e5d87
simplified signature (again, see 751bcf0473a7): e.g. relevant for non-Isabelle ML environments;
 wenzelm parents: 
68379diff
changeset | 114 | NONE => PolyML.PrettyString "<future>" | 
| 
3a0db30e5d87
simplified signature (again, see 751bcf0473a7): e.g. relevant for non-Isabelle ML environments;
 wenzelm parents: 
68379diff
changeset | 115 | | SOME (Exn.Exn _) => PolyML.PrettyString "<failed>" | 
| 62663 | 116 | | SOME (Exn.Res y) => pretty (y, depth))); | 
| 117 | ||
| 28167 | 118 | |
| 28177 
8c0335bc9336
inherit group from running thread, or create a new one -- make it harder to re-use canceled groups;
 wenzelm parents: 
28170diff
changeset | 119 | |
| 
8c0335bc9336
inherit group from running thread, or create a new one -- make it harder to re-use canceled groups;
 wenzelm parents: 
28170diff
changeset | 120 | (** scheduling **) | 
| 
8c0335bc9336
inherit group from running thread, or create a new one -- make it harder to re-use canceled groups;
 wenzelm parents: 
28170diff
changeset | 121 | |
| 
8c0335bc9336
inherit group from running thread, or create a new one -- make it harder to re-use canceled groups;
 wenzelm parents: 
28170diff
changeset | 122 | (* synchronization *) | 
| 28156 | 123 | |
| 32219 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 124 | val scheduler_event = ConditionVar.conditionVar (); | 
| 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 125 | val work_available = ConditionVar.conditionVar (); | 
| 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 126 | val work_finished = ConditionVar.conditionVar (); | 
| 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 127 | |
| 28156 | 128 | local | 
| 129 | val lock = Mutex.mutex (); | |
| 130 | in | |
| 131 | ||
| 59054 
61b723761dff
load simple_thread.ML later, such that it benefits from redefined print_exception_trace;
 wenzelm parents: 
57350diff
changeset | 132 | fun SYNCHRONIZED name = Multithreading.synchronized name lock; | 
| 28156 | 133 | |
| 32219 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 134 | fun wait cond = (*requires SYNCHRONIZED*) | 
| 64276 | 135 | Multithreading.sync_wait NONE cond lock; | 
| 28206 
bcd48c6897d4
eliminated requests, use global state variables uniformly;
 wenzelm parents: 
28203diff
changeset | 136 | |
| 32295 
400cc493d466
renamed Multithreading.regular_interrupts to Multithreading.public_interrupts;
 wenzelm parents: 
32293diff
changeset | 137 | fun wait_timeout timeout cond = (*requires SYNCHRONIZED*) | 
| 64276 | 138 | Multithreading.sync_wait (SOME (Time.now () + timeout)) cond lock; | 
| 28166 
43087721a66e
moved task, thread_data, group, queue to task_queue.ML;
 wenzelm parents: 
28163diff
changeset | 139 | |
| 32219 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 140 | fun signal cond = (*requires SYNCHRONIZED*) | 
| 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 141 | ConditionVar.signal cond; | 
| 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 142 | |
| 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 143 | fun broadcast cond = (*requires SYNCHRONIZED*) | 
| 28166 
43087721a66e
moved task, thread_data, group, queue to task_queue.ML;
 wenzelm parents: 
28163diff
changeset | 144 | ConditionVar.broadcast cond; | 
| 28156 | 145 | |
| 146 | end; | |
| 147 | ||
| 148 | ||
| 33410 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 149 | (* global state *) | 
| 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 150 | |
| 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 151 | val queue = Unsynchronized.ref Task_Queue.empty; | 
| 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 152 | val next = Unsynchronized.ref 0; | 
| 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 153 | val scheduler = Unsynchronized.ref (NONE: Thread.thread option); | 
| 44300 
349cc426d929
tuned signature -- treat structure Task_Queue as private to implementation;
 wenzelm parents: 
44299diff
changeset | 154 | val canceled = Unsynchronized.ref ([]: group list); | 
| 33410 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 155 | val do_shutdown = Unsynchronized.ref false; | 
| 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 156 | val max_workers = Unsynchronized.ref 0; | 
| 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 157 | val max_active = Unsynchronized.ref 0; | 
| 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 158 | |
| 50280 | 159 | val status_ticks = Unsynchronized.ref 0; | 
| 160 | val last_round = Unsynchronized.ref Time.zeroTime; | |
| 161 | val next_round = seconds 0.05; | |
| 162 | ||
| 33410 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 163 | datatype worker_state = Working | Waiting | Sleeping; | 
| 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 164 | val workers = Unsynchronized.ref ([]: (Thread.thread * worker_state Unsynchronized.ref) list); | 
| 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 165 | |
| 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 166 | fun count_workers state = (*requires SYNCHRONIZED*) | 
| 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 167 | fold (fn (_, state_ref) => fn i => if ! state_ref = state then i + 1 else i) (! workers) 0; | 
| 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 168 | |
| 
e351f4c1f18c
worker activity: distinguish between waiting (formerly active) and sleeping;
 wenzelm parents: 
33409diff
changeset | 169 | |
| 72112 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 170 | (* ML statistics *) | 
| 50280 | 171 | |
| 72112 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 172 | fun ML_statistics () = (*requires SYNCHRONIZED*) | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 173 | let | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 174 |     val {ready, pending, running, passive, urgent} = Task_Queue.status (! queue);
 | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 175 | val workers_total = length (! workers); | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 176 | val workers_active = count_workers Working; | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 177 | val workers_waiting = count_workers Waiting; | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 178 | in | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 179 | ML_Statistics.set | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 180 |      {tasks_ready = ready,
 | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 181 | tasks_pending = pending, | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 182 | tasks_running = running, | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 183 | tasks_passive = passive, | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 184 | tasks_urgent = urgent, | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 185 | workers_total = workers_total, | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 186 | workers_active = workers_active, | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 187 | workers_waiting = workers_waiting} | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 188 | end; | 
| 50280 | 189 | |
| 190 | ||
| 44110 | 191 | (* cancellation primitives *) | 
| 32099 | 192 | |
| 34279 
02936e77a07c
tasks of canceled groups are considered "ready" -- enables to purge the queue from tasks depending on unfinished promises (also improves general reactivity);
 wenzelm parents: 
34277diff
changeset | 193 | fun cancel_now group = (*requires SYNCHRONIZED*) | 
| 44341 
a93d25fb14fc
purely functional task_queue.ML -- moved actual interrupt_unsynchronized to future.ML;
 wenzelm parents: 
44330diff
changeset | 194 | let | 
| 47404 
e6e5750f1311
simplified Future.cancel/cancel_group (again) -- running threads only;
 wenzelm parents: 
45666diff
changeset | 195 | val running = Task_Queue.cancel (! queue) group; | 
| 49894 
69bfd86cc711
more robust cancel_now: avoid shooting yourself in the foot;
 wenzelm parents: 
49009diff
changeset | 196 | val _ = running |> List.app (fn thread => | 
| 71692 | 197 | if Isabelle_Thread.is_self thread then () | 
| 198 | else Isabelle_Thread.interrupt_unsynchronized thread); | |
| 47404 
e6e5750f1311
simplified Future.cancel/cancel_group (again) -- running threads only;
 wenzelm parents: 
45666diff
changeset | 199 | in running end; | 
| 44341 
a93d25fb14fc
purely functional task_queue.ML -- moved actual interrupt_unsynchronized to future.ML;
 wenzelm parents: 
44330diff
changeset | 200 | |
| 
a93d25fb14fc
purely functional task_queue.ML -- moved actual interrupt_unsynchronized to future.ML;
 wenzelm parents: 
44330diff
changeset | 201 | fun cancel_all () = (*requires SYNCHRONIZED*) | 
| 
a93d25fb14fc
purely functional task_queue.ML -- moved actual interrupt_unsynchronized to future.ML;
 wenzelm parents: 
44330diff
changeset | 202 | let | 
| 
a93d25fb14fc
purely functional task_queue.ML -- moved actual interrupt_unsynchronized to future.ML;
 wenzelm parents: 
44330diff
changeset | 203 | val (groups, threads) = Task_Queue.cancel_all (! queue); | 
| 71692 | 204 | val _ = List.app Isabelle_Thread.interrupt_unsynchronized threads; | 
| 44341 
a93d25fb14fc
purely functional task_queue.ML -- moved actual interrupt_unsynchronized to future.ML;
 wenzelm parents: 
44330diff
changeset | 205 | in groups end; | 
| 34279 
02936e77a07c
tasks of canceled groups are considered "ready" -- enables to purge the queue from tasks depending on unfinished promises (also improves general reactivity);
 wenzelm parents: 
34277diff
changeset | 206 | |
| 
02936e77a07c
tasks of canceled groups are considered "ready" -- enables to purge the queue from tasks depending on unfinished promises (also improves general reactivity);
 wenzelm parents: 
34277diff
changeset | 207 | fun cancel_later group = (*requires SYNCHRONIZED*) | 
| 32738 | 208 | (Unsynchronized.change canceled (insert Task_Queue.eq_group group); | 
| 209 | broadcast scheduler_event); | |
| 29341 
6bb007a0f9f2
more reactive scheduler: reduced loop timeout, propagate broadcast interrupt via TaskQueue.cancel_all;
 wenzelm parents: 
29119diff
changeset | 210 | |
| 44301 | 211 | fun interruptible_task f x = | 
| 62923 | 212 | Thread_Attributes.with_attributes | 
| 62359 | 213 | (if is_some (worker_task ()) | 
| 62923 | 214 | then Thread_Attributes.private_interrupts | 
| 215 | else Thread_Attributes.public_interrupts) | |
| 62359 | 216 | (fn _ => f x) | 
| 62924 
ce47945ce4fb
tuned signature -- closer to Exn.Interrupt.expose in Scala;
 wenzelm parents: 
62923diff
changeset | 217 | before Thread_Attributes.expose_interrupt (); | 
| 44301 | 218 | |
| 219 | ||
| 44110 | 220 | (* worker threads *) | 
| 221 | ||
| 222 | fun worker_exec (task, jobs) = | |
| 28167 | 223 | let | 
| 41683 
73dde8006820
maintain Task_Queue.group within Task_Queue.task;
 wenzelm parents: 
41681diff
changeset | 224 | val group = Task_Queue.group_of_task task; | 
| 32102 | 225 | val valid = not (Task_Queue.is_canceled group); | 
| 41670 | 226 | val ok = | 
| 227 | Task_Queue.running task (fn () => | |
| 41683 
73dde8006820
maintain Task_Queue.group within Task_Queue.task;
 wenzelm parents: 
41681diff
changeset | 228 | setmp_worker_task task (fn () => | 
| 41670 | 229 | fold (fn job => fn ok => job valid andalso ok) jobs true) ()); | 
| 50975 | 230 | val _ = | 
| 231 | if ! Multithreading.trace >= 2 then | |
| 56333 
38f1422ef473
support bulk messages consisting of small string segments, which are more healthy to the Poly/ML RTS and might prevent spurious GC crashes such as MTGCProcessMarkPointers::ScanAddressesInObject;
 wenzelm parents: 
54671diff
changeset | 232 | Output.try_protocol_message (Markup.task_statistics :: Task_Queue.task_statistics task) [] | 
| 50975 | 233 | else (); | 
| 32246 | 234 | val _ = SYNCHRONIZED "finish" (fn () => | 
| 32219 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 235 | let | 
| 32738 | 236 | val maximal = Unsynchronized.change_result queue (Task_Queue.finish task); | 
| 62924 
ce47945ce4fb
tuned signature -- closer to Exn.Interrupt.expose in Scala;
 wenzelm parents: 
62923diff
changeset | 237 | val test = Exn.capture Thread_Attributes.expose_interrupt (); | 
| 32219 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 238 | val _ = | 
| 44295 
e43f0ea90c9a
more focused use of Multithreading.interrupted: retain interrupts within task group boundary, without loss of information;
 wenzelm parents: 
44294diff
changeset | 239 | if ok andalso not (Exn.is_interrupt_exn test) then () | 
| 44299 
061599cb6eb0
refined Future.cancel: explicit future allows to join actual cancellation;
 wenzelm parents: 
44298diff
changeset | 240 | else if null (cancel_now group) then () | 
| 34279 
02936e77a07c
tasks of canceled groups are considered "ready" -- enables to purge the queue from tasks depending on unfinished promises (also improves general reactivity);
 wenzelm parents: 
34277diff
changeset | 241 | else cancel_later group; | 
| 32219 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 242 | val _ = broadcast work_finished; | 
| 33413 
cb409680dda8
avoid broadcast work_available, use daisy-chained signal instead;
 wenzelm parents: 
33411diff
changeset | 243 | val _ = if maximal then () else signal work_available; | 
| 32219 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 244 | in () end); | 
| 28167 | 245 | in () end; | 
| 246 | ||
| 59465 | 247 | fun worker_wait worker_state cond = (*requires SYNCHRONIZED*) | 
| 52558 
271663ddf289
allow worker guest threads, which participate actively in future joins, but are outside thread accounting;
 wenzelm parents: 
51990diff
changeset | 248 | (case AList.lookup Thread.equal (! workers) (Thread.self ()) of | 
| 59465 | 249 | SOME state => Unsynchronized.setmp state worker_state wait cond | 
| 250 | | NONE => wait cond); | |
| 28162 | 251 | |
| 33415 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 252 | fun worker_next () = (*requires SYNCHRONIZED*) | 
| 33406 
1ddcb8472bd2
slightly leaner and more direct control of worker activity etc.;
 wenzelm parents: 
33061diff
changeset | 253 | if length (! workers) > ! max_workers then | 
| 
1ddcb8472bd2
slightly leaner and more direct control of worker activity etc.;
 wenzelm parents: 
33061diff
changeset | 254 | (Unsynchronized.change workers (AList.delete Thread.equal (Thread.self ())); | 
| 33415 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 255 | signal work_available; | 
| 28167 | 256 | NONE) | 
| 28166 
43087721a66e
moved task, thread_data, group, queue to task_queue.ML;
 wenzelm parents: 
28163diff
changeset | 257 | else | 
| 60610 
f52b4b0c10c4
improved scheduling for urgent tasks, using farm of replacement threads (may lead to factor 2 overloading, but CPUs are usually hyperthreaded);
 wenzelm parents: 
59468diff
changeset | 258 | let val urgent_only = count_workers Working > ! max_active in | 
| 
f52b4b0c10c4
improved scheduling for urgent tasks, using farm of replacement threads (may lead to factor 2 overloading, but CPUs are usually hyperthreaded);
 wenzelm parents: 
59468diff
changeset | 259 | (case Unsynchronized.change_result queue (Task_Queue.dequeue (Thread.self ()) urgent_only) of | 
| 
f52b4b0c10c4
improved scheduling for urgent tasks, using farm of replacement threads (may lead to factor 2 overloading, but CPUs are usually hyperthreaded);
 wenzelm parents: 
59468diff
changeset | 260 | NONE => (worker_wait Sleeping work_available; worker_next ()) | 
| 
f52b4b0c10c4
improved scheduling for urgent tasks, using farm of replacement threads (may lead to factor 2 overloading, but CPUs are usually hyperthreaded);
 wenzelm parents: 
59468diff
changeset | 261 | | some => (signal work_available; some)) | 
| 
f52b4b0c10c4
improved scheduling for urgent tasks, using farm of replacement threads (may lead to factor 2 overloading, but CPUs are usually hyperthreaded);
 wenzelm parents: 
59468diff
changeset | 262 | end; | 
| 28156 | 263 | |
| 28167 | 264 | fun worker_loop name = | 
| 33415 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 265 | (case SYNCHRONIZED name (fn () => worker_next ()) of | 
| 29119 | 266 | NONE => () | 
| 44295 
e43f0ea90c9a
more focused use of Multithreading.interrupted: retain interrupts within task group boundary, without loss of information;
 wenzelm parents: 
44294diff
changeset | 267 | | SOME work => (worker_exec work; worker_loop name)); | 
| 28156 | 268 | |
| 33407 
1427333220bc
worker_next: ensure that work_available is passed on before sleeping (was occasionally lost when worker configuration changed, causing scheduler deadlock);
 wenzelm parents: 
33406diff
changeset | 269 | fun worker_start name = (*requires SYNCHRONIZED*) | 
| 59468 
fe6651760643
explicit threads_stack_limit (for recent Poly/ML SVN versions), which leads to soft interrupt instead of exhaustion of virtual memory, which is particularly relevant for the bigger address space of x86_64;
 wenzelm parents: 
59467diff
changeset | 270 | let | 
| 
fe6651760643
explicit threads_stack_limit (for recent Poly/ML SVN versions), which leads to soft interrupt instead of exhaustion of virtual memory, which is particularly relevant for the bigger address space of x86_64;
 wenzelm parents: 
59467diff
changeset | 271 | val worker = | 
| 71883 | 272 | Isabelle_Thread.fork | 
| 273 |         {name = "worker", stack_limit = Isabelle_Thread.stack_limit (), interrupts = false}
 | |
| 59468 
fe6651760643
explicit threads_stack_limit (for recent Poly/ML SVN versions), which leads to soft interrupt instead of exhaustion of virtual memory, which is particularly relevant for the bigger address space of x86_64;
 wenzelm parents: 
59467diff
changeset | 274 | (fn () => worker_loop name); | 
| 
fe6651760643
explicit threads_stack_limit (for recent Poly/ML SVN versions), which leads to soft interrupt instead of exhaustion of virtual memory, which is particularly relevant for the bigger address space of x86_64;
 wenzelm parents: 
59467diff
changeset | 275 | in Unsynchronized.change workers (cons (worker, Unsynchronized.ref Working)) end | 
| 59338 
2ea1bf517842
discontinued worker_trend: prefer constant number of active + reserve threads;
 wenzelm parents: 
59330diff
changeset | 276 | handle Fail msg => Multithreading.tracing 0 (fn () => "SCHEDULER: " ^ msg); | 
| 59330 
cb3a4caf206d
permissive worker_start: failure to fork thread is deferred to later attempt to provide missing threads, without crashing scheduler;
 wenzelm parents: 
59180diff
changeset | 277 | |
| 28156 | 278 | |
| 279 | (* scheduler *) | |
| 280 | ||
| 72078 
b8d0b8659e0a
more robust scheduler shutdown, notably for spurious crashes;
 wenzelm parents: 
72038diff
changeset | 281 | fun scheduler_end () = (*requires SYNCHRONIZED*) | 
| 72112 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 282 | (ML_statistics (); scheduler := NONE); | 
| 72078 
b8d0b8659e0a
more robust scheduler shutdown, notably for spurious crashes;
 wenzelm parents: 
72038diff
changeset | 283 | |
| 28206 
bcd48c6897d4
eliminated requests, use global state variables uniformly;
 wenzelm parents: 
28203diff
changeset | 284 | fun scheduler_next () = (*requires SYNCHRONIZED*) | 
| 28156 | 285 | let | 
| 33407 
1427333220bc
worker_next: ensure that work_available is passed on before sleeping (was occasionally lost when worker configuration changed, causing scheduler deadlock);
 wenzelm parents: 
33406diff
changeset | 286 | val now = Time.now (); | 
| 62826 | 287 | val tick = ! last_round + next_round <= now; | 
| 33407 
1427333220bc
worker_next: ensure that work_available is passed on before sleeping (was occasionally lost when worker configuration changed, causing scheduler deadlock);
 wenzelm parents: 
33406diff
changeset | 288 | val _ = if tick then last_round := now else (); | 
| 
1427333220bc
worker_next: ensure that work_available is passed on before sleeping (was occasionally lost when worker configuration changed, causing scheduler deadlock);
 wenzelm parents: 
33406diff
changeset | 289 | |
| 33415 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 290 | |
| 50280 | 291 | (* runtime status *) | 
| 33415 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 292 | |
| 32226 | 293 | val _ = | 
| 51046 | 294 | if tick then Unsynchronized.change status_ticks (fn i => i + 1) else (); | 
| 33407 
1427333220bc
worker_next: ensure that work_available is passed on before sleeping (was occasionally lost when worker configuration changed, causing scheduler deadlock);
 wenzelm parents: 
33406diff
changeset | 295 | val _ = | 
| 72112 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 296 | if tick andalso ! status_ticks mod (if ! Multithreading.trace >= 1 then 2 else 5) = 0 | 
| 
3546dd4ade74
ML statistics via external process: allows monitoring RTS while ML program sleeps;
 wenzelm parents: 
72086diff
changeset | 297 | then ML_statistics () else (); | 
| 32053 | 298 | |
| 28191 | 299 | val _ = | 
| 59338 
2ea1bf517842
discontinued worker_trend: prefer constant number of active + reserve threads;
 wenzelm parents: 
59330diff
changeset | 300 | if not tick orelse forall (Thread.isActive o #1) (! workers) then () | 
| 32095 | 301 | else | 
| 33409 | 302 | let | 
| 37682 | 303 | val (alive, dead) = List.partition (Thread.isActive o #1) (! workers); | 
| 33409 | 304 | val _ = workers := alive; | 
| 305 | in | |
| 306 | Multithreading.tracing 0 (fn () => | |
| 51279 | 307 | "SCHEDULER: disposed " ^ string_of_int (length dead) ^ " dead worker threads") | 
| 33409 | 308 | end; | 
| 28191 | 309 | |
| 33415 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 310 | |
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 311 | (* worker pool adjustments *) | 
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 312 | |
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 313 | val max_active0 = ! max_active; | 
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 314 | val max_workers0 = ! max_workers; | 
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 315 | |
| 72086 
41e1e2395a67
avoid exhaustion of worker threads, notably due to complex interaction of future/promise/lazy in Proofterm.make_thm_node;
 wenzelm parents: 
72085diff
changeset | 316 | val workers_waiting = count_workers Waiting; | 
| 
41e1e2395a67
avoid exhaustion of worker threads, notably due to complex interaction of future/promise/lazy in Proofterm.make_thm_node;
 wenzelm parents: 
72085diff
changeset | 317 | |
| 59340 
734bb148503e
explicit shutdown of scheduler and worker thread farm, assuming Session.shutdown() before saving heap image;
 wenzelm parents: 
59338diff
changeset | 318 | val m = | 
| 
734bb148503e
explicit shutdown of scheduler and worker thread farm, assuming Session.shutdown() before saving heap image;
 wenzelm parents: 
59338diff
changeset | 319 | if ! do_shutdown andalso Task_Queue.all_passive (! queue) then 0 | 
| 62925 | 320 | else Multithreading.max_threads (); | 
| 33406 
1ddcb8472bd2
slightly leaner and more direct control of worker activity etc.;
 wenzelm parents: 
33061diff
changeset | 321 | val _ = max_active := m; | 
| 72086 
41e1e2395a67
avoid exhaustion of worker threads, notably due to complex interaction of future/promise/lazy in Proofterm.make_thm_node;
 wenzelm parents: 
72085diff
changeset | 322 | val _ = max_workers := | 
| 
41e1e2395a67
avoid exhaustion of worker threads, notably due to complex interaction of future/promise/lazy in Proofterm.make_thm_node;
 wenzelm parents: 
72085diff
changeset | 323 | Int.max (2 * m, if workers_waiting > 0 then workers_waiting + 1 else 0); | 
| 33406 
1ddcb8472bd2
slightly leaner and more direct control of worker activity etc.;
 wenzelm parents: 
33061diff
changeset | 324 | |
| 33407 
1427333220bc
worker_next: ensure that work_available is passed on before sleeping (was occasionally lost when worker configuration changed, causing scheduler deadlock);
 wenzelm parents: 
33406diff
changeset | 325 | val missing = ! max_workers - length (! workers); | 
| 28203 | 326 | val _ = | 
| 33407 
1427333220bc
worker_next: ensure that work_available is passed on before sleeping (was occasionally lost when worker configuration changed, causing scheduler deadlock);
 wenzelm parents: 
33406diff
changeset | 327 | if missing > 0 then | 
| 33415 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 328 | funpow missing (fn () => | 
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 329 |           ignore (worker_start ("worker " ^ string_of_int (Unsynchronized.inc next)))) ()
 | 
| 28203 | 330 | else (); | 
| 28206 
bcd48c6897d4
eliminated requests, use global state variables uniformly;
 wenzelm parents: 
28203diff
changeset | 331 | |
| 33415 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 332 | val _ = | 
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 333 | if ! max_active = max_active0 andalso ! max_workers = max_workers0 then () | 
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 334 | else signal work_available; | 
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 335 | |
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 336 | |
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 337 | (* canceled groups *) | 
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 338 | |
| 32225 
d5d6f47fb018
cancel: improved reactivity due to more careful broadcasting;
 wenzelm parents: 
32224diff
changeset | 339 | val _ = | 
| 
d5d6f47fb018
cancel: improved reactivity due to more careful broadcasting;
 wenzelm parents: 
32224diff
changeset | 340 | if null (! canceled) then () | 
| 32293 | 341 | else | 
| 342 | (Multithreading.tracing 1 (fn () => | |
| 343 | string_of_int (length (! canceled)) ^ " canceled groups"); | |
| 44299 
061599cb6eb0
refined Future.cancel: explicit future allows to join actual cancellation;
 wenzelm parents: 
44298diff
changeset | 344 | Unsynchronized.change canceled (filter_out (null o cancel_now)); | 
| 51281 
c05f7e1dd602
signal work_available should be sufficient to initiate daisy-chained workers, and lead to separate broadcast work_finished eventually -- NB: broadcasting all worker threads tends to burn parallel CPU cycles;
 wenzelm parents: 
51280diff
changeset | 345 | signal work_available); | 
| 28206 
bcd48c6897d4
eliminated requests, use global state variables uniformly;
 wenzelm parents: 
28203diff
changeset | 346 | |
| 33415 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 347 | |
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 348 | (* delay loop *) | 
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 349 | |
| 32295 
400cc493d466
renamed Multithreading.regular_interrupts to Multithreading.public_interrupts;
 wenzelm parents: 
32293diff
changeset | 350 | val _ = Exn.release (wait_timeout next_round scheduler_event); | 
| 28167 | 351 | |
| 33415 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 352 | |
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 353 | (* shutdown *) | 
| 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 354 | |
| 32219 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 355 | val continue = not (! do_shutdown andalso null (! workers)); | 
| 72078 
b8d0b8659e0a
more robust scheduler shutdown, notably for spurious crashes;
 wenzelm parents: 
72038diff
changeset | 356 | val _ = if continue then () else scheduler_end (); | 
| 33415 
352fe8e9162d
worker_next: plain signalling via work_available only, not scheduler_event;
 wenzelm parents: 
33413diff
changeset | 357 | |
| 32219 
9a2566d1fdbd
more specific conditions: scheduler_event, work_available, work_finished -- considereably reduces overhead with many threads;
 wenzelm parents: 
32186diff
changeset | 358 | val _ = broadcast scheduler_event; | 
| 32295 
400cc493d466
renamed Multithreading.regular_interrupts to Multithreading.public_interrupts;
 wenzelm parents: 
32293diff
changeset | 359 | in continue end | 
| 39232 
69c6d3e87660
more abstract treatment of interrupts in structure Exn -- hardly ever need to mention Interrupt literally;
 wenzelm parents: 
38236diff
changeset | 360 | handle exn => | 
| 72078 
b8d0b8659e0a
more robust scheduler shutdown, notably for spurious crashes;
 wenzelm parents: 
72038diff
changeset | 361 | (Multithreading.tracing 1 (fn () => "SCHEDULER: " ^ General.exnMessage exn); | 
| 39232 
69c6d3e87660
more abstract treatment of interrupts in structure Exn -- hardly ever need to mention Interrupt literally;
 wenzelm parents: 
38236diff
changeset | 362 | if Exn.is_interrupt exn then | 
| 72078 
b8d0b8659e0a
more robust scheduler shutdown, notably for spurious crashes;
 wenzelm parents: 
72038diff
changeset | 363 | (List.app cancel_later (cancel_all ()); | 
| 51281 
c05f7e1dd602
signal work_available should be sufficient to initiate daisy-chained workers, and lead to separate broadcast work_finished eventually -- NB: broadcasting all worker threads tends to burn parallel CPU cycles;
 wenzelm parents: 
51280diff
changeset | 364 | signal work_available; true) | 
| 72078 
b8d0b8659e0a
more robust scheduler shutdown, notably for spurious crashes;
 wenzelm parents: 
72038diff
changeset | 365 | else | 
| 
b8d0b8659e0a
more robust scheduler shutdown, notably for spurious crashes;
 wenzelm parents: 
72038diff
changeset | 366 | (scheduler_end (); | 
| 
b8d0b8659e0a
more robust scheduler shutdown, notably for spurious crashes;
 wenzelm parents: 
72038diff
changeset | 367 | Exn.reraise exn)); | 
| 32295 
400cc493d466
renamed Multithreading.regular_interrupts to Multithreading.public_interrupts;
 wenzelm parents: 
32293diff
changeset | 368 | |
| 28206 
bcd48c6897d4
eliminated requests, use global state variables uniformly;
 wenzelm parents: 
28203diff
changeset | 369 | fun scheduler_loop () = | 
| 44173 
aaaa13e297dc
immediate fork of initial workers -- avoid 5 ticks (250ms) for adaptive scheme (a07558eb5029);
 wenzelm parents: 
44115diff
changeset | 370 | (while | 
| 62923 | 371 | Thread_Attributes.with_attributes | 
| 372 | (Thread_Attributes.sync_interrupts Thread_Attributes.public_interrupts) | |
| 33416 
13d00799fe49
scheduler: clarified interrupt attributes and handling;
 wenzelm parents: 
33415diff
changeset | 373 | (fn _ => SYNCHRONIZED "scheduler" (fn () => scheduler_next ())) | 
| 50429 
f8cd5e53653b
final report_status within SYNCHRONIZED part of scheduler loop: required for sanity of data;
 wenzelm parents: 
50280diff
changeset | 374 | do (); last_round := Time.zeroTime); | 
| 28156 | 375 | |
| 28203 | 376 | fun scheduler_active () = (*requires SYNCHRONIZED*) | 
| 377 | (case ! scheduler of NONE => false | SOME thread => Thread.isActive thread); | |
| 378 | ||
| 32228 
7622c03141b0
scheduler: shutdown spontaneously (after some delay) if queue is empty;
 wenzelm parents: 
32227diff
changeset | 379 | fun scheduler_check () = (*requires SYNCHRONIZED*) | 
| 
7622c03141b0
scheduler: shutdown spontaneously (after some delay) if queue is empty;
 wenzelm parents: 
32227diff
changeset | 380 | (do_shutdown := false; | 
| 32248 
0241916a5f06
more precise treatment of scheduler_event: continous pulse (50ms) instead of flooding, which was burning many CPU cycles in spare threads;
 wenzelm parents: 
32247diff
changeset | 381 | if scheduler_active () then () | 
| 59468 
fe6651760643
explicit threads_stack_limit (for recent Poly/ML SVN versions), which leads to soft interrupt instead of exhaustion of virtual memory, which is particularly relevant for the bigger address space of x86_64;
 wenzelm parents: 
59467diff
changeset | 382 | else | 
| 60764 | 383 | scheduler := | 
| 71692 | 384 |       SOME (Isabelle_Thread.fork {name = "scheduler", stack_limit = NONE, interrupts = false}
 | 
| 60764 | 385 | scheduler_loop)); | 
| 28156 | 386 | |
| 44301 | 387 | |
| 388 | ||
| 389 | (** futures **) | |
| 390 | ||
| 391 | (* cancel *) | |
| 392 | ||
| 49906 | 393 | fun cancel_group_unsynchronized group = (*requires SYNCHRONIZED*) | 
| 44299 
061599cb6eb0
refined Future.cancel: explicit future allows to join actual cancellation;
 wenzelm parents: 
44298diff
changeset | 394 | let | 
| 47421 
9624408d8827
always signal after cancel_group: passive tasks may have become active;
 wenzelm parents: 
47404diff
changeset | 395 | val _ = if null (cancel_now group) then () else cancel_later group; | 
| 
9624408d8827
always signal after cancel_group: passive tasks may have become active;
 wenzelm parents: 
47404diff
changeset | 396 | val _ = signal work_available; | 
| 
9624408d8827
always signal after cancel_group: passive tasks may have become active;
 wenzelm parents: 
47404diff
changeset | 397 | val _ = scheduler_check (); | 
| 49906 | 398 | in () end; | 
| 399 | ||
| 400 | fun cancel_group group = | |
| 401 | SYNCHRONIZED "cancel_group" (fn () => cancel_group_unsynchronized group); | |
| 44299 
061599cb6eb0
refined Future.cancel: explicit future allows to join actual cancellation;
 wenzelm parents: 
44298diff
changeset | 402 | |
| 44301 | 403 | fun cancel x = cancel_group (Task_Queue.group_of_task (task_of x)); | 
| 29366 | 404 | |
| 28156 | 405 | |
| 50914 
fe4714886d92
identify future results more carefully, to avoid odd duplication of error messages, notably from forked goals;
 wenzelm parents: 
50911diff
changeset | 406 | (* results *) | 
| 
fe4714886d92
identify future results more carefully, to avoid odd duplication of error messages, notably from forked goals;
 wenzelm parents: 
50911diff
changeset | 407 | |
| 56333 
38f1422ef473
support bulk messages consisting of small string segments, which are more healthy to the Poly/ML RTS and might prevent spurious GC crashes such as MTGCProcessMarkPointers::ScanAddressesInObject;
 wenzelm parents: 
54671diff
changeset | 408 | fun error_message pos ((serial, msg), exec_id) = | 
| 50916 
fd902b616b48
proper runtime position (cf. fe4714886d92 and Toplevel.error_msg) -- to make error messages actually appear in the document;
 wenzelm parents: 
50914diff
changeset | 409 | Position.setmp_thread_data pos (fn () => | 
| 50931 
a7484a7b6c8a
clarified Future.error_msg: slightly more robust id check, actually suppress displaced messages;
 wenzelm parents: 
50916diff
changeset | 410 | let val id = Position.get_id pos in | 
| 
a7484a7b6c8a
clarified Future.error_msg: slightly more robust id check, actually suppress displaced messages;
 wenzelm parents: 
50916diff
changeset | 411 | if is_none id orelse is_none exec_id orelse id = exec_id | 
| 54387 | 412 | then Output.error_message' (serial, msg) else () | 
| 50931 
a7484a7b6c8a
clarified Future.error_msg: slightly more robust id check, actually suppress displaced messages;
 wenzelm parents: 
50916diff
changeset | 413 | end) (); | 
| 44110 | 414 | |
| 50914 
fe4714886d92
identify future results more carefully, to avoid odd duplication of error messages, notably from forked goals;
 wenzelm parents: 
50911diff
changeset | 415 | fun identify_result pos res = | 
| 61077 
06cca32aa519
thread context for exceptions from forks, e.g. relevant when printing errors;
 wenzelm parents: 
60911diff
changeset | 416 | res |> Exn.map_exn (fn exn => | 
| 
06cca32aa519
thread context for exceptions from forks, e.g. relevant when printing errors;
 wenzelm parents: 
60911diff
changeset | 417 | let val exec_id = | 
| 
06cca32aa519
thread context for exceptions from forks, e.g. relevant when printing errors;
 wenzelm parents: 
60911diff
changeset | 418 | (case Position.get_id pos of | 
| 
06cca32aa519
thread context for exceptions from forks, e.g. relevant when printing errors;
 wenzelm parents: 
60911diff
changeset | 419 | NONE => [] | 
| 
06cca32aa519
thread context for exceptions from forks, e.g. relevant when printing errors;
 wenzelm parents: 
60911diff
changeset | 420 | | SOME id => [(Markup.exec_idN, id)]) | 
| 
06cca32aa519
thread context for exceptions from forks, e.g. relevant when printing errors;
 wenzelm parents: 
60911diff
changeset | 421 | in Par_Exn.identify exec_id exn end); | 
| 50914 
fe4714886d92
identify future results more carefully, to avoid odd duplication of error messages, notably from forked goals;
 wenzelm parents: 
50911diff
changeset | 422 | |
| 
fe4714886d92
identify future results more carefully, to avoid odd duplication of error messages, notably from forked goals;
 wenzelm parents: 
50911diff
changeset | 423 | fun assign_result group result res = | 
| 44110 | 424 | let | 
| 425 | val _ = Single_Assignment.assign result res | |
| 426 | handle exn as Fail _ => | |
| 427 | (case Single_Assignment.peek result of | |
| 62505 | 428 | SOME (Exn.Exn e) => Exn.reraise (if Exn.is_interrupt e then e else exn) | 
| 429 | | _ => Exn.reraise exn); | |
| 44110 | 430 | val ok = | 
| 431 | (case the (Single_Assignment.peek result) of | |
| 44111 
2d16c693d536
synchronized cancel and flushing of Multithreading.interrupted state, to ensure that interrupts stay within task boundaries;
 wenzelm parents: 
44110diff
changeset | 432 | Exn.Exn exn => | 
| 
2d16c693d536
synchronized cancel and flushing of Multithreading.interrupted state, to ensure that interrupts stay within task boundaries;
 wenzelm parents: 
44110diff
changeset | 433 | (SYNCHRONIZED "cancel" (fn () => Task_Queue.cancel_group group exn); false) | 
| 44110 | 434 | | Exn.Res _ => true); | 
| 435 | in ok end; | |
| 436 | ||
| 50914 
fe4714886d92
identify future results more carefully, to avoid odd duplication of error messages, notably from forked goals;
 wenzelm parents: 
50911diff
changeset | 437 | |
| 
fe4714886d92
identify future results more carefully, to avoid odd duplication of error messages, notably from forked goals;
 wenzelm parents: 
50911diff
changeset | 438 | (* future jobs *) | 
| 
fe4714886d92
identify future results more carefully, to avoid odd duplication of error messages, notably from forked goals;
 wenzelm parents: 
50911diff
changeset | 439 | |
| 54649 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 440 | fun future_job group atts (e: unit -> 'a) = | 
| 44110 | 441 | let | 
| 442 | val result = Single_Assignment.var "future" : 'a result; | |
| 443 | val pos = Position.thread_data (); | |
| 62889 | 444 | val context = Context.get_generic_context (); | 
| 44110 | 445 | fun job ok = | 
| 446 | let | |
| 447 | val res = | |
| 448 | if ok then | |
| 449 | Exn.capture (fn () => | |
| 62923 | 450 | Thread_Attributes.with_attributes atts (fn _ => | 
| 62889 | 451 | (Position.setmp_thread_data pos o Context.setmp_generic_context context) e ())) () | 
| 44110 | 452 | else Exn.interrupt_exn; | 
| 50914 
fe4714886d92
identify future results more carefully, to avoid odd duplication of error messages, notably from forked goals;
 wenzelm parents: 
50911diff
changeset | 453 | in assign_result group result (identify_result pos res) end; | 
| 44110 | 454 | in (result, job) end; | 
| 455 | ||
| 456 | ||
| 29366 | 457 | (* fork *) | 
| 458 | ||
| 44427 | 459 | type params = {name: string, group: group option, deps: task list, pri: int, interrupts: bool};
 | 
| 460 | val default_params: params = {name = "", group = NONE, deps = [], pri = 0, interrupts = true};
 | |
| 44113 | 461 | |
| 44427 | 462 | fun forks ({name, group, deps, pri, interrupts}: params) es =
 | 
| 41674 | 463 | if null es then [] | 
| 464 | else | |
| 465 | let | |
| 466 | val grp = | |
| 467 | (case group of | |
| 468 | NONE => worker_subgroup () | |
| 469 | | SOME grp => grp); | |
| 41708 | 470 | fun enqueue e queue = | 
| 41674 | 471 | let | 
| 54649 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 472 | val atts = | 
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 473 | if interrupts | 
| 62923 | 474 | then Thread_Attributes.private_interrupts | 
| 475 | else Thread_Attributes.no_interrupts; | |
| 54649 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 476 | val (result, job) = future_job grp atts e; | 
| 41708 | 477 | val (task, queue') = Task_Queue.enqueue name grp deps pri job queue; | 
| 41683 
73dde8006820
maintain Task_Queue.group within Task_Queue.task;
 wenzelm parents: 
41681diff
changeset | 478 |           val future = Future {promised = false, task = task, result = result};
 | 
| 41708 | 479 | in (future, queue') end; | 
| 41674 | 480 | in | 
| 481 | SYNCHRONIZED "enqueue" (fn () => | |
| 482 | let | |
| 41708 | 483 | val (futures, queue') = fold_map enqueue es (! queue); | 
| 484 | val _ = queue := queue'; | |
| 485 | val minimal = forall (not o Task_Queue.known_task queue') deps; | |
| 41674 | 486 | val _ = if minimal then signal work_available else (); | 
| 487 | val _ = scheduler_check (); | |
| 488 | in futures end) | |
| 489 | end; | |
| 28162 | 490 | |
| 50983 | 491 | fun fork e = | 
| 492 |   (singleton o forks) {name = "fork", group = NONE, deps = [], pri = 0, interrupts = true} e;
 | |
| 28186 | 493 | |
| 494 | ||
| 29366 | 495 | (* join *) | 
| 496 | ||
| 32099 | 497 | fun get_result x = | 
| 498 | (case peek x of | |
| 37852 
a902f158b4fc
eliminated old-style sys_error/SYS_ERROR in favour of exception Fail -- after careful checking that there is no overlap with existing handling of that;
 wenzelm parents: 
37690diff
changeset | 499 | NONE => Exn.Exn (Fail "Unfinished future") | 
| 39232 
69c6d3e87660
more abstract treatment of interrupts in structure Exn -- hardly ever need to mention Interrupt literally;
 wenzelm parents: 
38236diff
changeset | 500 | | SOME res => | 
| 
69c6d3e87660
more abstract treatment of interrupts in structure Exn -- hardly ever need to mention Interrupt literally;
 wenzelm parents: 
38236diff
changeset | 501 | if Exn.is_interrupt_exn res then | 
| 44247 | 502 | (case Task_Queue.group_status (Task_Queue.group_of_task (task_of x)) of | 
| 53190 
5d92649a310e
simplified Goal.forked_proofs: status is determined via group instead of dummy future (see also Pure/PIDE/execution.ML);
 wenzelm parents: 
53189diff
changeset | 503 | [] => res | 
| 
5d92649a310e
simplified Goal.forked_proofs: status is determined via group instead of dummy future (see also Pure/PIDE/execution.ML);
 wenzelm parents: 
53189diff
changeset | 504 | | exns => Exn.Exn (Par_Exn.make exns)) | 
| 39232 
69c6d3e87660
more abstract treatment of interrupts in structure Exn -- hardly ever need to mention Interrupt literally;
 wenzelm parents: 
38236diff
changeset | 505 | else res); | 
| 28186 | 506 | |
| 49935 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 507 | local | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 508 | |
| 59465 | 509 | fun join_next atts deps = (*requires SYNCHRONIZED*) | 
| 41695 
afdbec23b92b
eliminated slightly odd abstract type Task_Queue.deps;
 wenzelm parents: 
41683diff
changeset | 510 | if null deps then NONE | 
| 32224 | 511 | else | 
| 41681 
b5d7b15166bf
Future.join_results: discontinued post-hoc recording of dynamic dependencies;
 wenzelm parents: 
41680diff
changeset | 512 | (case Unsynchronized.change_result queue (Task_Queue.dequeue_deps (Thread.self ()) deps) of | 
| 41695 
afdbec23b92b
eliminated slightly odd abstract type Task_Queue.deps;
 wenzelm parents: 
41683diff
changeset | 513 | (NONE, []) => NONE | 
| 
afdbec23b92b
eliminated slightly odd abstract type Task_Queue.deps;
 wenzelm parents: 
41683diff
changeset | 514 | | (NONE, deps') => | 
| 59465 | 515 | (worker_waiting deps' (fn () => | 
| 62923 | 516 | Thread_Attributes.with_attributes atts (fn _ => | 
| 59465 | 517 | Exn.release (worker_wait Waiting work_finished))); | 
| 518 | join_next atts deps') | |
| 32224 | 519 | | (SOME work, deps') => SOME (work, deps')); | 
| 32095 | 520 | |
| 59465 | 521 | fun join_loop atts deps = | 
| 522 | (case SYNCHRONIZED "join" (fn () => join_next atts deps) of | |
| 523 | NONE => () | |
| 524 | | SOME (work, deps') => (worker_joining (fn () => worker_exec work); join_loop atts deps')); | |
| 32814 
81897d30b97f
added Task_Queue.depend (again) -- light-weight version for transitive graph;
 wenzelm parents: 
32738diff
changeset | 525 | |
| 29551 
95e469919c3e
join_results: when dependencies are resulved (but not finished yet),
 wenzelm parents: 
29431diff
changeset | 526 | in | 
| 
95e469919c3e
join_results: when dependencies are resulved (but not finished yet),
 wenzelm parents: 
29431diff
changeset | 527 | |
| 29366 | 528 | fun join_results xs = | 
| 41679 | 529 | let | 
| 530 | val _ = | |
| 531 | if forall is_finished xs then () | |
| 59465 | 532 | else if is_some (worker_task ()) then | 
| 62923 | 533 | Thread_Attributes.with_attributes Thread_Attributes.no_interrupts | 
| 59465 | 534 | (fn orig_atts => join_loop orig_atts (map task_of xs)) | 
| 66958 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 535 | else | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 536 | xs |> List.app | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 537 | (fn Value _ => () | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 538 |             | Future {result, ...} => ignore (Single_Assignment.await result));
 | 
| 41679 | 539 | in map get_result xs end; | 
| 28186 | 540 | |
| 29551 
95e469919c3e
join_results: when dependencies are resulved (but not finished yet),
 wenzelm parents: 
29431diff
changeset | 541 | end; | 
| 
95e469919c3e
join_results: when dependencies are resulved (but not finished yet),
 wenzelm parents: 
29431diff
changeset | 542 | |
| 28647 
8068cdc84e7e
join_results: allow CRITICAL join of finished futures;
 wenzelm parents: 
28645diff
changeset | 543 | fun join_result x = singleton join_results x; | 
| 44330 | 544 | fun joins xs = Par_Exn.release_all (join_results xs); | 
| 28647 
8068cdc84e7e
join_results: allow CRITICAL join of finished futures;
 wenzelm parents: 
28645diff
changeset | 545 | fun join x = Exn.release (join_result x); | 
| 28156 | 546 | |
| 29366 | 547 | |
| 67658 | 548 | (* forked results: nested parallel evaluation *) | 
| 549 | ||
| 550 | fun forked_results {name, deps} es =
 | |
| 551 | Thread_Attributes.uninterruptible (fn restore_attributes => fn () => | |
| 552 | let | |
| 553 | val (group, pri) = | |
| 554 | (case worker_task () of | |
| 555 | SOME task => | |
| 556 | (new_group (SOME (Task_Queue.group_of_task task)), Task_Queue.pri_of_task task) | |
| 557 | | NONE => (new_group NONE, 0)); | |
| 558 | val futures = | |
| 559 |         forks {name = name, group = SOME group, deps = deps, pri = pri, interrupts = true} es;
 | |
| 560 | in | |
| 561 | restore_attributes join_results futures | |
| 562 | handle exn => (if Exn.is_interrupt exn then cancel_group group else (); Exn.reraise exn) | |
| 563 | end) (); | |
| 564 | ||
| 565 | ||
| 54649 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 566 | (* task context for running thread *) | 
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 567 | |
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 568 | fun task_context name group f x = | 
| 62923 | 569 | Thread_Attributes.with_attributes Thread_Attributes.no_interrupts (fn orig_atts => | 
| 54649 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 570 | let | 
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 571 | val (result, job) = future_job group orig_atts (fn () => f x); | 
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 572 | val task = | 
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 573 | SYNCHRONIZED "enroll" (fn () => | 
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 574 | Unsynchronized.change_result queue (Task_Queue.enroll (Thread.self ()) name group)); | 
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 575 | val _ = worker_exec (task, [job]); | 
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 576 | in | 
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 577 | (case Single_Assignment.peek result of | 
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 578 | NONE => raise Fail "Missing task context result" | 
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 579 | | SOME res => Exn.release res) | 
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 580 | end); | 
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 581 | |
| 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 582 | |
| 68130 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 583 | (* scheduling parameters *) | 
| 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 584 | |
| 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 585 | fun enabled () = | 
| 68147 | 586 | let val threads = Multithreading.max_threads () in | 
| 587 | threads > 1 andalso | |
| 588 | let val lim = threads * Options.default_int "parallel_limit" | |
| 589 | in lim <= 0 orelse SYNCHRONIZED "enabled" (fn () => Task_Queue.total_jobs (! queue) < lim) end | |
| 590 | end; | |
| 68130 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 591 | |
| 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 592 | val relevant = (fn [] => false | [_] => false | _ => enabled ()); | 
| 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 593 | |
| 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 594 | fun proofs_enabled n = | 
| 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 595 | ! Multithreading.parallel_proofs >= n andalso is_some (worker_task ()) andalso enabled (); | 
| 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 596 | |
| 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 597 | fun proofs_enabled_timing t = | 
| 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 598 | proofs_enabled 1 andalso Time.toReal t >= Options.default_real "parallel_subproofs_threshold"; | 
| 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 599 | |
| 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 600 | |
| 51325 
bcd6b1aa4db5
more uniform Future.map: always internalize failure;
 wenzelm parents: 
51283diff
changeset | 601 | (* fast-path operations -- bypass task queue if possible *) | 
| 34277 
7325a5e3587f
added Future.promise/fulfill -- promised futures that are fulfilled by external means;
 wenzelm parents: 
33416diff
changeset | 602 | |
| 44294 
a0ddd5760444
clarified Future.cond_forks: more uniform handling of exceptional situations;
 wenzelm parents: 
44268diff
changeset | 603 | fun value_result (res: 'a Exn.result) = | 
| 34277 
7325a5e3587f
added Future.promise/fulfill -- promised futures that are fulfilled by external means;
 wenzelm parents: 
33416diff
changeset | 604 | let | 
| 45136 
2afb928c71ca
static dummy_task (again) to avoid a few extra allocations;
 wenzelm parents: 
44427diff
changeset | 605 | val task = Task_Queue.dummy_task; | 
| 41683 
73dde8006820
maintain Task_Queue.group within Task_Queue.task;
 wenzelm parents: 
41681diff
changeset | 606 | val group = Task_Queue.group_of_task task; | 
| 35016 | 607 | val result = Single_Assignment.var "value" : 'a result; | 
| 50914 
fe4714886d92
identify future results more carefully, to avoid odd duplication of error messages, notably from forked goals;
 wenzelm parents: 
50911diff
changeset | 608 | val _ = assign_result group result (identify_result (Position.thread_data ()) res); | 
| 41683 
73dde8006820
maintain Task_Queue.group within Task_Queue.task;
 wenzelm parents: 
41681diff
changeset | 609 |   in Future {promised = false, task = task, result = result} end;
 | 
| 29366 | 610 | |
| 44294 
a0ddd5760444
clarified Future.cond_forks: more uniform handling of exceptional situations;
 wenzelm parents: 
44268diff
changeset | 611 | fun value x = value_result (Exn.Res x); | 
| 
a0ddd5760444
clarified Future.cond_forks: more uniform handling of exceptional situations;
 wenzelm parents: 
44268diff
changeset | 612 | |
| 44330 | 613 | fun cond_forks args es = | 
| 68130 
6fb85346cb79
clarified future scheduling parameters, with support for parallel_limit;
 wenzelm parents: 
68129diff
changeset | 614 | if enabled () then forks args es | 
| 44330 | 615 | else map (fn e => value_result (Exn.interruptible_capture e ())) es; | 
| 616 | ||
| 29384 
a3c7e9ae9b71
more robust propagation of errors through bulk jobs;
 wenzelm parents: 
29366diff
changeset | 617 | fun map_future f x = | 
| 51325 
bcd6b1aa4db5
more uniform Future.map: always internalize failure;
 wenzelm parents: 
51283diff
changeset | 618 | if is_finished x then value_result (Exn.interruptible_capture (f o join) x) | 
| 49935 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 619 | else | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 620 | let | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 621 | val task = task_of x; | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 622 | val group = Task_Queue.group_of_task task; | 
| 54649 
99b9249b3e05
more official task context via Task_Queue.enroll, which is required to participate in group cancellation (e.g. to terminate command exec);
 wenzelm parents: 
54637diff
changeset | 623 | val (result, job) = | 
| 62923 | 624 | future_job group Thread_Attributes.private_interrupts (fn () => f (join x)); | 
| 29384 
a3c7e9ae9b71
more robust propagation of errors through bulk jobs;
 wenzelm parents: 
29366diff
changeset | 625 | |
| 49935 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 626 | val extended = SYNCHRONIZED "extend" (fn () => | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 627 | (case Task_Queue.extend task job (! queue) of | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 628 | SOME queue' => (queue := queue'; true) | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 629 | | NONE => false)); | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 630 | in | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 631 |       if extended then Future {promised = false, task = task, result = result}
 | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 632 | else | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 633 | (singleton o cond_forks) | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 634 |           {name = "map_future", group = SOME group, deps = [task],
 | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 635 | pri = Task_Queue.pri_of_task task, interrupts = true} | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 636 | (fn () => f (join x)) | 
| 
d1ecb3554b25
clarified Future.map (again): finished value is mapped in-place, which saves task structures and changes error behaviour slightly (tolerance against canceled group of old value etc.);
 wenzelm parents: 
49910diff
changeset | 637 | end; | 
| 28979 
3ce619d8d432
fork/map: no inheritance of group (structure is nested, not parallel);
 wenzelm parents: 
28972diff
changeset | 638 | |
| 28191 | 639 | |
| 34277 
7325a5e3587f
added Future.promise/fulfill -- promised futures that are fulfilled by external means;
 wenzelm parents: 
33416diff
changeset | 640 | (* promised futures -- fulfilled by external means *) | 
| 
7325a5e3587f
added Future.promise/fulfill -- promised futures that are fulfilled by external means;
 wenzelm parents: 
33416diff
changeset | 641 | |
| 66166 | 642 | fun promise_name name abort : 'a future = | 
| 34277 
7325a5e3587f
added Future.promise/fulfill -- promised futures that are fulfilled by external means;
 wenzelm parents: 
33416diff
changeset | 643 | let | 
| 66166 | 644 | val group = worker_subgroup (); | 
| 35016 | 645 | val result = Single_Assignment.var "promise" : 'a result; | 
| 44298 
b8f8488704e2
Future.promise: explicit abort operation (like uninterruptible future job);
 wenzelm parents: 
44295diff
changeset | 646 | fun assign () = assign_result group result Exn.interrupt_exn | 
| 39243 
307e3d07d19f
Future.promise: more robust treatment of concurrent abort vs. fulfill (amending 047c96f41455);
 wenzelm parents: 
39232diff
changeset | 647 | handle Fail _ => true | 
| 
307e3d07d19f
Future.promise: more robust treatment of concurrent abort vs. fulfill (amending 047c96f41455);
 wenzelm parents: 
39232diff
changeset | 648 | | exn => | 
| 44298 
b8f8488704e2
Future.promise: explicit abort operation (like uninterruptible future job);
 wenzelm parents: 
44295diff
changeset | 649 | if Exn.is_interrupt exn | 
| 
b8f8488704e2
Future.promise: explicit abort operation (like uninterruptible future job);
 wenzelm parents: 
44295diff
changeset | 650 | then raise Fail "Concurrent attempt to fulfill promise" | 
| 62505 | 651 | else Exn.reraise exn; | 
| 44298 
b8f8488704e2
Future.promise: explicit abort operation (like uninterruptible future job);
 wenzelm parents: 
44295diff
changeset | 652 | fun job () = | 
| 62923 | 653 | Thread_Attributes.with_attributes Thread_Attributes.no_interrupts | 
| 47423 
8a179a0493e3
more robust Future.fulfill wrt. duplicate assignment and interrupt;
 wenzelm parents: 
47421diff
changeset | 654 | (fn _ => Exn.release (Exn.capture assign () before abort ())); | 
| 37854 
047c96f41455
back to more strict dependencies, even for canceled groups (reverting parts of 02936e77a07c);
 wenzelm parents: 
37852diff
changeset | 655 | val task = SYNCHRONIZED "enqueue_passive" (fn () => | 
| 66166 | 656 | Unsynchronized.change_result queue (Task_Queue.enqueue_passive group name job)); | 
| 41683 
73dde8006820
maintain Task_Queue.group within Task_Queue.task;
 wenzelm parents: 
41681diff
changeset | 657 |   in Future {promised = true, task = task, result = result} end;
 | 
| 34277 
7325a5e3587f
added Future.promise/fulfill -- promised futures that are fulfilled by external means;
 wenzelm parents: 
33416diff
changeset | 658 | |
| 66166 | 659 | fun promise abort = promise_name "passive" abort; | 
| 34277 
7325a5e3587f
added Future.promise/fulfill -- promised futures that are fulfilled by external means;
 wenzelm parents: 
33416diff
changeset | 660 | |
| 66958 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 661 | fun fulfill_result (Future {promised = true, task, result}) res =
 | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 662 | let | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 663 | val group = Task_Queue.group_of_task task; | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 664 | val pos = Position.thread_data (); | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 665 | fun job ok = | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 666 | assign_result group result (if ok then identify_result pos res else Exn.interrupt_exn); | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 667 | val _ = | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 668 | Thread_Attributes.with_attributes Thread_Attributes.no_interrupts (fn _ => | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 669 | let | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 670 | val passive_job = | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 671 | SYNCHRONIZED "fulfill_result" (fn () => | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 672 | Unsynchronized.change_result queue | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 673 | (Task_Queue.dequeue_passive (Thread.self ()) task)); | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 674 | in | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 675 | (case passive_job of | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 676 | SOME true => worker_exec (task, [job]) | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 677 | | SOME false => () | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 678 | | NONE => ignore (job (not (Task_Queue.is_canceled group)))) | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 679 | end); | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 680 | val _ = | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 681 | if is_some (Single_Assignment.peek result) then () | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 682 | else worker_waiting [task] (fn () => ignore (Single_Assignment.await result)); | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 683 | in () end | 
| 
86bc3f1ec97e
minor performance tuning: avoid mutable variable for plain value, e.g. relevant for GC;
 wenzelm parents: 
66378diff
changeset | 684 | | fulfill_result _ _ = raise Fail "Not a promised future"; | 
| 34277 
7325a5e3587f
added Future.promise/fulfill -- promised futures that are fulfilled by external means;
 wenzelm parents: 
33416diff
changeset | 685 | |
| 43761 
e72ba84ae58f
tuned signature -- corresponding to Scala version;
 wenzelm parents: 
43665diff
changeset | 686 | fun fulfill x res = fulfill_result x (Exn.Res res); | 
| 34277 
7325a5e3587f
added Future.promise/fulfill -- promised futures that are fulfilled by external means;
 wenzelm parents: 
33416diff
changeset | 687 | |
| 
7325a5e3587f
added Future.promise/fulfill -- promised futures that are fulfilled by external means;
 wenzelm parents: 
33416diff
changeset | 688 | |
| 66378 | 689 | (* snapshot: current tasks of groups *) | 
| 54369 | 690 | |
| 68379 | 691 | fun snapshot [] = [] | 
| 692 | | snapshot groups = | |
| 693 | SYNCHRONIZED "snapshot" (fn () => | |
| 694 | Task_Queue.group_tasks (! queue) groups); | |
| 54369 | 695 | |
| 696 | ||
| 32228 
7622c03141b0
scheduler: shutdown spontaneously (after some delay) if queue is empty;
 wenzelm parents: 
32227diff
changeset | 697 | (* shutdown *) | 
| 29366 | 698 | |
| 28203 | 699 | fun shutdown () = | 
| 62359 | 700 | if is_some (worker_task ()) then | 
| 51283 
fefd07697979
disallow shutdown from worker, which would lead to deadlock since the scheduler cannot terminate;
 wenzelm parents: 
51281diff
changeset | 701 | raise Fail "Cannot shutdown while running as worker thread" | 
| 
fefd07697979
disallow shutdown from worker, which would lead to deadlock since the scheduler cannot terminate;
 wenzelm parents: 
51281diff
changeset | 702 | else | 
| 28276 | 703 | SYNCHRONIZED "shutdown" (fn () => | 
| 51279 | 704 | while scheduler_active () do | 
| 59340 
734bb148503e
explicit shutdown of scheduler and worker thread farm, assuming Session.shutdown() before saving heap image;
 wenzelm parents: 
59338diff
changeset | 705 | (do_shutdown := true; | 
| 
734bb148503e
explicit shutdown of scheduler and worker thread farm, assuming Session.shutdown() before saving heap image;
 wenzelm parents: 
59338diff
changeset | 706 | Multithreading.tracing 1 (fn () => "SHUTDOWN: wait"); | 
| 72078 
b8d0b8659e0a
more robust scheduler shutdown, notably for spurious crashes;
 wenzelm parents: 
72038diff
changeset | 707 | wait_timeout next_round scheduler_event)); | 
| 28203 | 708 | |
| 29366 | 709 | |
| 710 | (*final declarations of this structure!*) | |
| 711 | val map = map_future; | |
| 712 | ||
| 28156 | 713 | end; | 
| 28972 | 714 | |
| 715 | type 'a future = 'a Future.future; |