Permalink
Browse files

Fix of #1400 - race in DriverDataSource.init

Used double checked locking for `initialized` flag in order to avoid the
initialization race. Also removed the initial setting of initialized = true
since it shouldn't be done until after initialization is complete, which
already happens in the finally block. There should be zero performance
penalty since the synchronized block is only reached when uninitialized.

Also made the `driver` and `connectionProps` variables volatile since I can't
see how the writes to them will be guaranteed to be visible to other threads
otherwise. The performance penalty should be almost zero in practice since
the `initialized` variable was already volatile and the read of `initialized`,
which happens at every getConnection() call, would've already triggered a
read through from main memory and a second one shouldn't be noticeable.
  • Loading branch information...
mustajarvi committed Dec 23, 2015
1 parent c01d93e commit 9c73813abd8ae0b676c411c848ba3932d27d17aa
Showing with 32 additions and 29 deletions.
  1. +32 −29 slick/src/main/scala/slick/jdbc/DriverDataSource.scala
@@ -46,42 +46,45 @@ class DriverDataSource(
// State that gets initialized by `init`
@volatile private[this] var registered: Boolean = false
@volatile private[this] var initialized = false
private[this] var driver: Driver = _
private[this] var connectionProps: Properties = _
@volatile private[this] var driver: Driver = _
@volatile private[this] var connectionProps: Properties = _
def init: Unit = if(!initialized) {
try {
initialized = true
if(url eq null) throw new SQLException("Required parameter \"url\" missing in DriverDataSource")
driver = if(driverObject eq null) {
if(driverClassName ne null) {
DriverManager.getDrivers.asScala.find(_.getClass.getName == driverClassName).getOrElse {
logger.debug(s"Driver $driverClassName not already registered; trying to load it")
val cl = classLoader.loadClass(driverClassName)
registered = true
DriverManager.getDrivers.asScala.find(_.getClass.getName == driverClassName).getOrElse {
logger.debug(s"Loaded driver $driverClassName but it did not register with DriverManager; trying to instantiate directly")
try cl.newInstance.asInstanceOf[Driver] catch { case ex: Exception =>
logger.debug(s"Instantiating driver class $driverClassName failed; asking DriverManager to handle URL $url", ex)
try DriverManager.getDriver(url) catch { case ex: Exception =>
throw new SlickException(s"Driver $driverClassName does not know how to handle URL $url", ex)
this.synchronized {
if(!initialized) {
try {
if(url eq null) throw new SQLException("Required parameter \"url\" missing in DriverDataSource")
driver = if(driverObject eq null) {
if(driverClassName ne null) {
DriverManager.getDrivers.asScala.find(_.getClass.getName == driverClassName).getOrElse {
logger.debug(s"Driver $driverClassName not already registered; trying to load it")
val cl = classLoader.loadClass(driverClassName)
registered = true
DriverManager.getDrivers.asScala.find(_.getClass.getName == driverClassName).getOrElse {
logger.debug(s"Loaded driver $driverClassName but it did not register with DriverManager; trying to instantiate directly")
try cl.newInstance.asInstanceOf[Driver] catch { case ex: Exception =>
logger.debug(s"Instantiating driver class $driverClassName failed; asking DriverManager to handle URL $url", ex)
try DriverManager.getDriver(url) catch { case ex: Exception =>
throw new SlickException(s"Driver $driverClassName does not know how to handle URL $url", ex)
}
}
}
}
} else try DriverManager.getDriver(url) catch { case ex: Exception =>
throw new SlickException(s"No driver specified and DriverManager does not know how to handle URL $url", ex)
}
} else driverObject
if(!driver.acceptsURL(url)) {
close()
throw new SlickException(s"Driver ${driver.getClass.getName} does not know how to handle URL $url")
}
} else try DriverManager.getDriver(url) catch { case ex: Exception =>
throw new SlickException(s"No driver specified and DriverManager does not know how to handle URL $url", ex)
}
} else driverObject
if(!driver.acceptsURL(url)) {
close()
throw new SlickException(s"Driver ${driver.getClass.getName} does not know how to handle URL $url")
connectionProps = propsWithUserAndPassword(properties, user, password)
} catch { case NonFatal(ex) =>
try close() catch ignoreFollowOnError
throw ex
} finally initialized = true
}
connectionProps = propsWithUserAndPassword(properties, user, password)
} catch { case NonFatal(ex) =>
try close() catch ignoreFollowOnError
throw ex
} finally initialized = true
}
}
private[this] def propsWithUserAndPassword(p: Properties, user: String, password: String): Properties = {

0 comments on commit 9c73813

Please sign in to comment.