@@ -36,15 +36,21 @@ import dotty.tools.dotc.report
36
36
37
37
import dotty .tools .backend .jvm .PostProcessorFrontendAccess .BackendReporting
38
38
import scala .annotation .constructorOnly
39
-
40
- /** Copied from `dotty.tools.backend.jvm.ClassfileWriters` but no `PostProcessorFrontendAccess` needed */
39
+ import java .util .concurrent .atomic .AtomicReference
40
+ import java .util .concurrent .atomic .AtomicBoolean
41
+
42
+ /** !!!Copied from `dotty.tools.backend.jvm.ClassfileWriters` but no `PostProcessorFrontendAccess` needed.
43
+ * this should probably be changed to wrap that class instead.
44
+ *
45
+ * Until then, any changes to this file should be copied to `dotty.tools.backend.jvm.ClassfileWriters` as well.
46
+ */
41
47
object FileWriters {
42
48
type InternalName = String
43
49
type NullableFile = AbstractFile | Null
44
50
45
51
inline def ctx (using ReadOnlyContext ): ReadOnlyContext = summon[ReadOnlyContext ]
46
52
47
- sealed trait DelayedReporting {
53
+ sealed trait DelayedReporter {
48
54
def hasErrors : Boolean
49
55
def error (message : Context ?=> Message , position : SourcePosition ): Unit
50
56
def warning (message : Context ?=> Message , position : SourcePosition ): Unit
@@ -54,7 +60,7 @@ object FileWriters {
54
60
def warning (message : Context ?=> Message ): Unit = warning(message, NoSourcePosition )
55
61
}
56
62
57
- final class EagerDelayedReporting (using captured : Context ) extends DelayedReporting :
63
+ final class EagerReporter (using captured : Context ) extends DelayedReporter :
58
64
private var _hasErrors = false
59
65
60
66
def hasErrors : Boolean = _hasErrors
@@ -68,62 +74,86 @@ object FileWriters {
68
74
69
75
def log (message : String ): Unit = report.echo(message)
70
76
71
- final class BufferingDelayedReporting extends DelayedReporting {
77
+ final class BufferingReporter extends DelayedReporter {
72
78
// We optimise access to the buffered reports for the common case - that there are no warning/errors to report
73
79
// We could use a listBuffer etc - but that would be extra allocation in the common case
74
- // Note - all access is externally synchronized, as this allow the reports to be generated in on thread and
75
- // consumed in another
76
- private var bufferedReports = List .empty[Report ]
77
- private var _hasErrors = false
80
+ // buffered logs are updated atomically.
81
+
82
+ private val _bufferedReports = AtomicReference (List .empty[Report ])
83
+ private val _hasErrors = AtomicBoolean (false )
84
+
78
85
enum Report (val relay : Context ?=> BackendReporting => Unit ):
79
86
case Error (message : Context => Message , position : SourcePosition ) extends Report (ctx ?=> _.error(message(ctx), position))
80
87
case Warning (message : Context => Message , position : SourcePosition ) extends Report (ctx ?=> _.warning(message(ctx), position))
81
88
case Log (message : String ) extends Report (_.log(message))
82
89
83
- def hasErrors : Boolean = synchronized :
84
- _hasErrors
90
+ /** Atomically record that an error occurred */
91
+ private def recordError (): Unit =
92
+ while
93
+ val old = _hasErrors.get
94
+ ! old && ! _hasErrors.compareAndSet(old, true )
95
+ do ()
96
+
97
+ /** Atomically add a report to the log */
98
+ private def recordReport (report : Report ): Unit =
99
+ while
100
+ val old = _bufferedReports.get
101
+ ! _bufferedReports.compareAndSet(old, report :: old)
102
+ do ()
103
+
104
+ /** atomically extract and clear the buffered reports */
105
+ private def resetReports (): List [Report ] =
106
+ while
107
+ val old = _bufferedReports.get
108
+ if _bufferedReports.compareAndSet(old, Nil ) then
109
+ return old
110
+ else
111
+ true
112
+ do ()
113
+ throw new AssertionError (" Unreachable" )
114
+
115
+ def hasErrors : Boolean = _hasErrors.get()
85
116
86
- def error (message : Context ?=> Message , position : SourcePosition ): Unit = synchronized :
87
- bufferedReports ::= Report .Error ({case given Context => message}, position)
88
- _hasErrors = true
117
+ def error (message : Context ?=> Message , position : SourcePosition ): Unit =
118
+ recordReport( Report .Error ({case given Context => message}, position) )
119
+ recordError()
89
120
90
- def warning (message : Context ?=> Message , position : SourcePosition ): Unit = synchronized :
91
- bufferedReports ::= Report .Warning ({case given Context => message}, position)
121
+ def warning (message : Context ?=> Message , position : SourcePosition ): Unit =
122
+ recordReport( Report .Warning ({case given Context => message}, position) )
92
123
93
- def log (message : String ): Unit = synchronized :
94
- bufferedReports ::= Report .Log (message)
124
+ def log (message : String ): Unit =
125
+ recordReport( Report .Log (message) )
95
126
96
127
/** Should only be called from main compiler thread. */
97
- def relayReports (toReporting : BackendReporting )(using Context ): Unit = synchronized :
98
- if bufferedReports.nonEmpty then
99
- bufferedReports.reverse.foreach(_.relay(toReporting))
100
- bufferedReports = Nil
128
+ def relayReports (toReporting : BackendReporting )(using Context ): Unit =
129
+ val reports = resetReports()
130
+ if reports.nonEmpty then
131
+ reports.reverse.foreach(_.relay(toReporting))
101
132
}
102
133
103
- trait ReadSettings :
134
+ trait ReadOnlySettings :
104
135
def jarCompressionLevel : Int
105
136
def debug : Boolean
106
137
107
138
trait ReadOnlyContext :
108
-
109
- val settings : ReadSettings
110
- val reporter : DelayedReporting
139
+ val settings : ReadOnlySettings
140
+ val reporter : DelayedReporter
111
141
112
142
trait BufferedReadOnlyContext extends ReadOnlyContext :
113
- val reporter : BufferingDelayedReporting
143
+ val reporter : BufferingReporter
114
144
115
145
object ReadOnlyContext :
116
- def readSettings (using ctx : Context ): ReadSettings = new :
146
+ def readSettings (using ctx : Context ): ReadOnlySettings = new :
117
147
val jarCompressionLevel = ctx.settings.YjarCompressionLevel .value
118
148
val debug = ctx.settings.Ydebug .value
119
149
120
150
def buffered (using Context ): BufferedReadOnlyContext = new :
121
151
val settings = readSettings
122
- val reporter = BufferingDelayedReporting ()
152
+ val reporter = BufferingReporter ()
123
153
124
154
def eager (using Context ): ReadOnlyContext = new :
125
155
val settings = readSettings
126
- val reporter = EagerDelayedReporting ()
156
+ val reporter = EagerReporter ()
127
157
128
158
/**
129
159
* The interface to writing classfiles. GeneratedClassHandler calls these methods to generate the
0 commit comments