Skip to content

Commit 4d4eb91

Browse files
committed
Merge pull request scala#4372 from som-snytt/issue/7775-tweak
SI-7775 Exclude nulls when iterating sys props
2 parents 917602c + 17caf79 commit 4d4eb91

File tree

5 files changed

+58
-6
lines changed

5 files changed

+58
-6
lines changed

src/compiler/scala/tools/cmd/Property.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package cmd
99
import nsc.io._
1010
import java.util.Properties
1111
import java.io.FileInputStream
12+
import scala.sys.SystemProperties
1213

1314
/** Contains logic for translating a property key/value pair into
1415
* equivalent command line arguments. The default settings will
@@ -58,7 +59,7 @@ trait Property extends Reference {
5859
returning(new Properties)(_ load new FileInputStream(file.path))
5960

6061
def systemPropertiesToOptions: List[String] =
61-
propertiesToOptions(System.getProperties)
62+
propertiesToOptions(new SystemProperties().toList)
6263

6364
def propertiesToOptions(file: File): List[String] =
6465
propertiesToOptions(loadProperties(file))

src/compiler/scala/tools/reflect/WrappedProperties.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ trait WrappedProperties extends PropertiesTrait {
3030
def systemProperties: List[(String, String)] = {
3131
import scala.collection.JavaConverters._
3232
wrap {
33+
// SI-7269,7775 Avoid `ConcurrentModificationException` and nulls if another thread modifies properties
3334
val props = System.getProperties
34-
// SI-7269 Be careful to avoid `ConcurrentModificationException` if another thread modifies the properties map
35-
props.stringPropertyNames().asScala.toList.map(k => (k, props.get(k).asInstanceOf[String]))
35+
val it = props.stringPropertyNames().asScala.iterator map (k => (k, props getProperty k)) filter (_._2 ne null)
36+
it.toList
3637
} getOrElse Nil
3738
}
3839
}

src/compiler/scala/tools/util/PathResolver.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ object PathResolver {
5252
*/
5353
object Environment {
5454
private def searchForBootClasspath =
55-
systemProperties find (_._1 endsWith ".boot.class.path") map (_._2) getOrElse ""
55+
systemProperties collectFirst { case (k, v) if k endsWith ".boot.class.path" => v } getOrElse ""
5656

5757
/** Environment variables which java pays attention to so it
5858
* seems we do as well.

src/library/scala/sys/SystemProperties.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,15 @@ extends mutable.AbstractMap[String, String]
3535
override def empty = new SystemProperties
3636
override def default(key: String): String = null
3737

38-
def iterator: Iterator[(String, String)] =
39-
wrapAccess(System.getProperties().asScala.iterator) getOrElse Iterator.empty
38+
def iterator: Iterator[(String, String)] = wrapAccess {
39+
val ps = System.getProperties()
40+
names map (k => (k, ps getProperty k)) filter (_._2 ne null)
41+
} getOrElse Iterator.empty
42+
43+
def names: Iterator[String] = wrapAccess (
44+
System.getProperties().stringPropertyNames().asScala.iterator
45+
) getOrElse Iterator.empty
46+
4047
def get(key: String) =
4148
wrapAccess(Option(System.getProperty(key))) flatMap (x => x)
4249
override def contains(key: String) =

test/files/run/t7775.scala

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,45 @@
1+
import scala.concurrent._, duration._
2+
import ExecutionContext.Implicits.global
3+
import scala.tools.reflect.WrappedProperties.AccessControl._
4+
import java.util.concurrent.CyclicBarrier
5+
6+
object Test extends App {
7+
@volatile var done = false
8+
val barrier = new CyclicBarrier(2)
9+
10+
val probe = Future {
11+
val attempts = 1024 // previously, failed after a few
12+
def fail(i: Int) = s"Failed at $i"
13+
barrier.await()
14+
for (i <- 1 to attempts ; p <- systemProperties)
15+
p match { case (k, v) => assert (k != null && v != null, fail(i)) }
16+
}
17+
probe onComplete {
18+
case _ => done = true
19+
}
20+
21+
System.setProperty("foo", "fooz")
22+
System.setProperty("bar", "barz")
23+
barrier.await() // just for fun, wait to start mucking with properties
24+
25+
// continually modify properties trying to break live iteration over sys props
26+
// hint: don't iterate lively over sys props
27+
var alt = true
28+
while (!done) {
29+
if (alt) {
30+
System.getProperties.remove("foo")
31+
System.setProperty("bar", "barz")
32+
alt = false
33+
} else {
34+
System.getProperties.remove("bar")
35+
System.setProperty("foo", "fooz")
36+
alt = true
37+
}
38+
}
39+
Await.result(probe, Duration.Inf)
40+
}
41+
42+
/*
143
import scala.concurrent.{duration, Future, Await, ExecutionContext}
244
import scala.tools.nsc.Settings
345
import ExecutionContext.Implicits.global
@@ -15,3 +57,4 @@ object Test {
1557
Await.result(compiler, duration.Duration.Inf)
1658
}
1759
}
60+
*/

0 commit comments

Comments
 (0)