SVNWCClient.doCleanup not releasing lock on working copy database

It appears as though SVNWClient.doCleanup does not release its lock on the working copy database once the operation has completed.

This was discovered while writing a JUnit5 test that creates a local filesystem repository inside a temporary directory which is automatically created before and deleted after each test. Deletion of the temporary directory fails after calling doCleanup due to the following cause:

java.nio.file.FileSystemException: C:\Users\sgreen\AppData\Local\Temp\junit2931781630062626873\working-copy\.svn\wc.db: The process cannot access the file because it is being used by another process.
		at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:86)
		at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
		at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
		at sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:269)
		at sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:103)
		at java.nio.file.Files.delete(Files.java:1126)
		at org.junit.jupiter.engine.extension.TempDirectory$CloseablePath$1.deleteAndContinue(TempDirectory.java:206)
		at org.junit.jupiter.engine.extension.TempDirectory$CloseablePath$1.visitFile(TempDirectory.java:196)
		at org.junit.jupiter.engine.extension.TempDirectory$CloseablePath$1.visitFile(TempDirectory.java:192)
		at java.nio.file.Files.walkFileTree(Files.java:2670)
		at java.nio.file.Files.walkFileTree(Files.java:2742)
		at org.junit.jupiter.engine.extension.TempDirectory$CloseablePath.deleteAllFilesAndDirectories(TempDirectory.java:192)
		at org.junit.jupiter.engine.extension.TempDirectory$CloseablePath.close(TempDirectory.java:180)
		... 82 more

This does not happen for any of the other client methods being used in the same test context.

An example gradle project with the issue can be found here: Bitbucket

openjdk version “11.0.11” 2021-04-20 LTS
svnkit version 1.10.3

Hello Steve,
thanks for the test but unfortunately I can’t reproduce the problem as I don’t have any Windows machine.

But from your code I see that you don’t call SVNClientManager#dispose, this could cause the problem. There’s an article about that:

http://vcs.atspace.co.uk/2012/09/22/what-svnkit-resources-should-be-disposed/

And there’s also another good article:

http://vcs.atspace.co.uk/2012/09/21/are-svnkit-methods-reenterable/

Let me cite:

The reason is that SVNBasicClient encapsulates SvnOperationFactory, that encapsulates SVNWCContext, that encapsulates SVNWCDb, that contains

private Map<String, SVNWCDbDir> dirData;

This is a cache path->working_copy_root_data where “working_copy_root_data” is a structure that contains a working copy root path and a database object (SVNSqlJetDb), and this database object contains “openCount” — transaction in progress counter that is increased when a transaction starts and is decreased when it ends (in thread-unsafe manner). If the operation is finished, but openCount > 0 (for example, because the database is used from another thread, you see

svn: E200030: There are unfinished transactions detected in …

exception). So SVNSqlJetDb objects can’t be reused among threads. And the same is true about callbacks.

And this extends to SVNClientManager which also contains SvnOperationFactory.

By the way, I would recommend you to use newer SvnOperationFactory-based API rather than older SVNClientManager API (which now just wraps SvnOperationFactory).

Hi Dmitry,

Thanks for your reply, dispose seems to have resolved the issue in the example project, but I cannot get it to work in my actual project where the context is slightly more complicated. I will find an alternative workaround instead.