sbt / io

IO module for sbt
Apache License 2.0
40 stars 45 forks source link

NameFilterSpecification sometimes hangs #228

Open eatkins opened 5 years ago

eatkins commented 5 years ago

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)
eed3si9n commented 5 years ago

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.

eatkins commented 5 years ago

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.

eatkins commented 5 years ago

I should probably add suffix filter as well since it's a trivial addition.