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...
1 parent c01d93e commit 9c73813abd8ae0b676c411c848ba3932d27d17aa @mustajarvi mustajarvi committed Dec 23, 2015
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.