Add thread validation to the .NET compressor object.

Plus for safeties sake use BeginThreadAffinity()...
2.0
dblack@fastmail.fm 14 years ago
parent 89206909a4
commit 20b73458a5

@ -1,6 +1,8 @@
using System; using System;
using System.Security; using System.Security;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
namespace Nvidia.TextureTools namespace Nvidia.TextureTools
{ {
@ -159,7 +161,12 @@ namespace Nvidia.TextureTools
get { return errorCode; } get { return errorCode; }
} }
public TextureToolsException(Error errorCode) : base(Compressor.ErrorString(errorCode)) public TextureToolsException(Error errorCode) : this(errorCode, Compressor.ErrorString(errorCode))
{
}
public TextureToolsException(Error errorCode, string msg)
: base(msg)
{ {
this.errorCode = errorCode; this.errorCode = errorCode;
} }
@ -720,14 +727,24 @@ namespace Nvidia.TextureTools
#endregion #endregion
internal IntPtr compressor; internal IntPtr compressor;
private Thread creatingThread;
public Compressor() public Compressor()
{ {
/*
* Strictly speaking the CLR may decide to move managed threads between OS thread, to prevent this we call BeginThreadAffinity().
* In practice this doesnt happen, but it may if the host is using some form of custom thread management.
*/
creatingThread = Thread.CurrentThread;
Thread.BeginThreadAffinity();
compressor = nvttCreateCompressor(); compressor = nvttCreateCompressor();
} }
public void Compress(InputOptions input, CompressionOptions compression, OutputOptions output) public void Compress(InputOptions input, CompressionOptions compression, OutputOptions output)
{ {
ValidateThread();
if (!nvttCompress(compressor, input.options, compression.options, output.options)) if (!nvttCompress(compressor, input.options, compression.options, output.options))
{ {
//An error occured, use the last error registered. //An error occured, use the last error registered.
@ -737,6 +754,8 @@ namespace Nvidia.TextureTools
public int EstimateSize(InputOptions input, CompressionOptions compression) public int EstimateSize(InputOptions input, CompressionOptions compression)
{ {
ValidateThread();
return nvttEstimateSize(compressor, input.options, compression.options); return nvttEstimateSize(compressor, input.options, compression.options);
} }
@ -752,9 +771,20 @@ namespace Nvidia.TextureTools
public void SetEnableCuda(bool enableCuda) public void SetEnableCuda(bool enableCuda)
{ {
ValidateThread();
nvttEnableCudaCompression(compressor, enableCuda); nvttEnableCudaCompression(compressor, enableCuda);
} }
private void ValidateThread()
{
if (Thread.CurrentThread != creatingThread)
{
throw new TextureToolsException(Error.Unknown, "Compressor objects should only be used on the thread which creates them, this is because CUDA stores per thread data.");
}
}
#region IDisposable Members #region IDisposable Members
public void Dispose() public void Dispose()
@ -766,11 +796,31 @@ namespace Nvidia.TextureTools
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (compressor != IntPtr.Zero) if (compressor != IntPtr.Zero)
{ {
if (!disposing)
{
/*
* Throwing exceptions from finalizers is a bad idea, so just let the user know in a debug build.
* Otherwise we presumably leak the cuda context data, but still deallocate the memory for the compressor object.
*/
Debug.Fail("Compressor objects should always be disposed on the thread which creates them, this is because CUDA stores per thread data which should be cleaned up on the calling thread");
}
else
{
ValidateThread();
}
nvttDestroyCompressor(compressor); nvttDestroyCompressor(compressor);
compressor = IntPtr.Zero; compressor = IntPtr.Zero;
} }
if (creatingThread != null)
{
Thread.EndThreadAffinity();
creatingThread = null;
}
} }
~Compressor() ~Compressor()

Loading…
Cancel
Save