Skip to content

Commit

Permalink
Merge pull request #87 from joaomatossilva/fix/thread-safety
Browse files Browse the repository at this point in the history
Fixed thread safety issues
  • Loading branch information
joaomatossilva authored Dec 10, 2018
2 parents ebed236 + b2619df commit f0b0c29
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 38 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
Changelog
===================
v5.3.0
- Fixed Thread safety issues.

v5.2.0
- Added es-CO culture, thanks to @canro91

Expand Down
21 changes: 21 additions & 0 deletions src/DateTimeExtensions/Common/ConcurrentLazyDictionary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.Collections.Concurrent;

namespace DateTimeExtensions.Common
{
internal class ConcurrentLazyDictionary<TKey,TValue>
{
private ConcurrentDictionary<TKey, Lazy<TValue>> innerDictionary;

public ConcurrentLazyDictionary()
{
innerDictionary = new ConcurrentDictionary<TKey, Lazy<TValue>>();
}

public TValue GetOrAdd(TKey key, Func<TValue> valueFactory)
{
var lazyValue = innerDictionary.GetOrAdd(key, new Lazy<TValue>(valueFactory));
return lazyValue.Value;
}
}
}
6 changes: 3 additions & 3 deletions src/DateTimeExtensions/DateTimeExtensions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<VersionPrefix>5.2.0</VersionPrefix>
<AssemblyVersion>5.2.0.0</AssemblyVersion>
<FileVersion>5.2.0.0</FileVersion>
<VersionPrefix>5.3.0</VersionPrefix>
<AssemblyVersion>5.3.0.0</AssemblyVersion>
<FileVersion>5.3.0.0</FileVersion>
<PackageIconUrl>https://github.com/joaomatossilva/DateTimeExtensions/raw/master/assets/datetimeextensions-60-logo.png</PackageIconUrl>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#endregion

using DateTimeExtensions.Common;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -29,26 +30,18 @@ public abstract class HolidayStrategyBase : IHolidayStrategy
{
protected readonly IList<Holiday> InnerHolidays;

private readonly IDictionary<int, IDictionary<DateTime, Holiday>> holidaysObservancesCache;
private readonly ConcurrentLazyDictionary<int, IDictionary<DateTime, Holiday>> holidaysObservancesCache;

protected HolidayStrategyBase()
{
holidaysObservancesCache = new Dictionary<int, IDictionary<DateTime, Holiday>>();
holidaysObservancesCache = new ConcurrentLazyDictionary<int, IDictionary<DateTime, Holiday>>();
this.InnerHolidays = new List<Holiday>();
}

public bool IsHoliDay(DateTime day)
{
this.CheckYearHasMap(day.Year);
return holidaysObservancesCache[day.Year].Any(m => m.Key.Date == day.Date);
}

private void CheckYearHasMap(int year)
{
if (!holidaysObservancesCache.ContainsKey(year))
{
holidaysObservancesCache.Add(year, this.BuildObservancesMap(year));
}
var map = holidaysObservancesCache.GetOrAdd(day.Year, () => BuildObservancesMap(day.Year));
return map.Any(m => m.Key.Date == day.Date);
}

protected virtual IDictionary<DateTime, Holiday> BuildObservancesMap(int year)
Expand All @@ -66,8 +59,8 @@ public virtual IEnumerable<Holiday> Holidays

public virtual IEnumerable<Holiday> GetHolidaysOfYear(int year)
{
this.CheckYearHasMap(year);
return holidaysObservancesCache[year].Select(m => m.Value);
var map = holidaysObservancesCache.GetOrAdd(year, () => BuildObservancesMap(year));
return map.Select(m => m.Value);
}
}
}
16 changes: 4 additions & 12 deletions src/DateTimeExtensions/WorkingDays/NthDayOfWeekAfterDayHoliday.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@

#endregion

using DateTimeExtensions.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DateTimeExtensions.WorkingDays
{
Expand All @@ -30,7 +28,7 @@ public class NthDayOfWeekAfterDayHoliday : Holiday
private readonly DayOfWeek dayOfWeek;
private readonly int count;
private readonly Holiday baseHoliday;
private readonly IDictionary<int, DateTime?> dayCache;
private readonly ConcurrentLazyDictionary<int, DateTime?> dayCache;

public NthDayOfWeekAfterDayHoliday(string name, int count, DayOfWeek dayOfWeek, int month, int day)
: this(name, count, dayOfWeek, new FixedHoliday(name, month, day))
Expand All @@ -56,18 +54,12 @@ public NthDayOfWeekAfterDayHoliday(string name, int count, DayOfWeek dayOfWeek,
this.count = count;
this.dayOfWeek = dayOfWeek;
this.baseHoliday = baseHoliday;
dayCache = new Dictionary<int, DateTime?>();
dayCache = new ConcurrentLazyDictionary<int, DateTime?>();
}

public override DateTime? GetInstance(int year)
{
if (dayCache.ContainsKey(year))
{
return dayCache[year];
}
var day = CalculateDayInYear(year);
dayCache.Add(year, day);
return day;
return dayCache.GetOrAdd(year, () => CalculateDayInYear(year));
}

public override bool IsInstanceOf(DateTime date)
Expand Down
13 changes: 4 additions & 9 deletions src/DateTimeExtensions/WorkingDays/NthDayOfWeekInMonthHoliday.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#endregion

using DateTimeExtensions.Common;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -37,7 +38,7 @@ public class NthDayOfWeekInMonthHoliday : Holiday
private DayOfWeek dayOfWeek;
private CountDirection direction;
private int month;
private IDictionary<int, DateTime> dayCache;
private ConcurrentLazyDictionary<int, DateTime> dayCache;

public NthDayOfWeekInMonthHoliday(string name, int count, DayOfWeek dayOfWeek, int month,
CountDirection direction)
Expand All @@ -47,18 +48,12 @@ public NthDayOfWeekInMonthHoliday(string name, int count, DayOfWeek dayOfWeek, i
this.dayOfWeek = dayOfWeek;
this.month = month;
this.direction = direction;
dayCache = new Dictionary<int, DateTime>();
dayCache = new ConcurrentLazyDictionary<int, DateTime>();
}

public override DateTime? GetInstance(int year)
{
if (dayCache.ContainsKey(year))
{
return dayCache[year];
}
var day = CalculateDayInYear(year);
dayCache.Add(year, day);
return day;
return dayCache.GetOrAdd(year, () => CalculateDayInYear(year));
}

public override bool IsInstanceOf(DateTime date)
Expand Down
25 changes: 25 additions & 0 deletions tests/DateTimeExtensions.Tests/ThreadSafeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using DateTimeExtensions.WorkingDays;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DateTimeExtensions.Tests
{
[TestFixture]
public class ThreadSafeTests
{
[Test]
public void AddWorkingDays_MultipleThreads_CanCalculate()
{
//Arrange
var culture = new WorkingDayCultureInfo("en-US");
var startDate = new DateTime(2018,5,1);

//Act
Parallel.ForEach(Enumerable.Range(1,10), (i) => startDate.AddWorkingDays(i, culture));
}
}
}

0 comments on commit f0b0c29

Please sign in to comment.