io
io copied to clipboard
NameFilterSpecification sometimes hangs
I've noticed that both in CI and on my computer that sometimes io/test hangs. I was able to get a stack trace with jstack:
"pool-1-thread-3" #14 prio=5 os_prio=31 tid=0x00007f85b4001800 nid=0x5703 runnable [0x000070000b7b6000]
java.lang.Thread.State: RUNNABLE
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Slice.match(Pattern.java:3974)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Slice.match(Pattern.java:3974)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Slice.match(Pattern.java:3974)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Slice.match(Pattern.java:3974)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Slice.match(Pattern.java:3974)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Slice.match(Pattern.java:3974)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Slice.match(Pattern.java:3974)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Slice.match(Pattern.java:3974)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Slice.match(Pattern.java:3974)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Slice.match(Pattern.java:3974)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Slice.match(Pattern.java:3974)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Slice.match(Pattern.java:3974)
at java.util.regex.Pattern$Curly.match0(Pattern.java:4274)
at java.util.regex.Pattern$Curly.match(Pattern.java:4236)
at java.util.regex.Pattern$Slice.match(Pattern.java:3974)
at java.util.regex.Matcher.match(Matcher.java:1270)
at java.util.regex.Matcher.matches(Matcher.java:604)
at sbt.io.PatternFilter.accept(NameFilter.scala:249)
at sbt.io.NameFilterSpecification$.$anonfun$new$24(NameFilterSpecification.scala:28)
at sbt.io.NameFilterSpecification$.$anonfun$new$24$adapted(NameFilterSpecification.scala:26)
at sbt.io.NameFilterSpecification$$$Lambda$272/1757925248.apply(Unknown Source)
at scala.Function1.$anonfun$andThen$1(Function1.scala:57)
at scala.Function1$$Lambda$10/1216590855.apply(Unknown Source)
at org.scalacheck.Prop$.$anonfun$forAllShrink$2(Prop.scala:761)
at org.scalacheck.Prop$$$Lambda$176/134405054.apply(Unknown Source)
at org.scalacheck.Prop$.secure(Prop.scala:471)
at org.scalacheck.Prop$.result$1(Prop.scala:761)
at org.scalacheck.Prop$.$anonfun$forAllShrink$1(Prop.scala:800)
at org.scalacheck.Prop$$$Lambda$116/1759437641.apply(Unknown Source)
at org.scalacheck.Prop$.$anonfun$apply$1(Prop.scala:307)
at org.scalacheck.Prop$$$Lambda$17/701141022.apply(Unknown Source)
at org.scalacheck.PropFromFun.apply(Prop.scala:22)
at org.scalacheck.Prop$.$anonfun$delay$1(Prop.scala:476)
at org.scalacheck.Prop$$$Lambda$16/1273765644.apply(Unknown Source)
at org.scalacheck.Prop$.$anonfun$apply$1(Prop.scala:307)
at org.scalacheck.Prop$$$Lambda$17/701141022.apply(Unknown Source)
at org.scalacheck.PropFromFun.apply(Prop.scala:22)
at org.scalacheck.Test$.workerFun$1(Test.scala:326)
at org.scalacheck.Test$.$anonfun$check$1(Test.scala:355)
at org.scalacheck.Test$.$anonfun$check$1$adapted(Test.scala:355)
at org.scalacheck.Test$$$Lambda$98/1422421383.apply(Unknown Source)
at org.scalacheck.Platform$.runWorkers(Platform.scala:40)
at org.scalacheck.Test$.check(Test.scala:355)
at org.scalacheck.ScalaCheckRunner$$anon$2.executeInternal(ScalaCheckFramework.scala:123)
at org.scalacheck.ScalaCheckRunner$$anon$2.$anonfun$execute$10(ScalaCheckFramework.scala:113)
at org.scalacheck.ScalaCheckRunner$$anon$2.$anonfun$execute$10$adapted(ScalaCheckFramework.scala:112)
at org.scalacheck.ScalaCheckRunner$$anon$2$$Lambda$85/1451299362.apply(Unknown Source)
at scala.collection.TraversableLike$WithFilter.$anonfun$foreach$1(TraversableLike.scala:792)
at scala.collection.TraversableLike$WithFilter$$Lambda$87/581483593.apply(Unknown Source)
at scala.collection.immutable.List.foreach(List.scala:392)
at scala.collection.generic.TraversableForwarder.foreach(TraversableForwarder.scala:38)
at scala.collection.generic.TraversableForwarder.foreach$(TraversableForwarder.scala:38)
at scala.collection.mutable.ListBuffer.foreach(ListBuffer.scala:47)
at scala.collection.TraversableLike$WithFilter.foreach(TraversableLike.scala:791)
at org.scalacheck.ScalaCheckRunner$$anon$2.execute(ScalaCheckFramework.scala:112)
at sbt.ForkMain$Run.lambda$runTest$1(ForkMain.java:304)
at sbt.ForkMain$Run$$Lambda$70/682376643.call(Unknown Source)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Matcher says:
Instances of this class are not safe for use by multiple concurrent threads.
final class PatternFilter(val pattern: Pattern) extends NameFilter {
def accept(name: String): Boolean = pattern.matcher(name).matches
...
}
so from a single pattern potentially multiple PatternFilter can be created, and accept
can be called multiple times.
Aah, good find. Looks like maybe I should add a lock to PatternFilter. The PrefixFilter that I added is a nice optimization (https://github.com/eatkins/io/commit/9ac0e3e5810a1fb905ece249b74c5667160cfac2) that should fix the test but does not preclude the lock.
I should probably add suffix filter as well since it's a trivial addition.