Skip to content

Commit 40e30c3

Browse files
committed
Use home-brewed futures for parallel pickling
Uses just one thread for the rest of pickling. One thread is sufficient since there is not that much to do.
1 parent d5662ef commit 40e30c3

File tree

2 files changed

+88
-6
lines changed

2 files changed

+88
-6
lines changed

compiler/src/dotty/tools/dotc/transform/Pickler.scala

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import Symbols._
1414
import Flags.Module
1515
import reporting.{ThrowingReporter, Profile, Message}
1616
import collection.mutable
17-
import scala.concurrent.{Future, Await, ExecutionContext}
18-
import scala.concurrent.duration.Duration
17+
import util.concurrent.{Executor, Future}
18+
import compiletime.uninitialized
1919

2020
object Pickler {
2121
val name: String = "pickler"
@@ -70,6 +70,11 @@ class Pickler extends Phase {
7070
body(scratch)
7171
}
7272

73+
private val executor = Executor[Array[Byte]]()
74+
75+
private def useExecutor(using Context) =
76+
Pickler.ParallelPickling && !ctx.settings.YtestPickler.value
77+
7378
override def run(using Context): Unit = {
7479
val unit = ctx.compilationUnit
7580
pickling.println(i"unpickling in run ${ctx.runId}")
@@ -123,10 +128,10 @@ class Pickler extends Phase {
123128
* function value.
124129
*/
125130
val demandPickled: () => Array[Byte] =
126-
if Pickler.ParallelPickling && !ctx.settings.YtestPickler.value then
127-
val futurePickled = Future(computePickled())(using ExecutionContext.global)
131+
if useExecutor then
132+
val futurePickled = executor.schedule(computePickled)
128133
() =>
129-
try Await.result(futurePickled, Duration.Inf)
134+
try futurePickled.force.get
130135
finally reportPositionWarnings()
131136
else
132137
val pickled = computePickled()
@@ -139,7 +144,13 @@ class Pickler extends Phase {
139144
}
140145

141146
override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] = {
142-
val result = super.runOn(units)
147+
val result =
148+
if useExecutor then
149+
executor.start()
150+
try super.runOn(units)
151+
finally executor.close()
152+
else
153+
super.runOn(units)
143154
if ctx.settings.YtestPickler.value then
144155
val ctx2 = ctx.fresh.setSetting(ctx.settings.YreadComments, true)
145156
testUnpickler(
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package dotty.tools.dotc.util
2+
import scala.util.{Try, Failure, Success}
3+
import scala.annotation.tailrec
4+
import scala.collection.mutable.ArrayBuffer
5+
6+
object concurrent:
7+
8+
class NoCompletion extends RuntimeException
9+
10+
class Future[T](exec: Executor[T]):
11+
private var result: Option[Try[T]] = None
12+
def force: Try[T] = synchronized {
13+
@tailrec def recur(): Try[T] = result match
14+
case Some(r) => r
15+
case None =>
16+
if exec.isRunning then
17+
wait(1000 /*ms*/)
18+
recur()
19+
else
20+
Failure(NoCompletion())
21+
recur()
22+
}
23+
def complete(r: Try[T]): Unit = synchronized {
24+
result = Some(r)
25+
notifyAll()
26+
}
27+
end Future
28+
29+
class Executor[T] extends Thread:
30+
private type WorkItem = (Future[T], () => T)
31+
32+
@volatile private var terminated = false
33+
private var allScheduled = false
34+
private val pending = new ArrayBuffer[WorkItem]
35+
36+
def isRunning: Boolean = !terminated
37+
38+
def schedule(op: () => T): Future[T] = synchronized {
39+
val f = Future[T](this)
40+
pending += ((f, op))
41+
notifyAll()
42+
f
43+
}
44+
45+
def close(): Unit = synchronized {
46+
allScheduled = true
47+
notifyAll()
48+
}
49+
50+
private def nextPending(): Option[WorkItem] = synchronized {
51+
while pending.isEmpty && !allScheduled do wait(1000 /*ms*/)
52+
if pending.isEmpty then None
53+
else
54+
val item = pending.head
55+
pending.dropInPlace(1)
56+
Some(item)
57+
}
58+
59+
override def run(): Unit =
60+
@tailrec def recur(): Unit = nextPending() match
61+
case Some((f, op)) =>
62+
f.complete(Try(op()))
63+
recur()
64+
case None =>
65+
try recur()
66+
finally terminated = true
67+
end Executor
68+
end concurrent
69+
70+
71+

0 commit comments

Comments
 (0)