SharpFluids icon indicating copy to clipboard operation
SharpFluids copied to clipboard

Thread Unsafe XUnit Testing

Open theodoreOnzGit opened this issue 3 years ago • 0 comments

Hi MadsKirkFoged,

As promised, here is a bug report demonstrating thread unsafe-ness of coolprop using sharpfluids.

I made an xunit test:

dotnet new xunit --output threadSafetyTestSharpFluids

and i made two identical unit tests and placed them under two files: UnitTest1.cs UnitTest2.cs

Here are the codes for each unit test respectively.

using EngineeringUnits;
using EngineeringUnits.Units;
using SharpFluids;
using Xunit;
using System;
using Xunit.Abstractions;


namespace therminolPipeTest;

public class UnitTest1 
{

	[Theory]
	[InlineData(20,1064)]
	[InlineData(30,1056)]
	[InlineData(40,1048)]
	[InlineData(50,1040)]
	[InlineData(60,1032)]
	[InlineData(70,1024)]
	[InlineData(80,1015)]
	[InlineData(90,1007)]
	[InlineData(100,999)]
	[InlineData(110,991)]
	[InlineData(120,982)]
	[InlineData(130,974)]
	[InlineData(140,965)]
	[InlineData(150,957)]
	[InlineData(160,948)]
	[InlineData(180,931)]
	public void WhenTherminolObjectTestedExpectVendorDensityValue(
			double temperatureC, double densityValueKgPerM3){

		//Setup


		// set temperature and pressure for dowtherm and Therminol
		Pressure referencePressure = new Pressure(1.1013e5, PressureUnit.Pascal);
		EngineeringUnits.Temperature testTemperature 
			= new EngineeringUnits.Temperature(temperatureC, 
					TemperatureUnit.DegreeCelsius);

		// get therminol VP-1 fluid object
		Fluid therminol = new Fluid(FluidList.InCompTherminolVP1);

		// Act
		therminol.UpdatePT(referencePressure, testTemperature);

		Density resultDensity = therminol.Density;

		// Assert 
		//
		// Check if densities are equal to within 0.2% of vendor data
	
		double errorMax = 0.2/100;
		double resultDensityValueKgPerM3 = resultDensity.
			As(DensityUnit.KilogramPerCubicMeter);
		double error = Math.Abs(resultDensityValueKgPerM3 - 
				densityValueKgPerM3)/densityValueKgPerM3;

		if (error < errorMax){
			return;
		}
		if (error > errorMax){

		Assert.Equal(densityValueKgPerM3, 
				resultDensity.As(DensityUnit.KilogramPerCubicMeter),
				0);
		}
		
	}


}

And,

using EngineeringUnits;
using EngineeringUnits.Units;
using SharpFluids;
using Xunit;
using System;
using Xunit.Abstractions;


namespace therminolPipeTest;

public class UnitTest2 
{

	[Theory]
	[InlineData(20,1064)]
	[InlineData(30,1056)]
	[InlineData(40,1048)]
	[InlineData(50,1040)]
	[InlineData(60,1032)]
	[InlineData(70,1024)]
	[InlineData(80,1015)]
	[InlineData(90,1007)]
	[InlineData(100,999)]
	[InlineData(110,991)]
	[InlineData(120,982)]
	[InlineData(130,974)]
	[InlineData(140,965)]
	[InlineData(150,957)]
	[InlineData(160,948)]
	[InlineData(180,931)]
	public void WhenTherminolObjectTestedExpectVendorDensityValue(
			double temperatureC, double densityValueKgPerM3){

		//Setup


		// set temperature and pressure for dowtherm and Therminol
		Pressure referencePressure = new Pressure(1.1013e5, PressureUnit.Pascal);
		EngineeringUnits.Temperature testTemperature 
			= new EngineeringUnits.Temperature(temperatureC, 
					TemperatureUnit.DegreeCelsius);

		// get therminol VP-1 fluid object
		Fluid therminol = new Fluid(FluidList.InCompTherminolVP1);

		// Act
		therminol.UpdatePT(referencePressure, testTemperature);

		Density resultDensity = therminol.Density;

		// Assert 
		//
		// Check if densities are equal to within 0.2% of vendor data
	
		double errorMax = 0.2/100;
		double resultDensityValueKgPerM3 = resultDensity.
			As(DensityUnit.KilogramPerCubicMeter);
		double error = Math.Abs(resultDensityValueKgPerM3 - 
				densityValueKgPerM3)/densityValueKgPerM3;

		if (error < errorMax){
			return;
		}
		if (error > errorMax){

		Assert.Equal(densityValueKgPerM3, 
				resultDensity.As(DensityUnit.KilogramPerCubicMeter),
				0);
		}
		
	}


}

Now running both of these using dotnet watch test yields:

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
The active test run was aborted. Reason: Test host process crashed : Unhandled exception. System.ApplicationException: FATAL: An earlier pending exception from unmanaged code was missed and thus not thrown (System.ApplicationException: This backend does not implement calc_phase function)
 ---> System.ApplicationException: calc_compressibility_factor is not implemented for this backend
   --- End of inner exception stack trace ---
   at CoolPropPINVOKE64.SWIGPendingException.Set(Exception e)
   at CoolPropPINVOKE64.SWIGExceptionHelper.SetPendingApplicationException(String message)


Test Run Aborted with error System.Exception: One or more errors occurred.
 ---> System.Exception: Unable to read beyond the end of the stream.
   at System.IO.BinaryReader.ReadByte()
   at System.IO.BinaryReader.Read7BitEncodedInt()
   at System.IO.BinaryReader.ReadString()
   at Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.LengthPrefixCommunicationChannel.NotifyDataAvailable()
   at Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.TcpClientExtensions.MessageLoopAsync(TcpClient client, ICommunicationChannel channel, Action`1 errorHandler, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---.
watch : Exited with error code 1
watch : Waiting for a file to change before restarting dotnet...

This happens with almost absolute certainty.

If you put the same classes in the same unit test, it will also throw some sort of race condition like error.

In fat i've gotten

Test host process crashed: malloc(): unaligned tcache chunk detected

So this can show that coolprop is definitely not thread safe.

How to solve it? I haven't quite thought of it lol... It's just quite the headache now.

theodoreOnzGit avatar Aug 25 '22 23:08 theodoreOnzGit