Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 23 additions & 19 deletions core/play/src/main/scala/play/api/libs/Files.scala
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,13 @@ object Files {
// Keeping references ensures that the FinalizablePhantomReference itself is not garbage-collected.
private val references = Sets.newConcurrentHashSet[Reference[TemporaryFile]]()

private val TempDirectoryPrefix = "playtemp"
private lazy val playTempFolder: Path = {
private val TempDirectoryPrefix = "playtemp"
private var _playTempFolder: Option[Path] = None
private def playTempFolder: Path = _playTempFolder.getOrElse {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we have a failing test now:

[error]   x not have a race condition when creating temporary files (794 ms)
[error]    the value is false (TemporaryFileCreatorSpec.scala:90)

The test ends with this line:

// All temporary files should be created at the same directory
results.forall(_.path.getParent.equals(parentDir)) must beTrue

So the problem is the code _playTempFolder.getOrElse { ... } is not atomic. Multiple threads can enter the getOrElse { ... } body, create a new tempfolder and update the _playTempFolder var and the `temporaryFileReaper´ tmp folder.

So this alternative does not work without syncing some code, therefore lets just merge #13039 instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, I thought about it for a sec and said to myself "we don't really care" but that's nice that a test caught it :) Because now that I think about it more, we do care! It could lead to some inconsistencies and not clean things properly at shutdown.

val dir = Paths.get(conf.get[String]("play.temporaryFile.dir"))
JFiles.createDirectories(dir) // make sure dir exists, otherwise createTempDirectory fails
val tmpFolder = JFiles.createTempDirectory(dir, TempDirectoryPrefix)
_playTempFolder = Some(tmpFolder)
temporaryFileReaper.updateTempFolder(tmpFolder)
tmpFolder
}
Expand Down Expand Up @@ -317,24 +319,26 @@ object Files {
*/
applicationLifecycle.addStopHook { () =>
Future.successful {
if (JFiles.isDirectory(playTempFolder)) {
JFiles.walkFileTree(
playTempFolder,
new SimpleFileVisitor[Path] {
override def visitFile(path: Path, attrs: BasicFileAttributes): FileVisitResult = {
logger.debug(s"stopHook: Removing leftover temporary file $path from $playTempFolder")
deletePath(path)
FileVisitResult.CONTINUE
_playTempFolder.foreach(playTempFolder => {
if (JFiles.isDirectory(playTempFolder)) {
JFiles.walkFileTree(
playTempFolder,
new SimpleFileVisitor[Path] {
override def visitFile(path: Path, attrs: BasicFileAttributes): FileVisitResult = {
logger.debug(s"stopHook: Removing leftover temporary file $path from $playTempFolder")
deletePath(path)
FileVisitResult.CONTINUE
}

override def postVisitDirectory(path: Path, exc: IOException): FileVisitResult = {
deletePath(path)
FileVisitResult.CONTINUE
}
}

override def postVisitDirectory(path: Path, exc: IOException): FileVisitResult = {
deletePath(path)
FileVisitResult.CONTINUE
}
}
)
}
frq.close()
)
}
frq.close()
})
}
}
}
Expand Down
Loading