From 20b73458a5865c3ac73e6f756c440792336bc680 Mon Sep 17 00:00:00 2001 From: "dblack@fastmail.fm" Date: Sat, 5 Jun 2010 15:26:34 +0000 Subject: [PATCH] Add thread validation to the .NET compressor object. Plus for safeties sake use BeginThreadAffinity()... --- .../vc8/Nvidia.TextureTools/TextureTools.cs | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/project/vc8/Nvidia.TextureTools/TextureTools.cs b/project/vc8/Nvidia.TextureTools/TextureTools.cs index 7192c73..9c5e2f9 100644 --- a/project/vc8/Nvidia.TextureTools/TextureTools.cs +++ b/project/vc8/Nvidia.TextureTools/TextureTools.cs @@ -1,6 +1,8 @@ using System; using System.Security; using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Threading; namespace Nvidia.TextureTools { @@ -159,7 +161,12 @@ namespace Nvidia.TextureTools 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; } @@ -720,14 +727,24 @@ namespace Nvidia.TextureTools #endregion internal IntPtr compressor; + private Thread creatingThread; 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(); } public void Compress(InputOptions input, CompressionOptions compression, OutputOptions output) { + ValidateThread(); + if (!nvttCompress(compressor, input.options, compression.options, output.options)) { //An error occured, use the last error registered. @@ -737,6 +754,8 @@ namespace Nvidia.TextureTools public int EstimateSize(InputOptions input, CompressionOptions compression) { + ValidateThread(); + return nvttEstimateSize(compressor, input.options, compression.options); } @@ -752,9 +771,20 @@ namespace Nvidia.TextureTools public void SetEnableCuda(bool enableCuda) { + ValidateThread(); + 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 public void Dispose() @@ -766,11 +796,31 @@ namespace Nvidia.TextureTools protected virtual void Dispose(bool disposing) { + 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); compressor = IntPtr.Zero; } + + if (creatingThread != null) + { + Thread.EndThreadAffinity(); + creatingThread = null; + } } ~Compressor()