Skip to content

Commit

Permalink
Merge pull request #5 from Cricle/with-gchandler
Browse files Browse the repository at this point in the history
With gchandler
  • Loading branch information
Cricle committed May 31, 2023
2 parents 97e200c + 4fa0415 commit bcb8d79
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 38 deletions.
1 change: 1 addition & 0 deletions samples/UnionType.Sample/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Diagnostics;
using System.Numerics;
using System.Runtime.InteropServices;

namespace UnionType.Sample
{
Expand Down
92 changes: 70 additions & 22 deletions src/UnionType/UnionValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,26 @@ namespace UnionType
[StructLayout(LayoutKind.Explicit)]
public unsafe struct UnionValue : IEquatable<UnionValue>, ICloneable, IComparable, IConvertible, IFormattable,IDisposable
{
private static bool objectWithType = true;
public static bool ObjectWithType
{
get => objectWithType;
set => objectWithType = value;
}

public static readonly int Size = 16+sizeof(UnionValueType)+sizeof(byte);

public static UnionValue Empty => new UnionValue();

private static readonly IntPtr stringPtr;
private static readonly GCHandle stringPtrGcHandler;
private static readonly byte[] EmptyBytes = Array.Empty<byte>();

static UnionValue()
{
var n = typeof(string).AssemblyQualifiedName;
stringPtr = (IntPtr)GCHandle.Alloc(n, GCHandleType.Pinned);
stringPtrGcHandler = GCHandle.Alloc(n, GCHandleType.Pinned);
stringPtr = (IntPtr)stringPtrGcHandler;
}

[FieldOffset(0)]
Expand Down Expand Up @@ -63,8 +72,12 @@ static UnionValue()
public Guid @guid;
[FieldOffset(0)]//8L
public IntPtr @intPtr;
[FieldOffset(0)]
public GCHandle objectHandler;
[FieldOffset(8)]//8L
public IntPtr @typeName;
[FieldOffset(8)]
public GCHandle typeGCHandler;
[FieldOffset(16)]
public UnionValueType unionValueType;
[FieldOffset(17)]
Expand Down Expand Up @@ -254,6 +267,36 @@ public IntPtr IntPtr
unionValueType = UnionValueType.IntPtr;
}
}
public GCHandle TypeGCHandler
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => typeGCHandler;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if (typeGCHandler != stringPtrGcHandler && typeGCHandler.IsAllocated)
{
typeGCHandler.Free();
}
typeGCHandler = value;
TypeName = (IntPtr)value;
}
}
public GCHandle ObjectHandler
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => objectHandler;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if (objectHandler.IsAllocated)
{
objectHandler.Free();
}
objectHandler = value;
IntPtr = (IntPtr)value;
}
}
public UnionValueType UnionValueType
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -314,10 +357,16 @@ public IntPtr TypeName
{
if (value != null)
{
typeName = (IntPtr)GCHandle.Alloc(value, (GCHandleType)gcHandleType);
typeGCHandler= GCHandle.Alloc(value, (GCHandleType)gcHandleType);
typeName = (IntPtr)typeGCHandler;
}
else
{
if (typeGCHandler.IsAllocated)
{
typeGCHandler.Free();
}
typeGCHandler = default;
typeName = IntPtr.Zero;
}
}
Expand All @@ -332,21 +381,21 @@ public IntPtr TypeName
return null;
}

return (string?)(GCHandle.FromIntPtr(intPtr).Target);
return (string?)(ObjectHandler.Target);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
if (value != null)
{
intPtr = (IntPtr)GCHandle.Alloc(value, (GCHandleType)gcHandleType);
ObjectHandler = GCHandle.Alloc(value, (GCHandleType)gcHandleType);
}
else
{
intPtr = IntPtr.Zero;
ObjectHandler = default;
}
unionValueType = UnionValueType.String;
@typeName = stringPtr;
TypeGCHandler = stringPtrGcHandler;
}
}

Expand Down Expand Up @@ -607,13 +656,13 @@ public unsafe void ToBytes(void* buffer)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public object? GetObject()
{
return GCHandle.FromIntPtr(intPtr).Target;
return objectHandler.Target;
}
public void SetObject(object? value, GCHandleType gcHandleType= GCHandleType.Weak)
{
if (unionValueType== UnionValueType.Object&&intPtr!=IntPtr.Zero&&gcHandleType!= GCHandleType.Weak)
if (objectHandler.IsAllocated)
{
GCHandle.FromIntPtr(intPtr).Free();
objectHandler.Free();
}
if (value == null)
{
Expand All @@ -624,8 +673,11 @@ public void SetObject(object? value, GCHandleType gcHandleType= GCHandleType.Wea
else
{
this.gcHandleType = (byte)gcHandleType;
IntPtr = (IntPtr)GCHandle.Alloc(value, (GCHandleType)gcHandleType);
TypeNameType = value!.GetType();
ObjectHandler = GCHandle.Alloc(value, gcHandleType);
if (objectWithType)
{
TypeNameType = value!.GetType();
}
unionValueType = UnionValueType.Object;
}
}
Expand Down Expand Up @@ -1299,21 +1351,17 @@ public BigInteger ToBigInteger()

public void Dispose()
{
try
if (TypeCode == TypeCode.Object || TypeCode == TypeCode.String)
{
if (TypeCode == TypeCode.Object|| TypeCode == TypeCode.String)
if (objectHandler.IsAllocated)
{
if (intPtr != IntPtr.Zero)
{
GCHandle.FromIntPtr(intPtr).Free();
}
if (typeName != IntPtr.Zero)
{
GCHandle.FromIntPtr(typeName).Free();
}
objectHandler.Free();
}
if (typeGCHandler.IsAllocated)
{
typeGCHandler.Free();
}
}
catch (Exception) { }
}
}

Expand Down
38 changes: 36 additions & 2 deletions test/UnionType.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,46 @@
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Analysers;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Validators;

namespace UnionType.Benchmarks
{
internal class Program
{
static void Main(string[] args)
{
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run();
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new MyConfig());
}
}

public class MyConfig : ManualConfig
{
public MyConfig()
{
AddLogger(BenchmarkDotNet.Loggers.ConsoleLogger.Unicode);
AddJob(Job.ShortRun.WithPlatform(Platform.X64).WithGcServer(true).WithRuntime(CoreRuntime.Core70));
AddJob(Job.ShortRun.WithPlatform(Platform.X64).WithGcServer(true).WithRuntime(NativeAotRuntime.Net70).WithId("AOT"));
AddExporter(BenchmarkDotNet.Exporters.MarkdownExporter.GitHub);
AddAnalyser(EnvironmentAnalyser.Default
, OutliersAnalyser.Default
, MinIterationTimeAnalyser.Default
, MultimodalDistributionAnalyzer.Default
, RuntimeErrorAnalyser.Default
, ZeroMeasurementAnalyser.Default
, BaselineCustomAnalyzer.Default
, HideColumnsAnalyser.Default
);
AddValidator(BaselineValidator.FailOnError
, SetupCleanupValidator.FailOnError
, JitOptimizationsValidator.FailOnError
, RunModeValidator.FailOnError
, GenericBenchmarksValidator.DontFailOnError
, DeferredExecutionValidator.FailOnError
, ParamsAllValuesValidator.FailOnError
);
AddColumnProvider(BenchmarkDotNet.Columns.DefaultColumnProviders.Instance);
}
}
}
17 changes: 4 additions & 13 deletions test/UnionType.Benchmarks/Runs/StoreObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,17 @@ public class StoreObject
[Params(500, 1_000_000)]
public int Count { get; set; }

[Benchmark(Baseline = true)]
public void Normal()
[GlobalSetup]
public void Setup()
{
var obj = new object[Count];
for (int i = 0; i < Count; i++)
{
obj[i] = new object();
}
UnionValue.ObjectWithType = false;
}
[Benchmark]
public void UnionObject()
{
var obj = new UnionValue[Count];
for (int i = 0; i < Count; i++)
{
obj[i] = new UnionValue { Object = new object() };
}
for (int i = 0; i < obj.Length; i++)
{
obj[i].Dispose();
new UnionValue { Object = new object() }.Dispose();
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion test/UnionType.Test/UnionType.Test.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net461;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>net472;net6.0;net7.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
Expand Down
36 changes: 36 additions & 0 deletions test/UnionType.Test/UnionValueTest.GC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,41 @@ namespace UnionType.Test
{
public partial class UnionValueTest
{
[TestMethod]
public void GCHandlerType()
{
var obj = new object();
var uv = new UnionValue();
uv.SetObject(obj, GCHandleType.Normal);
Assert.AreEqual(GCHandleType.Normal, uv.GCHandleType);
obj = null;
GC.Collect();
var back = uv.GetObject();
Assert.IsNotNull(back);
uv.Dispose();
}
[TestMethod]
public void GCHandlerType_Changed()
{
var obj = new object();
var uv = new UnionValue();
uv.GCHandleType = GCHandleType.Weak;
uv.Object = obj;
obj = null;
GC.Collect();
Assert.IsNull(uv.TypeGCHandler.Target);
uv.Dispose();
}
[TestMethod]
public void WithNoName_NotStoreTypeName()
{
UnionValue.ObjectWithType = false;
Assert.IsFalse(UnionValue.ObjectWithType);
var uv = new UnionValue();
uv.Object = new object();
Assert.IsTrue(uv.TypeName == IntPtr.Zero);
uv.Dispose();
UnionValue.ObjectWithType = true;
}
}
}

0 comments on commit bcb8d79

Please sign in to comment.