Skip to content

Commit a9b26a1

Browse files
committed
Hardening thread safety for ContextCache
Resolves #179 Changed internal implementation of ContextCache and interactions with Context construction to properly operate thread safe. (e.g. multiple threads retrieve the same context for which a context instance is not mapped yet)
1 parent bbac220 commit a9b26a1

File tree

2 files changed

+17
-23
lines changed

2 files changed

+17
-23
lines changed

src/Ubiquity.NET.Llvm/Context.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using System.Diagnostics;
1010
using System.Globalization;
1111
using System.Linq;
12-
using System.Numerics;
1312
using System.Text;
1413

1514
using Ubiquity.ArgValidators;
@@ -50,6 +49,7 @@ public sealed class Context
5049
public Context( )
5150
: this( LLVMContextCreate( ) )
5251
{
52+
ContextCache.Add( this );
5353
}
5454

5555
/// <summary>Gets the LLVM void type for this context</summary>
@@ -849,14 +849,14 @@ internal Context( LLVMContextRef contextRef )
849849
}
850850

851851
ContextHandle = contextRef;
852-
ContextCache.Add( this );
853852
ActiveHandler = new WrappedNativeCallback<LLVMDiagnosticHandler>( DiagnosticHandler );
854-
LLVMContextSetDiagnosticHandler( ContextHandle, ActiveHandler, IntPtr.Zero );
855853
ValueCache = new ValueCache( this );
856854
ModuleCache = new BitcodeModule.InterningFactory( this );
857855
TypeCache = new TypeRef.InterningFactory( this );
858856
AttributeValueCache = new AttributeValue.InterningFactory( this );
859857
MetadataCache = new LlvmMetadata.InterningFactory( this );
858+
859+
LLVMContextSetDiagnosticHandler( ContextHandle, ActiveHandler, IntPtr.Zero );
860860
}
861861

862862
/// <inheritdoc />
@@ -876,7 +876,7 @@ protected override void Dispose( bool disposing )
876876
LLVMContextSetDiagnosticHandler( ContextHandle, null, IntPtr.Zero );
877877
ActiveHandler.Dispose( );
878878

879-
ContextCache.Remove( ContextHandle );
879+
ContextCache.TryRemove( ContextHandle );
880880

881881
ContextHandle.Dispose( );
882882
}

src/Ubiquity.NET.Llvm/ContextCache.cs

+13-19
Original file line numberDiff line numberDiff line change
@@ -15,36 +15,30 @@
1515
namespace Ubiquity.NET.Llvm
1616
{
1717
/// <summary>Maintains a global cache of <see cref="LLVMContextRef"/> to <see cref="Context"/> mappings</summary>
18+
/// <remarks>
19+
/// The public constructor <see cref="Context.Context()"/> will add itself to the cache, since it is a new instance
20+
/// that is a safe operation. In all other cases a lookup in the cache based on the underlying LLVM handle is
21+
/// performed in a thread safe manner.
22+
/// </remarks>
1823
internal static class ContextCache
1924
{
20-
internal static bool TryGetValue( LLVMContextRef h, out Context value )
21-
{
22-
return Instance.Value.TryGetValue( h, out value );
23-
}
24-
25-
internal static bool Remove( LLVMContextRef h )
25+
internal static bool TryRemove( LLVMContextRef h )
2626
{
2727
return Instance.Value.TryRemove( h, out Context _ );
2828
}
2929

30-
internal static Context Add( Context context )
30+
internal static void Add( Context context )
3131
{
32-
return Instance.Value.GetOrAdd( context.ContextHandle, context );
32+
if( !Instance.Value.TryAdd( context.ContextHandle, context ) )
33+
{
34+
throw new InternalCodeGeneratorException( "Internal Error: Can't add context to Cache as it already exists!" );
35+
}
3336
}
3437

3538
internal static Context GetContextFor( LLVMContextRef contextRef )
3639
{
37-
contextRef.ValidateNotNull( nameof( contextRef ) );
38-
39-
if( TryGetValue( contextRef, out Context retVal ) )
40-
{
41-
return retVal;
42-
}
43-
44-
// Context constructor will add itself to this cache
45-
// and remove itself on Dispose/finalize
46-
// TODO: resolve thread safety bug (https://github.com/UbiquityDotNET/Llvm.NET/issues/179)
47-
return new Context( contextRef );
40+
contextRef.ThrowIfInvalid( );
41+
return Instance.Value.GetOrAdd( contextRef, h => new Context( h ) );
4842
}
4943

5044
private static ConcurrentDictionary<LLVMContextRef, Context> CreateInstance( )

0 commit comments

Comments
 (0)