net-vips: Unstable and floating exception with the access of the file after reading it in Image.NewFromFile
Hi @kleisauke!
I got an exception and think that it relates to the implementation of NetVips (or LibVips) System.IO.IOException: The process cannot access the file ‘test-X.png’ because it is being used by another process
Explanation
So, we got an unstable and floating exception with the access of the file that was created via System.Diagnostics.Process in async operation and which was read in Image.NewFromFile after that.
So for the reproducing, needs 3 things:
- creates files in the async method via System.Diagnostics.Process in some temp directory
- read files via Image.NewFromFile
- try to delete the temp directory
The exception occurs on deleting the temp directory on the 3 step.
Why I think the issue relates to the NetVips (libvips).
-
The first reason, if I do not read files via Image.NewFromFile (2 step), then deletion temp directory works stable and well.
-
The second reason… If I do:
Image.NewFromBuffer(File.ReadAllBytes(filePath), access: Enums.Access.Sequential)instead ofImage.NewFromFile(filePath, access: Enums.Access.Sequential)the code works stable and well also (no exception).
Resources for reproducing
Bellow, I listed the full test code where you can reproduce the issue.
Also, here the project with test code for easy reproducing: TestNetVipsAsync.zip
This is the test picture (test.png) needed for this test:

Important! For reproducing this issue, the test picture must be quite small (as I created).
Full test example
(Please, create the directory C:\Temp\TestNetVips and put there the picture test.png)
static async Task Main(string[] args)
{
Console.WriteLine("Hello NetVips!");
string basePath = "C:\\Temp\\TestNetVips\\";
Directory.SetCurrentDirectory(basePath);
var testPngFile = "test.png";
var testDirectory = "TestDirectory";
Directory.CreateDirectory(testDirectory);
// Prepare 50 test files (clone 50 from test.png)
for (int i = 0; i < 50; i++)
File.Copy(testPngFile, Path.Combine(testDirectory, $"test-{i}.png"), true);
async Task DoAll(string tempDirectory)
{
try
{
// prepare directory for converting
Directory.CreateDirectory(tempDirectory);
// Create test files for converting via external process
// System.Diagnostics.Process
using (var proc = Process.Start(new ProcessStartInfo
{
FileName = "xcopy",
Arguments = $"/S /E /Q /Y {basePath}\\{testDirectory}\\*.* {Path.Combine(basePath, tempDirectory) }\\*.*\r\n",
WorkingDirectory = "C:\\Temp",
UseShellExecute = false,
CreateNoWindow = true
}))
{
proc?.WaitForExit();
}
// Do any await operation to make method really async and run it in different thread
await File.ReadAllBytesAsync("test.png");
// Ensure we are in different threads
Debug.WriteLine($"Work with {tempDirectory} in {Thread.CurrentThread.ManagedThreadId} thread");
// Open files for converting
foreach (var filePath in Directory.GetFiles(tempDirectory, "test-*.png", SearchOption.TopDirectoryOnly))
{
using (Image image = Image.NewFromBuffer(File.ReadAllBytes(filePath), access: Enums.Access.Sequential))
{
// Do nothing, just read, this is already enough for issue
//image.Jpegsave(".....", 75);
}
}
// Delete temp directory !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Here sometimes unexpectedly we got (this is an issue):
// "System.IO.IOException: The process cannot access the file 'test-X.png' because it is being used by another process"
Directory.Delete(tempDirectory, true);
}
catch (Exception e)
{
Debug.WriteLine(e);
throw;
}
}
try
{
// 10 iteration because the exception not stable
for (int i = 0; i < 10; i++)
{
// 20 parallel tasks for reading files via NetVips
var tasks = new List<Task>();
for (int j = 0; j < 20; j++)
tasks.Add(DoAll($"TempDirectory-{i}-{j}"));
await Task.WhenAll(tasks);
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 20 (13 by maintainers)
NetVips.Native v8.12.1 is now available.
PR https://github.com/libvips/libvips/pull/2497 should fix this. The pre-compiled libvips Windows binaries build from that PR can be downloaded here (for testing purposes): https://libvips-packaging.s3.amazonaws.com/vips-dev-w64-web-8.12.0-29fb557-static.zip
Ah, I didn’t realize that
WaitForExitAsyncis only for .NET Core 5.0+. If you want, I can retest this with .NET Core 3.1, and see if there is way to fix this with this requirement.The
Image.NewFromFile->Image.NewFromBufferchange is more like a workaround than a solution. I think you could also workaround this with theImage.NewFromStreamfunction to avoid loading the entire file into memory, for example: https://github.com/kleisauke/net-vips/blob/087f9f7245b425285d548847d2f6f2c9abef28e3/tests/NetVips.Tests/ForeignTests.cs#L1055-L1060I was able to reproduce it, and after some testing, I was able to resolve it with this patch:
(tested 20 times without problems)
So, it looks like a race condition between the
xcopycommand and whenever libvips opens a file. This comment might also be relevant here: https://github.com/dotnet/runtime/blob/4ac2aaa87db417e57715abe63078bb6a2d8a18a4/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.cs#L1424-L1458There might be still a change you’ll get a
System.UnauthorizedAccessExceptionexception on Windows due toThumbs.db, search indexers and/or anti-virus software. In this case, it might be better to delete the image immediately after you have used it. For example: