Skip to content

Commit 7fb58a4

Browse files
committed
add Concurrency mod
1 parent 60f39b1 commit 7fb58a4

File tree

3 files changed

+120
-14
lines changed

3 files changed

+120
-14
lines changed

src/Mapster.Tests/WhenRegisteringAndMappingRace.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public void TestCleanup()
1515
{
1616
TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = false;
1717
TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = false;
18+
TypeAdapterConfig.GlobalSettings.IsConcurrencyEnvironment = false;
1819
}
1920

2021

@@ -111,21 +112,23 @@ public void Race_Condition_Working()
111112
var simplePoco = new WhenAddingCustomMappings.SimplePoco { Id = Guid.NewGuid(), Name = "TestName" };
112113

113114
//first state (i = 0) Must be configured
114-
TypeAdapterConfig<WhenAddingCustomMappings.SimplePoco, WeirdPoco>.NewConfig()
115+
TypeAdapterConfigConcurrency<WhenAddingCustomMappings.SimplePoco, WeirdPoco>.NewConfig()
115116
.Map(dest => dest.IHaveADifferentId, src => src.Id)
116117
.Map(dest => dest.MyNamePropertyIsDifferent, src => src.Name)
117-
.Ignore(dest => dest.Children);
118+
.Ignore(dest => dest.Children)
119+
.FinalizeConfig();
118120

119121

120122
for (int i = 0; i < 100; i++)
121123
{
122124
Parallel.Invoke(
123125
() =>
124126
{
125-
TypeAdapterConfig<WhenAddingCustomMappings.SimplePoco, WeirdPoco>.NewConfig()
127+
TypeAdapterConfigConcurrency<WhenAddingCustomMappings.SimplePoco, WeirdPoco>.NewConfig()
126128
.Map(dest => dest.IHaveADifferentId, src => src.Id)
127129
.Map(dest => dest.MyNamePropertyIsDifferent, src => src.Name)
128-
.Ignore(dest => dest.Children);
130+
.Ignore(dest => dest.Children)
131+
.FinalizeConfig();
129132
},
130133
() => { TypeAdapter.Adapt<WeirdPoco>(simplePoco); }
131134
);
@@ -150,7 +153,7 @@ public void Scan_Race_Condition_Working()
150153
Parallel.Invoke(
151154
() =>
152155
{
153-
TypeAdapterConfig.GlobalSettings.Scan(Assembly.GetExecutingAssembly());
156+
TypeAdapterConfig.GlobalSettings.ScanConcurrency(Assembly.GetExecutingAssembly());
154157
},
155158
() => { TypeAdapter.Adapt<WeirdPoco>(simplePoco); }
156159
);

src/Mapster/TypeAdapterConfig.cs

Lines changed: 95 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,32 @@
1-
using System;
1+
using Mapster.Adapters;
2+
using Mapster.Models;
3+
using Mapster.Utils;
4+
using System;
25
using System.Collections.Concurrent;
36
using System.Collections.Generic;
47
using System.Linq;
58
using System.Linq.Expressions;
69
using System.Reflection;
710
using System.Runtime.CompilerServices;
8-
using Mapster.Adapters;
9-
using Mapster.Models;
10-
using Mapster.Utils;
11+
using System.Threading;
1112

1213
namespace Mapster
1314
{
1415
public class TypeAdapterConfig
1516
{
17+
#region ConcurrencyMod
18+
19+
[AdaptIgnore]
20+
public AutoResetEvent ConfigureSync { get; set; }
21+
22+
[AdaptIgnore]
23+
public AutoResetEvent ApplySync { get; set; }
24+
25+
public bool IsConcurrencyEnvironment { get; set; }
26+
public bool IsScanConcurrency { get; set; }
27+
28+
#endregion ConcurrencyMod
29+
1630
public static List<TypeAdapterRule> RulesTemplate { get; } = CreateRuleTemplate();
1731
public static TypeAdapterConfig GlobalSettings { get; } = new TypeAdapterConfig();
1832

@@ -95,6 +109,9 @@ private static List<TypeAdapterRule> CreateRuleTemplate()
95109

96110
public TypeAdapterConfig()
97111
{
112+
ConfigureSync = new(true);
113+
ApplySync = new(true);
114+
98115
Rules = RulesTemplate.ToList();
99116
var settings = new TypeAdapterSettings();
100117
Default = new TypeAdapterSetter(settings, this);
@@ -148,6 +165,9 @@ public TypeAdapterSetter When(Func<PreCompileArgument, bool> canMap)
148165
/// <returns></returns>
149166
public TypeAdapterSetter<TSource, TDestination> NewConfig<TSource, TDestination>()
150167
{
168+
if (IsConcurrencyEnvironment && !IsScanConcurrency)
169+
ConfigureSync.WaitOne(-1, false);
170+
151171
Remove(typeof(TSource), typeof(TDestination));
152172
return ForType<TSource, TDestination>();
153173
}
@@ -161,6 +181,9 @@ public TypeAdapterSetter<TSource, TDestination> NewConfig<TSource, TDestination>
161181
/// <returns></returns>
162182
public TypeAdapterSetter NewConfig(Type sourceType, Type destinationType)
163183
{
184+
if (IsConcurrencyEnvironment && !IsScanConcurrency)
185+
ConfigureSync.WaitOne(-1, false);
186+
164187
Remove(sourceType, destinationType);
165188
return ForType(sourceType, destinationType);
166189
}
@@ -385,6 +408,12 @@ internal Expression CreateDynamicMapInvokeExpressionBody(Type destinationType, E
385408

386409
public LambdaExpression CreateMapExpression(TypeTuple tuple, MapType mapType)
387410
{
411+
if (IsConcurrencyEnvironment)
412+
ConfigureSync.WaitOne(-1);
413+
414+
if (IsScanConcurrency)
415+
ApplySync.WaitOne(-1);
416+
388417
var context = new CompileContext(this);
389418
context.Running.Add(tuple);
390419
Action<TypeAdapterConfig>? fork = null;
@@ -403,6 +432,9 @@ public LambdaExpression CreateMapExpression(TypeTuple tuple, MapType mapType)
403432
}
404433
finally
405434
{
435+
ApplySync.Set();
436+
ConfigureSync.Set();
437+
406438
if (fork != null)
407439
context.Configs.Pop();
408440
context.Running.Remove(tuple);
@@ -738,12 +770,29 @@ public IList<IRegister> Scan(params Assembly[] assemblies)
738770
return registers;
739771
}
740772

773+
public IList<IRegister> ScanConcurrency(params Assembly[] assemblies)
774+
{
741775

742-
/// <summary>
743-
/// Applies type mappings.
744-
/// </summary>
745-
/// <param name="registers">collection of IRegister interface to apply mapping.</param>
746-
public void Apply(IEnumerable<Lazy<IRegister>> registers)
776+
IsConcurrencyEnvironment = true;
777+
ConfigureSync.WaitOne(-1);
778+
IsScanConcurrency = true;
779+
780+
try
781+
{
782+
return Scan(assemblies);
783+
}
784+
finally
785+
{
786+
ConfigureSync.Set();
787+
IsConcurrencyEnvironment = false;
788+
}
789+
}
790+
791+
/// <summary>
792+
/// Applies type mappings.
793+
/// </summary>
794+
/// <param name="registers">collection of IRegister interface to apply mapping.</param>
795+
public void Apply(IEnumerable<Lazy<IRegister>> registers)
747796
{
748797
Apply(registers.Select(register => register.Value));
749798
}
@@ -755,10 +804,14 @@ public void Apply(IEnumerable<Lazy<IRegister>> registers)
755804
/// <param name="registers">collection of IRegister interface to apply mapping.</param>
756805
public void Apply(IEnumerable<IRegister> registers)
757806
{
807+
ApplySync.WaitOne(-1, false);
808+
758809
foreach (IRegister register in registers)
759810
{
760811
register.Register(this);
761812
}
813+
814+
ApplySync.Set();
762815
}
763816

764817

@@ -882,4 +935,37 @@ public static void Clear()
882935
TypeAdapterConfig.GlobalSettings.Remove(typeof(TSource), typeof(TDestination));
883936
}
884937
}
938+
939+
public static class TypeAdapterConfigConcurrency<TSource, TDestination>
940+
{
941+
/// <summary>
942+
/// Creates a new configuration for mapping between the source and destination types.
943+
/// </summary>
944+
/// <returns></returns>
945+
public static TypeAdapterSetter<TSource, TDestination> NewConfig()
946+
{
947+
TypeAdapterConfig.GlobalSettings.IsConcurrencyEnvironment = true;
948+
return TypeAdapterConfig.GlobalSettings.NewConfig<TSource, TDestination>();
949+
}
950+
951+
952+
/// <summary>
953+
/// Creates a configuration for mapping between the source and destination types.
954+
/// </summary>
955+
/// <returns></returns>
956+
public static TypeAdapterSetter<TSource, TDestination> ForType()
957+
{
958+
TypeAdapterConfig.GlobalSettings.IsConcurrencyEnvironment = true;
959+
return TypeAdapterConfig.GlobalSettings.ForType<TSource, TDestination>();
960+
}
961+
962+
963+
/// <summary>
964+
/// Clears the type mapping configuration for the specified source and destination types.
965+
/// </summary>
966+
public static void Clear()
967+
{
968+
TypeAdapterConfig.GlobalSettings.Remove(typeof(TSource), typeof(TDestination));
969+
}
970+
}
885971
}

src/Mapster/TypeAdapterSetter.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,23 @@ internal static void CheckCompiled<TSetter>(this TSetter setter) where TSetter :
3131
throw new InvalidOperationException("TypeAdapter.Adapt was already called, please clone or create new TypeAdapterConfig.");
3232
}
3333

34+
public static TSetter FinalizeConfig<TSetter>(this TSetter setter) where TSetter : TypeAdapterSetter
35+
{
36+
try
37+
{
38+
setter.CheckCompiled();
39+
return setter;
40+
}
41+
finally
42+
{
43+
if (setter.Config.IsConcurrencyEnvironment)
44+
{
45+
setter.Config.ConfigureSync.Set();
46+
}
47+
48+
}
49+
}
50+
3451
public static TSetter AddDestinationTransform<TSetter, TDestinationMember>(this TSetter setter, Expression<Func<TDestinationMember, TDestinationMember>> transform) where TSetter : TypeAdapterSetter
3552
{
3653
setter.CheckCompiled();

0 commit comments

Comments
 (0)