From 6bc556715bf50cabf41955d14ec962bee8226541 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Fri, 31 Oct 2025 16:28:02 +0500 Subject: [PATCH 1/9] fix Test --- src/Mapster.Tests/WhenRegisteringAndMappingRace.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs b/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs index e6d796dc..fb28087a 100644 --- a/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs +++ b/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs @@ -39,6 +39,12 @@ public void Race_Condition_Produces_Error() var simplePoco = new WhenAddingCustomMappings.SimplePoco {Id = Guid.NewGuid(), Name = "TestName"}; + //first state (i = 0) Must be configured + TypeAdapterConfig.NewConfig() + .Map(dest => dest.IHaveADifferentId, src => src.Id) + .Map(dest => dest.MyNamePropertyIsDifferent, src => src.Name) + .Ignore(dest => dest.Children); + var exception = Should.Throw(() => { for (int i = 0; i < 100; i++) @@ -66,6 +72,12 @@ public void Explicit_Mapping_Requirement_Throws_Before_Mapping_Attempted() TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true; TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; + //first state (i = 0) Must be configured + TypeAdapterConfig.NewConfig() + .Map(dest => dest.IHaveADifferentId, src => src.Id) + .Map(dest => dest.MyNamePropertyIsDifferent, src => src.Name) + .Ignore(dest => dest.Children); + var simplePoco = new WhenAddingCustomMappings.SimplePoco { Id = Guid.NewGuid(), Name = "TestName" }; Should.Throw(() => From 60f39b101f6641f6210b35135e9d545ec387e4b2 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Fri, 31 Oct 2025 16:38:00 +0500 Subject: [PATCH 2/9] Add Tests --- .../WhenRegisteringAndMappingRace.cs | 73 ++++++++++++++++++- src/Mapster.Tests/WhenScanningForRegisters.cs | 2 +- 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs b/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs index fb28087a..11a2a3ef 100644 --- a/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs +++ b/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs @@ -1,8 +1,9 @@ -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shouldly; +using System; using System.Collections.Generic; +using System.Reflection; using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Shouldly; namespace Mapster.Tests { @@ -101,12 +102,78 @@ public void Explicit_Mapping_Requirement_Throws_Before_Mapping_Attempted() TypeAdapter.Adapt(simplePoco); } + [TestMethod] + public void Race_Condition_Working() + { + TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; + TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; + + var simplePoco = new WhenAddingCustomMappings.SimplePoco { Id = Guid.NewGuid(), Name = "TestName" }; + + //first state (i = 0) Must be configured + TypeAdapterConfig.NewConfig() + .Map(dest => dest.IHaveADifferentId, src => src.Id) + .Map(dest => dest.MyNamePropertyIsDifferent, src => src.Name) + .Ignore(dest => dest.Children); + + + for (int i = 0; i < 100; i++) + { + Parallel.Invoke( + () => + { + TypeAdapterConfig.NewConfig() + .Map(dest => dest.IHaveADifferentId, src => src.Id) + .Map(dest => dest.MyNamePropertyIsDifferent, src => src.Name) + .Ignore(dest => dest.Children); + }, + () => { TypeAdapter.Adapt(simplePoco); } + ); + } + + } + + [TestMethod] + public void Scan_Race_Condition_Working() + { + TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; + TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; + + var simplePoco = new WhenAddingCustomMappings.SimplePoco { Id = Guid.NewGuid(), Name = "TestName" }; + + //first state (i = 0) Must be configured + TypeAdapterConfig.GlobalSettings.Scan(Assembly.GetExecutingAssembly()); + + for (int i = 0; i < 100; i++) + { + Parallel.Invoke( + () => + { + TypeAdapterConfig.GlobalSettings.Scan(Assembly.GetExecutingAssembly()); + }, + () => { TypeAdapter.Adapt(simplePoco); } + ); + } + + } } #region TestClasses + public class RegData : IRegister + { + public void Register(TypeAdapterConfig config) + { + config.NewConfig() + .Map(dest => dest.IHaveADifferentId, src => src.Id) + .Map(dest => dest.MyNamePropertyIsDifferent, src => src.Name) + .Ignore(dest => dest.Children); + + } + } + public class SimplePoco { public Guid Id { get; set; } diff --git a/src/Mapster.Tests/WhenScanningForRegisters.cs b/src/Mapster.Tests/WhenScanningForRegisters.cs index 20b287b6..c86e1298 100644 --- a/src/Mapster.Tests/WhenScanningForRegisters.cs +++ b/src/Mapster.Tests/WhenScanningForRegisters.cs @@ -16,7 +16,7 @@ public void Registers_Are_Found() { var config = new TypeAdapterConfig(); IList registers = config.Scan(Assembly.GetExecutingAssembly()); - registers.Count.ShouldBe(2); + registers.Count.ShouldBe(3); var typeTuples = config.RuleMap.Keys.ToList(); From 7fb58a434a400321ef1cfbe73a4552866b214cfc Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Fri, 31 Oct 2025 17:21:47 +0500 Subject: [PATCH 3/9] add Concurrency mod --- .../WhenRegisteringAndMappingRace.cs | 13 ++- src/Mapster/TypeAdapterConfig.cs | 104 ++++++++++++++++-- src/Mapster/TypeAdapterSetter.cs | 17 +++ 3 files changed, 120 insertions(+), 14 deletions(-) diff --git a/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs b/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs index 11a2a3ef..b8378ec4 100644 --- a/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs +++ b/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs @@ -15,6 +15,7 @@ public void TestCleanup() { TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = false; TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = false; + TypeAdapterConfig.GlobalSettings.IsConcurrencyEnvironment = false; } @@ -111,10 +112,11 @@ public void Race_Condition_Working() var simplePoco = new WhenAddingCustomMappings.SimplePoco { Id = Guid.NewGuid(), Name = "TestName" }; //first state (i = 0) Must be configured - TypeAdapterConfig.NewConfig() + TypeAdapterConfigConcurrency.NewConfig() .Map(dest => dest.IHaveADifferentId, src => src.Id) .Map(dest => dest.MyNamePropertyIsDifferent, src => src.Name) - .Ignore(dest => dest.Children); + .Ignore(dest => dest.Children) + .FinalizeConfig(); for (int i = 0; i < 100; i++) @@ -122,10 +124,11 @@ public void Race_Condition_Working() Parallel.Invoke( () => { - TypeAdapterConfig.NewConfig() + TypeAdapterConfigConcurrency.NewConfig() .Map(dest => dest.IHaveADifferentId, src => src.Id) .Map(dest => dest.MyNamePropertyIsDifferent, src => src.Name) - .Ignore(dest => dest.Children); + .Ignore(dest => dest.Children) + .FinalizeConfig(); }, () => { TypeAdapter.Adapt(simplePoco); } ); @@ -150,7 +153,7 @@ public void Scan_Race_Condition_Working() Parallel.Invoke( () => { - TypeAdapterConfig.GlobalSettings.Scan(Assembly.GetExecutingAssembly()); + TypeAdapterConfig.GlobalSettings.ScanConcurrency(Assembly.GetExecutingAssembly()); }, () => { TypeAdapter.Adapt(simplePoco); } ); diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index 1e6abade..6219384a 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -1,18 +1,32 @@ -using System; +using Mapster.Adapters; +using Mapster.Models; +using Mapster.Utils; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; -using Mapster.Adapters; -using Mapster.Models; -using Mapster.Utils; +using System.Threading; namespace Mapster { public class TypeAdapterConfig { + #region ConcurrencyMod + + [AdaptIgnore] + public AutoResetEvent ConfigureSync { get; set; } + + [AdaptIgnore] + public AutoResetEvent ApplySync { get; set; } + + public bool IsConcurrencyEnvironment { get; set; } + public bool IsScanConcurrency { get; set; } + + #endregion ConcurrencyMod + public static List RulesTemplate { get; } = CreateRuleTemplate(); public static TypeAdapterConfig GlobalSettings { get; } = new TypeAdapterConfig(); @@ -95,6 +109,9 @@ private static List CreateRuleTemplate() public TypeAdapterConfig() { + ConfigureSync = new(true); + ApplySync = new(true); + Rules = RulesTemplate.ToList(); var settings = new TypeAdapterSettings(); Default = new TypeAdapterSetter(settings, this); @@ -148,6 +165,9 @@ public TypeAdapterSetter When(Func canMap) /// public TypeAdapterSetter NewConfig() { + if (IsConcurrencyEnvironment && !IsScanConcurrency) + ConfigureSync.WaitOne(-1, false); + Remove(typeof(TSource), typeof(TDestination)); return ForType(); } @@ -161,6 +181,9 @@ public TypeAdapterSetter NewConfig /// public TypeAdapterSetter NewConfig(Type sourceType, Type destinationType) { + if (IsConcurrencyEnvironment && !IsScanConcurrency) + ConfigureSync.WaitOne(-1, false); + Remove(sourceType, destinationType); return ForType(sourceType, destinationType); } @@ -385,6 +408,12 @@ internal Expression CreateDynamicMapInvokeExpressionBody(Type destinationType, E public LambdaExpression CreateMapExpression(TypeTuple tuple, MapType mapType) { + if (IsConcurrencyEnvironment) + ConfigureSync.WaitOne(-1); + + if (IsScanConcurrency) + ApplySync.WaitOne(-1); + var context = new CompileContext(this); context.Running.Add(tuple); Action? fork = null; @@ -403,6 +432,9 @@ public LambdaExpression CreateMapExpression(TypeTuple tuple, MapType mapType) } finally { + ApplySync.Set(); + ConfigureSync.Set(); + if (fork != null) context.Configs.Pop(); context.Running.Remove(tuple); @@ -738,12 +770,29 @@ public IList Scan(params Assembly[] assemblies) return registers; } + public IList ScanConcurrency(params Assembly[] assemblies) + { - /// - /// Applies type mappings. - /// - /// collection of IRegister interface to apply mapping. - public void Apply(IEnumerable> registers) + IsConcurrencyEnvironment = true; + ConfigureSync.WaitOne(-1); + IsScanConcurrency = true; + + try + { + return Scan(assemblies); + } + finally + { + ConfigureSync.Set(); + IsConcurrencyEnvironment = false; + } + } + + /// + /// Applies type mappings. + /// + /// collection of IRegister interface to apply mapping. + public void Apply(IEnumerable> registers) { Apply(registers.Select(register => register.Value)); } @@ -755,10 +804,14 @@ public void Apply(IEnumerable> registers) /// collection of IRegister interface to apply mapping. public void Apply(IEnumerable registers) { + ApplySync.WaitOne(-1, false); + foreach (IRegister register in registers) { register.Register(this); } + + ApplySync.Set(); } @@ -882,4 +935,37 @@ public static void Clear() TypeAdapterConfig.GlobalSettings.Remove(typeof(TSource), typeof(TDestination)); } } + + public static class TypeAdapterConfigConcurrency + { + /// + /// Creates a new configuration for mapping between the source and destination types. + /// + /// + public static TypeAdapterSetter NewConfig() + { + TypeAdapterConfig.GlobalSettings.IsConcurrencyEnvironment = true; + return TypeAdapterConfig.GlobalSettings.NewConfig(); + } + + + /// + /// Creates a configuration for mapping between the source and destination types. + /// + /// + public static TypeAdapterSetter ForType() + { + TypeAdapterConfig.GlobalSettings.IsConcurrencyEnvironment = true; + return TypeAdapterConfig.GlobalSettings.ForType(); + } + + + /// + /// Clears the type mapping configuration for the specified source and destination types. + /// + public static void Clear() + { + TypeAdapterConfig.GlobalSettings.Remove(typeof(TSource), typeof(TDestination)); + } + } } \ No newline at end of file diff --git a/src/Mapster/TypeAdapterSetter.cs b/src/Mapster/TypeAdapterSetter.cs index e08ef312..8fae8263 100644 --- a/src/Mapster/TypeAdapterSetter.cs +++ b/src/Mapster/TypeAdapterSetter.cs @@ -31,6 +31,23 @@ internal static void CheckCompiled(this TSetter setter) where TSetter : throw new InvalidOperationException("TypeAdapter.Adapt was already called, please clone or create new TypeAdapterConfig."); } + public static TSetter FinalizeConfig(this TSetter setter) where TSetter : TypeAdapterSetter + { + try + { + setter.CheckCompiled(); + return setter; + } + finally + { + if (setter.Config.IsConcurrencyEnvironment) + { + setter.Config.ConfigureSync.Set(); + } + + } + } + public static TSetter AddDestinationTransform(this TSetter setter, Expression> transform) where TSetter : TypeAdapterSetter { setter.CheckCompiled(); From 231223612f23a0fefc9f03af6b5807e0516edc0e Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 1 Nov 2025 11:32:31 +0500 Subject: [PATCH 4/9] Fix Test settings --- src/Mapster.Tests/WhenRegisteringAndMappingRace.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs b/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs index b8378ec4..804f8253 100644 --- a/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs +++ b/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs @@ -106,7 +106,7 @@ public void Explicit_Mapping_Requirement_Throws_Before_Mapping_Attempted() [TestMethod] public void Race_Condition_Working() { - TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; + TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true; TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; var simplePoco = new WhenAddingCustomMappings.SimplePoco { Id = Guid.NewGuid(), Name = "TestName" }; @@ -139,7 +139,7 @@ public void Race_Condition_Working() [TestMethod] public void Scan_Race_Condition_Working() { - TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; + TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = true; TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = true; var simplePoco = new WhenAddingCustomMappings.SimplePoco { Id = Guid.NewGuid(), Name = "TestName" }; From e9e997b42730be052121af439743d8cad7af1784 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sat, 1 Nov 2025 11:35:35 +0500 Subject: [PATCH 5/9] Fix Setter Compile Race Condition --- src/Mapster/TypeAdapterConfig.cs | 98 ++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 25 deletions(-) diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index 6219384a..6f7f6fbf 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -345,10 +345,23 @@ public Func GetMapFunction() } internal Delegate GetMapFunction(Type sourceType, Type destinationType) { - var key = new TypeTuple(sourceType, destinationType); - if (!_mapDict.TryGetValue(key, out var del)) - del = AddToHash(_mapDict, key, tuple => Compiler(CreateMapExpression(tuple, MapType.Map))); - return del; + if (IsConcurrencyEnvironment) + ConfigureSync.WaitOne(-1); + + if (IsScanConcurrency) + ApplySync.WaitOne(-1); + try + { + var key = new TypeTuple(sourceType, destinationType); + if (!_mapDict.TryGetValue(key, out var del)) + del = AddToHash(_mapDict, key, tuple => Compiler(CreateMapExpression(tuple, MapType.Map))); + return del; + } + finally + { + ApplySync.Set(); + ConfigureSync.Set(); + } } private readonly ConcurrentDictionary _mapToTargetDict = new ConcurrentDictionary(); @@ -358,10 +371,25 @@ public Func GetMapToTargetFunction Compiler(CreateMapExpression(tuple, MapType.MapToTarget))); - return del; + if (IsConcurrencyEnvironment) + ConfigureSync.WaitOne(-1); + + if (IsScanConcurrency) + ApplySync.WaitOne(-1); + + try + { + var key = new TypeTuple(sourceType, destinationType); + if (!_mapToTargetDict.TryGetValue(key, out var del)) + del = AddToHash(_mapToTargetDict, key, tuple => Compiler(CreateMapExpression(tuple, MapType.MapToTarget))); + return del; + } + finally + { + ApplySync.Set(); + ConfigureSync.Set(); + } + } private readonly ConcurrentDictionary _projectionDict = new ConcurrentDictionary(); @@ -373,19 +401,47 @@ internal Expression> GetProjectionExpression _dynamicMapDict = new ConcurrentDictionary(); public Func GetDynamicMapFunction(Type sourceType) { - var key = new TypeTuple(sourceType, typeof(TDestination)); - if (!_dynamicMapDict.TryGetValue(key, out var del)) - del = AddToHash(_dynamicMapDict, key, tuple => Compiler(CreateDynamicMapExpression(tuple))); - return (Func)del; + if (IsConcurrencyEnvironment) + ConfigureSync.WaitOne(-1); + + if (IsScanConcurrency) + ApplySync.WaitOne(-1); + + try + { + var key = new TypeTuple(sourceType, typeof(TDestination)); + if (!_dynamicMapDict.TryGetValue(key, out var del)) + del = AddToHash(_dynamicMapDict, key, tuple => Compiler(CreateDynamicMapExpression(tuple))); + return (Func)del; + } + finally + { + ApplySync.Set(); + ConfigureSync.Set(); + } } private Expression CreateSelfExpression() @@ -408,12 +464,6 @@ internal Expression CreateDynamicMapInvokeExpressionBody(Type destinationType, E public LambdaExpression CreateMapExpression(TypeTuple tuple, MapType mapType) { - if (IsConcurrencyEnvironment) - ConfigureSync.WaitOne(-1); - - if (IsScanConcurrency) - ApplySync.WaitOne(-1); - var context = new CompileContext(this); context.Running.Add(tuple); Action? fork = null; @@ -432,9 +482,7 @@ public LambdaExpression CreateMapExpression(TypeTuple tuple, MapType mapType) } finally { - ApplySync.Set(); - ConfigureSync.Set(); - + if (fork != null) context.Configs.Pop(); context.Running.Remove(tuple); From 59a9435bbb32d0a9a121645691968394469b56d9 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Sun, 2 Nov 2025 09:11:12 +0500 Subject: [PATCH 6/9] refactoring and fix test --- .../WhenRegisteringAndMappingRace.cs | 37 ++++++++----- src/Mapster/TypeAdapterConfig.cs | 55 ++++++------------- 2 files changed, 38 insertions(+), 54 deletions(-) diff --git a/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs b/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs index 804f8253..0a003cde 100644 --- a/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs +++ b/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs @@ -112,28 +112,35 @@ public void Race_Condition_Working() var simplePoco = new WhenAddingCustomMappings.SimplePoco { Id = Guid.NewGuid(), Name = "TestName" }; //first state (i = 0) Must be configured - TypeAdapterConfigConcurrency.NewConfig() - .Map(dest => dest.IHaveADifferentId, src => src.Id) - .Map(dest => dest.MyNamePropertyIsDifferent, src => src.Name) - .Ignore(dest => dest.Children) - .FinalizeConfig(); - - - for (int i = 0; i < 100; i++) + TypeAdapterConfigConcurrency + .NewConfig(cfg => + { + cfg + .Map(dest => dest.IHaveADifferentId, src => src.Id) + .Map(dest => dest.IHaveADifferentId, src => src.Id) + .Map(dest => dest.MyNamePropertyIsDifferent, src => src.Name) + .Ignore(dest => dest.Children); + }); + + for (int i = 0; i < 1000; i++) { Parallel.Invoke( () => { - TypeAdapterConfigConcurrency.NewConfig() - .Map(dest => dest.IHaveADifferentId, src => src.Id) - .Map(dest => dest.MyNamePropertyIsDifferent, src => src.Name) - .Ignore(dest => dest.Children) - .FinalizeConfig(); + TypeAdapterConfigConcurrency + .NewConfig(cfg => + { + cfg + .Map(dest => dest.IHaveADifferentId, src => src.Id) + .Map(dest => dest.IHaveADifferentId, src => src.Id) + .Map(dest => dest.MyNamePropertyIsDifferent, src => src.Name) + .Ignore(dest => dest.Children); + }); }, () => { TypeAdapter.Adapt(simplePoco); } ); } - + } [TestMethod] @@ -148,7 +155,7 @@ public void Scan_Race_Condition_Working() TypeAdapterConfig.GlobalSettings.Scan(Assembly.GetExecutingAssembly()); - for (int i = 0; i < 100; i++) + for (int i = 0; i < 1000; i++) { Parallel.Invoke( () => diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index 6f7f6fbf..138d41f5 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -165,9 +165,6 @@ public TypeAdapterSetter When(Func canMap) /// public TypeAdapterSetter NewConfig() { - if (IsConcurrencyEnvironment && !IsScanConcurrency) - ConfigureSync.WaitOne(-1, false); - Remove(typeof(TSource), typeof(TDestination)); return ForType(); } @@ -181,9 +178,6 @@ public TypeAdapterSetter NewConfig /// public TypeAdapterSetter NewConfig(Type sourceType, Type destinationType) { - if (IsConcurrencyEnvironment && !IsScanConcurrency) - ConfigureSync.WaitOne(-1, false); - Remove(sourceType, destinationType); return ForType(sourceType, destinationType); } @@ -345,8 +339,7 @@ public Func GetMapFunction() } internal Delegate GetMapFunction(Type sourceType, Type destinationType) { - if (IsConcurrencyEnvironment) - ConfigureSync.WaitOne(-1); + ConfigureSync.WaitOne(-1); if (IsScanConcurrency) ApplySync.WaitOne(-1); @@ -371,8 +364,7 @@ public Func GetMapToTargetFunction> GetProjectionExpression _dynamicMapDict = new ConcurrentDictionary(); public Func GetDynamicMapFunction(Type sourceType) { - if (IsConcurrencyEnvironment) - ConfigureSync.WaitOne(-1); + ConfigureSync.WaitOne(-1); if (IsScanConcurrency) ApplySync.WaitOne(-1); @@ -986,34 +976,21 @@ public static void Clear() public static class TypeAdapterConfigConcurrency { - /// - /// Creates a new configuration for mapping between the source and destination types. - /// - /// - public static TypeAdapterSetter NewConfig() + public static void NewConfig(Action> cfg) { - TypeAdapterConfig.GlobalSettings.IsConcurrencyEnvironment = true; - return TypeAdapterConfig.GlobalSettings.NewConfig(); - } + var config = TypeAdapterConfig.GlobalSettings; + config.IsConcurrencyEnvironment = true; + config.ConfigureSync.WaitOne(-1, false); - /// - /// Creates a configuration for mapping between the source and destination types. - /// - /// - public static TypeAdapterSetter ForType() - { - TypeAdapterConfig.GlobalSettings.IsConcurrencyEnvironment = true; - return TypeAdapterConfig.GlobalSettings.ForType(); - } - - - /// - /// Clears the type mapping configuration for the specified source and destination types. - /// - public static void Clear() - { - TypeAdapterConfig.GlobalSettings.Remove(typeof(TSource), typeof(TDestination)); + try + { + cfg.Invoke(TypeAdapterConfig.NewConfig()); + } + finally + { + config.ConfigureSync.Set(); + } } } } \ No newline at end of file From f71c108a92a83ae5be57fe5f8ad54e99cf53db85 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Wed, 5 Nov 2025 09:41:56 +0500 Subject: [PATCH 7/9] final refactoring: - drop IsConcurrencyEnvironment - all Concurrencymod property to internal --- .../WhenRegisteringAndMappingRace.cs | 1 - src/Mapster/TypeAdapterConfig.cs | 12 +++--------- src/Mapster/TypeAdapterSetter.cs | 17 ----------------- 3 files changed, 3 insertions(+), 27 deletions(-) diff --git a/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs b/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs index 0a003cde..f32250c6 100644 --- a/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs +++ b/src/Mapster.Tests/WhenRegisteringAndMappingRace.cs @@ -15,7 +15,6 @@ public void TestCleanup() { TypeAdapterConfig.GlobalSettings.RequireExplicitMapping = false; TypeAdapterConfig.GlobalSettings.RequireDestinationMemberSource = false; - TypeAdapterConfig.GlobalSettings.IsConcurrencyEnvironment = false; } diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index 138d41f5..1551f1b5 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -17,13 +17,11 @@ public class TypeAdapterConfig #region ConcurrencyMod [AdaptIgnore] - public AutoResetEvent ConfigureSync { get; set; } + internal AutoResetEvent ConfigureSync { get; set; } [AdaptIgnore] - public AutoResetEvent ApplySync { get; set; } - - public bool IsConcurrencyEnvironment { get; set; } - public bool IsScanConcurrency { get; set; } + internal AutoResetEvent ApplySync { get; set; } + internal bool IsScanConcurrency { get; set; } #endregion ConcurrencyMod @@ -810,8 +808,6 @@ public IList Scan(params Assembly[] assemblies) public IList ScanConcurrency(params Assembly[] assemblies) { - - IsConcurrencyEnvironment = true; ConfigureSync.WaitOne(-1); IsScanConcurrency = true; @@ -822,7 +818,6 @@ public IList ScanConcurrency(params Assembly[] assemblies) finally { ConfigureSync.Set(); - IsConcurrencyEnvironment = false; } } @@ -980,7 +975,6 @@ public static void NewConfig(Action> cf { var config = TypeAdapterConfig.GlobalSettings; - config.IsConcurrencyEnvironment = true; config.ConfigureSync.WaitOne(-1, false); try diff --git a/src/Mapster/TypeAdapterSetter.cs b/src/Mapster/TypeAdapterSetter.cs index 8fae8263..e08ef312 100644 --- a/src/Mapster/TypeAdapterSetter.cs +++ b/src/Mapster/TypeAdapterSetter.cs @@ -31,23 +31,6 @@ internal static void CheckCompiled(this TSetter setter) where TSetter : throw new InvalidOperationException("TypeAdapter.Adapt was already called, please clone or create new TypeAdapterConfig."); } - public static TSetter FinalizeConfig(this TSetter setter) where TSetter : TypeAdapterSetter - { - try - { - setter.CheckCompiled(); - return setter; - } - finally - { - if (setter.Config.IsConcurrencyEnvironment) - { - setter.Config.ConfigureSync.Set(); - } - - } - } - public static TSetter AddDestinationTransform(this TSetter setter, Expression> transform) where TSetter : TypeAdapterSetter { setter.CheckCompiled(); From a05b56cb5da813a4d21c05ce32a35eb0df7daefb Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 6 Nov 2025 09:35:10 +0500 Subject: [PATCH 8/9] Refactoring calls sync --- src/Mapster/TypeAdapterConfig.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index 1551f1b5..80d97eba 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -337,10 +337,10 @@ public Func GetMapFunction() } internal Delegate GetMapFunction(Type sourceType, Type destinationType) { - ConfigureSync.WaitOne(-1); + ConfigureSync.WaitOne(); if (IsScanConcurrency) - ApplySync.WaitOne(-1); + ApplySync.WaitOne(); try { var key = new TypeTuple(sourceType, destinationType); @@ -362,10 +362,10 @@ public Func GetMapToTargetFunction> GetProjectionExpression _dynamicMapDict = new ConcurrentDictionary(); public Func GetDynamicMapFunction(Type sourceType) { - ConfigureSync.WaitOne(-1); + ConfigureSync.WaitOne(); if (IsScanConcurrency) ApplySync.WaitOne(-1); @@ -808,7 +808,7 @@ public IList Scan(params Assembly[] assemblies) public IList ScanConcurrency(params Assembly[] assemblies) { - ConfigureSync.WaitOne(-1); + ConfigureSync.WaitOne(); IsScanConcurrency = true; try @@ -837,7 +837,7 @@ public void Apply(IEnumerable> registers) /// collection of IRegister interface to apply mapping. public void Apply(IEnumerable registers) { - ApplySync.WaitOne(-1, false); + ApplySync.WaitOne(); foreach (IRegister register in registers) { @@ -975,7 +975,7 @@ public static void NewConfig(Action> cf { var config = TypeAdapterConfig.GlobalSettings; - config.ConfigureSync.WaitOne(-1, false); + config.ConfigureSync.WaitOne(); try { From 3383e05d9cb2b5b1f4a42982721a8ca80fd78c26 Mon Sep 17 00:00:00 2001 From: DocSvartz Date: Thu, 6 Nov 2025 10:04:25 +0500 Subject: [PATCH 9/9] drop ApplySync --- src/Mapster/TypeAdapterConfig.cs | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/src/Mapster/TypeAdapterConfig.cs b/src/Mapster/TypeAdapterConfig.cs index 80d97eba..f865c3d8 100644 --- a/src/Mapster/TypeAdapterConfig.cs +++ b/src/Mapster/TypeAdapterConfig.cs @@ -19,10 +19,6 @@ public class TypeAdapterConfig [AdaptIgnore] internal AutoResetEvent ConfigureSync { get; set; } - [AdaptIgnore] - internal AutoResetEvent ApplySync { get; set; } - internal bool IsScanConcurrency { get; set; } - #endregion ConcurrencyMod public static List RulesTemplate { get; } = CreateRuleTemplate(); @@ -108,8 +104,7 @@ private static List CreateRuleTemplate() public TypeAdapterConfig() { ConfigureSync = new(true); - ApplySync = new(true); - + Rules = RulesTemplate.ToList(); var settings = new TypeAdapterSettings(); Default = new TypeAdapterSetter(settings, this); @@ -339,8 +334,6 @@ internal Delegate GetMapFunction(Type sourceType, Type destinationType) { ConfigureSync.WaitOne(); - if (IsScanConcurrency) - ApplySync.WaitOne(); try { var key = new TypeTuple(sourceType, destinationType); @@ -350,7 +343,6 @@ internal Delegate GetMapFunction(Type sourceType, Type destinationType) } finally { - ApplySync.Set(); ConfigureSync.Set(); } } @@ -364,9 +356,6 @@ internal Delegate GetMapToTargetFunction(Type sourceType, Type destinationType) { ConfigureSync.WaitOne(); - if (IsScanConcurrency) - ApplySync.WaitOne(); - try { var key = new TypeTuple(sourceType, destinationType); @@ -376,7 +365,6 @@ internal Delegate GetMapToTargetFunction(Type sourceType, Type destinationType) } finally { - ApplySync.Set(); ConfigureSync.Set(); } @@ -393,9 +381,6 @@ internal MethodCallExpression GetProjectionCallExpression(Type sourceType, Type { ConfigureSync.WaitOne(); - if (IsScanConcurrency) - ApplySync.WaitOne(); - try { var key = new TypeTuple(sourceType, destinationType); @@ -405,7 +390,6 @@ internal MethodCallExpression GetProjectionCallExpression(Type sourceType, Type } finally { - ApplySync.Set(); ConfigureSync.Set(); } } @@ -415,9 +399,6 @@ public Func GetDynamicMapFunction(Type sourc { ConfigureSync.WaitOne(); - if (IsScanConcurrency) - ApplySync.WaitOne(-1); - try { var key = new TypeTuple(sourceType, typeof(TDestination)); @@ -427,7 +408,6 @@ public Func GetDynamicMapFunction(Type sourc } finally { - ApplySync.Set(); ConfigureSync.Set(); } } @@ -809,8 +789,7 @@ public IList Scan(params Assembly[] assemblies) public IList ScanConcurrency(params Assembly[] assemblies) { ConfigureSync.WaitOne(); - IsScanConcurrency = true; - + try { return Scan(assemblies); @@ -837,14 +816,10 @@ public void Apply(IEnumerable> registers) /// collection of IRegister interface to apply mapping. public void Apply(IEnumerable registers) { - ApplySync.WaitOne(); - foreach (IRegister register in registers) { register.Register(this); } - - ApplySync.Set(); }