@@ -177,9 +177,18 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
177
177
if local != null then
178
178
op(using ctx)(local)
179
179
180
- def doBeginUnit ()(using Context ): Unit =
181
- trackProgress : progress =>
182
- progress.informUnitStarting(ctx.compilationUnit)
180
+ private inline def foldProgress [T ](using Context )(inline default : T )(inline op : Context ?=> Progress => T ): T =
181
+ val local = _progress
182
+ if local != null then
183
+ op(using ctx)(local)
184
+ else
185
+ default
186
+
187
+ def didEnterUnit ()(using Context ): Boolean =
188
+ foldProgress(true /* should progress by default */ )(_.tryEnterUnit(ctx.compilationUnit))
189
+
190
+ def didEnterFinal ()(using Context ): Boolean =
191
+ foldProgress(true /* should progress by default */ )(p => ! p.checkCancellation())
183
192
184
193
def doAdvanceUnit ()(using Context ): Unit =
185
194
trackProgress : progress =>
@@ -195,6 +204,13 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
195
204
trackProgress : progress =>
196
205
progress.enterPhase(currentPhase)
197
206
207
+ /** interrupt the thread and set cancellation state */
208
+ private def cancelInterrupted (): Unit =
209
+ try
210
+ trackProgress(_.cancel())
211
+ finally
212
+ Thread .currentThread().nn.interrupt()
213
+
198
214
private def doAdvancePhase (currentPhase : Phase , wasRan : Boolean )(using Context ): Unit =
199
215
trackProgress : progress =>
200
216
progress.unitc = 0 // reset unit count in current (sub)phase
@@ -213,7 +229,8 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
213
229
progress.seen += 1 // trace that we've seen a (sub)phase
214
230
progress.traversalc += 1 // add an extra traversal now that we completed a (sub)phase
215
231
progress.subtraversalc += 1 // record that we've seen a subphase
216
- progress.tickSubphase()
232
+ if ! progress.isCancelled() then
233
+ progress.tickSubphase()
217
234
218
235
/** Will be set to true if any of the compiled compilation units contains
219
236
* a pureFunctions language import.
@@ -297,7 +314,8 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
297
314
Stats .trackTime(s " phase time ms/ $phase" ) {
298
315
val start = System .currentTimeMillis
299
316
val profileBefore = profiler.beforePhase(phase)
300
- units = phase.runOn(units)
317
+ try units = phase.runOn(units)
318
+ catch case _ : InterruptedException => cancelInterrupted()
301
319
profiler.afterPhase(phase, profileBefore)
302
320
if (ctx.settings.Xprint .value.containsPhase(phase))
303
321
for (unit <- units)
@@ -333,7 +351,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
333
351
if (! ctx.reporter.hasErrors)
334
352
Rewrites .writeBack()
335
353
suppressions.runFinished(hasErrors = ctx.reporter.hasErrors)
336
- while (finalizeActions.nonEmpty) {
354
+ while (finalizeActions.nonEmpty && didEnterFinal() ) {
337
355
val action = finalizeActions.remove(0 )
338
356
action()
339
357
}
@@ -481,6 +499,8 @@ object Run {
481
499
482
500
483
501
private class Progress (cb : ProgressCallback , private val run : Run , val initialTraversals : Int ):
502
+ export cb .{cancel , isCancelled }
503
+
484
504
private [Run ] var totalTraversals : Int = initialTraversals // track how many phases we expect to run
485
505
private [Run ] var unitc : Int = 0 // current unit count in the current (sub)phase
486
506
private [Run ] var latec : Int = 0 // current late unit count
@@ -515,34 +535,46 @@ object Run {
515
535
516
536
517
537
/** Counts the number of completed full traversals over files, plus the number of units in the current phase */
518
- private def currentProgress ()( using Context ) : Int =
519
- traversalc * run.files.size + unitc + latec
538
+ private def currentProgress (): Int =
539
+ traversalc * work() + unitc + latec
520
540
521
541
/** Total progress is computed as the sum of
522
542
* - the number of traversals we expect to make over all files
523
543
* - the number of late compilations
524
544
*/
525
- private def totalProgress ()(using Context ): Int =
526
- totalTraversals * run.files.size + run.lateFiles.size
545
+ private def totalProgress (): Int =
546
+ totalTraversals * work() + run.lateFiles.size
547
+
548
+ private def work (): Int = run.files.size
527
549
528
550
private def requireInitialized (): Unit =
529
551
require((currPhase : Phase | Null ) != null , " enterPhase was not called" )
530
552
531
- /** trace that we are beginning a unit in the current (sub)phase */
532
- private [Run ] def informUnitStarting (unit : CompilationUnit )(using Context ): Unit =
533
- requireInitialized()
534
- cb.informUnitStarting(currPhaseName, unit)
553
+ private [Run ] def checkCancellation (): Boolean =
554
+ if Thread .interrupted() then cancel()
555
+ isCancelled()
556
+
557
+ /** trace that we are beginning a unit in the current (sub)phase, unless cancelled */
558
+ private [Run ] def tryEnterUnit (unit : CompilationUnit ): Boolean =
559
+ if checkCancellation() then false
560
+ else
561
+ requireInitialized()
562
+ cb.informUnitStarting(currPhaseName, unit)
563
+ true
535
564
536
565
/** trace the current progress out of the total, in the current (sub)phase, reporting the next (sub)phase */
537
566
private [Run ] def refreshProgress ()(using Context ): Unit =
538
567
requireInitialized()
539
- cb.progress(currentProgress(), totalProgress(), currPhaseName, nextPhaseName)
568
+ val total = totalProgress()
569
+ if total > 0 && ! cb.progress(currentProgress(), total, currPhaseName, nextPhaseName) then
570
+ cancel()
540
571
541
572
extension (run : Run | Null )
542
573
543
574
/** record that the current phase has begun for the compilation unit of the current Context */
544
- def beginUnit ()(using Context ): Unit =
545
- if run != null then run.doBeginUnit()
575
+ def enterUnit ()(using Context ): Boolean =
576
+ if run != null then run.didEnterUnit()
577
+ else true // don't check cancellation if we're not tracking progress
546
578
547
579
/** advance the unit count and record progress in the current phase */
548
580
def advanceUnit ()(using Context ): Unit =
0 commit comments