I have a test (ObjectOfMapSpec) that throws a NullPointerException when run a 24xcore machine while it works fine on a 2-core machine. Question is whether the problem is related to Thread Visibility, and if so, why does it occur.
trait Resource {
val key: String = UUID.randomUUID().toString()
}
trait SubResource extends Resource {
val name: String
//In a multi-core machine, removing the comment creates a NullPointerException in the map
override val key: String = name
}
object ObjectOfResource extends SubResource {
val name = “ObjectOfResource”
}
trait SomeMap {
private val hashMap = new ConcurrentHashMap[String, Resource]
class ObjectOfMapSpec extends FlatSpec with Matchers {
“ObjectOfMap” should “work just fine…” in {
val ok = ObjectOfMap.dummyMethod()
ok should be(true)
}
}
It is not a thread visibility problem and it should fail in any computer regardless of thread count.
It is an initialization order issue.
You can fix it by making name a def on the trait and overriding it with a final val on the object.
Like this:
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
trait Resource {
// I personally would also make this a def and remove the default implementation.
val key: String = UUID.randomUUID().toString()
}
trait SubResource extends Resource {
def name: String
override val key: String = name
}
object ObjectOfResource extends SubResource {
override final val name = "ObjectOfResource"
}
trait SomeMap {
// I made this final and private[this], because subclasses shouldn't know about this.
private[this] final val hashMap = new ConcurrentHashMap[String, Resource]
def addResource(someResource: Resource): Resource = {
println(s"Adding resource: ${someResource.key}")
hashMap.putIfAbsent(someResource.key, someResource)
}
}
object ObjectOfMap extends SomeMap {
addResource(ObjectOfResource)
}