Skip to content

Commit d7e1dc9

Browse files
Modernize concurrency handling in SysCache2
* Fixes #20.
1 parent cb0f83a commit d7e1dc9

File tree

1 file changed

+33
-44
lines changed

1 file changed

+33
-44
lines changed

SysCache2/NHibernate.Caches.SysCache2/SysCacheProvider.cs

Lines changed: 33 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System;
2+
using System.Collections.Concurrent;
13
using System.Collections.Generic;
24
using NHibernate.Cache;
35

@@ -9,17 +11,14 @@ namespace NHibernate.Caches.SysCache2
911
public class SysCacheProvider : ICacheProvider
1012
{
1113
/// <summary>pre configured cache region settings</summary>
12-
private static readonly Dictionary<string, SysCacheRegion> CacheRegions;
14+
private static readonly ConcurrentDictionary<string, Lazy<ICache>> CacheRegions = new ConcurrentDictionary<string, Lazy<ICache>>();
1315

1416
/// <summary>list of pre configured already built cache regions</summary>
15-
private static readonly CacheRegionCollection CacheRegionSettingsList;
17+
private static readonly Dictionary<string, CacheRegionElement> CacheRegionSettings;
1618

1719
/// <summary>log4net logger</summary>
1820
private static readonly IInternalLogger Log;
1921

20-
/// <summary>synchronizing object for the cache regions dictionary</summary>
21-
private static readonly object RegionsSyncRoot = new object();
22-
2322
/// <summary>
2423
/// Initializes the <see cref="SysCacheProvider"/> class.
2524
/// </summary>
@@ -35,12 +34,15 @@ static SysCacheProvider()
3534

3635
if (configSection != null && configSection.CacheRegions.Count > 0)
3736
{
38-
CacheRegionSettingsList = configSection.CacheRegions;
39-
CacheRegions = new Dictionary<string, SysCacheRegion>(CacheRegionSettingsList.Count);
37+
CacheRegionSettings = new Dictionary<string, CacheRegionElement>(configSection.CacheRegions.Count);
38+
foreach (var cacheRegion in configSection.CacheRegions)
39+
{
40+
if (cacheRegion is CacheRegionElement element)
41+
CacheRegionSettings.Add(element.Name, element);
42+
}
4043
}
4144
else
4245
{
43-
CacheRegions = new Dictionary<string, SysCacheRegion>(0);
4446
Log.Info(
4547
"No cache regions specified. Cache regions can be specified in sysCache configuration section with custom settings.");
4648
}
@@ -56,49 +58,36 @@ public ICache BuildCache(string regionName, IDictionary<string, string> properti
5658
// since query caches are not configured at session factory startup. This may also happen
5759
// if many session factories are built.
5860
// This cache avoids to duplicate the configured SQL dependencies registration in above cases.
59-
if (!string.IsNullOrEmpty(regionName) && CacheRegions.TryGetValue(regionName, out var cache))
61+
if (!string.IsNullOrEmpty(regionName)
62+
// We do not cache non-configured caches, so must first look-up settings for knowing if it
63+
// is a configured one.
64+
&& CacheRegionSettings.TryGetValue(regionName, out var regionSettings))
6065
{
61-
return cache;
66+
// The Lazy<T> is required for ensuring the cache is built only once. ConcurrentDictionary
67+
// may run concurrently the value factory for the same key, but it will yield only one
68+
// of the resulting Lazy<T>. The lazy will then actually build the cache when accessing its
69+
// value after having obtained it, and it will not do that concurrently.
70+
// https://stackoverflow.com/a/31637510/1178314
71+
var cache = CacheRegions.GetOrAdd(regionName,
72+
r => new Lazy<ICache>(() => BuildCache(r, properties, regionSettings)));
73+
return cache.Value;
6274
}
6375

64-
// Build the cache from preconfigured values if the region has configuration values
65-
if (CacheRegionSettingsList != null)
66-
{
67-
var regionSettings = regionName == null ? null : CacheRegionSettingsList[regionName];
68-
69-
if (regionSettings != null)
70-
{
71-
SysCacheRegion cacheRegion;
72-
73-
lock (RegionsSyncRoot)
74-
{
75-
// Note that the only reason we have to do this double check is because the query cache
76-
// can try to create caches at unpredictable times.
77-
if (CacheRegions.TryGetValue(regionName, out cacheRegion) == false)
78-
{
79-
if (Log.IsDebugEnabled)
80-
{
81-
Log.DebugFormat("building cache region, '{0}', from configuration", regionName);
82-
}
83-
84-
//build the cache region with settings and put it into the list so that this proces will not occur again
85-
cacheRegion = new SysCacheRegion(regionName, regionSettings, properties);
86-
CacheRegions[regionName] = cacheRegion;
87-
}
88-
}
89-
90-
return cacheRegion;
91-
}
92-
}
76+
// We will end up creating cache regions here for cache regions that NHibernate
77+
// uses internally and cache regions that weren't specified in the application config file
78+
return BuildCache(regionName, properties, null);
79+
}
9380

81+
private ICache BuildCache(string regionName, IDictionary<string, string> properties, CacheRegionElement settings)
82+
{
9483
if (Log.IsDebugEnabled)
9584
{
96-
Log.DebugFormat("building non-configured cache region : {0}", regionName);
85+
Log.DebugFormat(
86+
settings != null
87+
? "building cache region, '{0}', from configuration"
88+
: "building non-configured cache region : {0}", regionName);
9789
}
98-
99-
//we will end up creating cache regions here for cache regions that nhibernate
100-
//uses internally and cache regions that weren't specified in the application config file
101-
return new SysCacheRegion(regionName, properties);
90+
return new SysCacheRegion(regionName, settings, properties);
10291
}
10392

10493
/// <inheritdoc />

0 commit comments

Comments
 (0)