Skip to content

Commit

Permalink
Logger - Properties added using WithProperty can now be inspected safely
Browse files Browse the repository at this point in the history
  • Loading branch information
snakefoot committed May 25, 2019
1 parent df0c1b6 commit ee168fb
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 33 deletions.
7 changes: 2 additions & 5 deletions src/NLog/Config/LoggingConfiguration.cs
Expand Up @@ -61,7 +61,7 @@ public class LoggingConfiguration
/// <summary>
/// Variables defined in xml or in API. name is case case insensitive.
/// </summary>
private readonly Dictionary<string, SimpleLayout> _variables = new Dictionary<string, SimpleLayout>(StringComparer.OrdinalIgnoreCase);
private readonly ThreadSafeDictionary<string, SimpleLayout> _variables = new ThreadSafeDictionary<string, SimpleLayout>(StringComparer.OrdinalIgnoreCase);

/// <summary>
/// Gets the factory that will be configured
Expand Down Expand Up @@ -812,10 +812,7 @@ private List<ISupportsInitialize> GetSupportsInitializes(bool reverse = false)
/// <param name="masterVariables">Master variables dictionary</param>
internal void CopyVariables(IDictionary<string, SimpleLayout> masterVariables)
{
foreach (var variable in masterVariables)
{
Variables[variable.Key] = variable.Value;
}
_variables.CopyFrom(masterVariables);
}

/// <summary>
Expand Down
9 changes: 5 additions & 4 deletions src/NLog/GlobalDiagnosticsContext.cs
Expand Up @@ -43,6 +43,7 @@ namespace NLog
/// </summary>
public static class GlobalDiagnosticsContext
{
private static readonly object _lockObject = new object();
private static Dictionary<string, object> _dict = new Dictionary<string, object>();
private static Dictionary<string, object> _dictReadOnly; // Reset cache on change

Expand All @@ -63,7 +64,7 @@ public static void Set(string item, string value)
/// <param name="value">Item value.</param>
public static void Set(string item, object value)
{
lock (_dict)
lock (_lockObject)
{
bool requireCopyOnWrite = _dictReadOnly != null && !_dict.ContainsKey(item); // Overwiting existing value is ok (no resize)
GetWritableDict(requireCopyOnWrite)[item] = value;
Expand Down Expand Up @@ -129,7 +130,7 @@ public static bool Contains(string item)
/// <param name="item">Item name.</param>
public static void Remove(string item)
{
lock (_dict)
lock (_lockObject)
{
bool requireCopyOnWrite = _dictReadOnly != null && _dict.ContainsKey(item);
GetWritableDict(requireCopyOnWrite).Remove(item);
Expand All @@ -141,7 +142,7 @@ public static void Remove(string item)
/// </summary>
public static void Clear()
{
lock (_dict)
lock (_lockObject)
{
bool requireCopyOnWrite = _dictReadOnly != null && _dict.Count > 0;
GetWritableDict(requireCopyOnWrite, true).Clear();
Expand All @@ -153,7 +154,7 @@ public static void Clear()
var readOnly = _dictReadOnly;
if (readOnly == null)
{
lock (_dict)
lock (_lockObject)
{
readOnly = _dictReadOnly = _dict;
}
Expand Down
233 changes: 233 additions & 0 deletions src/NLog/Internal/ThreadSafeDictionary.cs
@@ -0,0 +1,233 @@
//
// Copyright (c) 2004-2019 Jaroslaw Kowalski <jaak@jkowalski.net>, Kim Christensen, Julian Verdurmen
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * Neither the name of Jaroslaw Kowalski nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//

namespace NLog.Internal
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

internal class ThreadSafeDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly object _lockObject = new object();
private readonly IEqualityComparer<TKey> _comparer;
private Dictionary<TKey, TValue> _dict;
private Dictionary<TKey, TValue> _dictReadOnly; // Reset cache on change

public ThreadSafeDictionary()
:this(EqualityComparer<TKey>.Default)
{
}

public ThreadSafeDictionary(IEqualityComparer<TKey> comparer)
{
_comparer = comparer;
_dict = new Dictionary<TKey, TValue>(_comparer);
}

public ThreadSafeDictionary(ThreadSafeDictionary<TKey, TValue> source)
{
_comparer = source._comparer;
_dict = source.GetReadOnlyDict();
GetWritableDict(); // Clone
}

public TValue this[TKey key]
{
get { return GetReadOnlyDict()[key]; }
set
{
lock (_lockObject)
{
GetWritableDict()[key] = value;
}
}
}

public ICollection<TKey> Keys => GetReadOnlyDict().Keys;

public ICollection<TValue> Values => GetReadOnlyDict().Values;

public int Count => GetReadOnlyDict().Count;

public bool IsReadOnly => false;

public void Add(TKey key, TValue value)
{
lock (_lockObject)
{
GetWritableDict().Add(key, value);
}
}

public void Add(KeyValuePair<TKey, TValue> item)
{
lock (_lockObject)
{
GetWritableDict().Add(item.Key, item.Value);
}
}

public void Clear()
{
lock (_lockObject)
{
GetWritableDict(true);
}
}

public bool Contains(KeyValuePair<TKey, TValue> item)
{
return GetReadOnlyDict().Contains(item);
}

public bool ContainsKey(TKey key)
{
return GetReadOnlyDict().ContainsKey(key);
}

public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
((IDictionary<TKey,TValue>)GetReadOnlyDict()).CopyTo(array, arrayIndex);
}

public void CopyFrom(IDictionary<TKey, TValue> source)
{
if (!ReferenceEquals(this, source))
{
if (source?.Count > 0)
{
lock (_lockObject)
{
var destDict = GetWritableDict();
foreach (var item in source)
destDict[item.Key] = item.Value;
}
}
}
}

public bool Remove(TKey key)
{
lock (_lockObject)
{
return GetWritableDict().Remove(key);
}
}

public bool Remove(KeyValuePair<TKey, TValue> item)
{
lock (_lockObject)
{
return GetWritableDict().Remove(item);
}
}

public bool TryGetValue(TKey key, out TValue value)
{
return GetReadOnlyDict().TryGetValue(key, out value);
}

IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return GetReadOnlyDict().GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetReadOnlyDict().GetEnumerator();
}

public Enumerator GetEnumerator()
{
return new Enumerator(GetReadOnlyDict().GetEnumerator());
}

public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
{
Dictionary<TKey, TValue>.Enumerator _enumerator;

public Enumerator(Dictionary<TKey, TValue>.Enumerator enumerator)
{
_enumerator = enumerator;
}

public KeyValuePair<TKey, TValue> Current => _enumerator.Current;

object IEnumerator.Current => _enumerator.Current;

public void Dispose()
{
_enumerator.Dispose();
}

public bool MoveNext()
{
return _enumerator.MoveNext();
}

void IEnumerator.Reset()
{
((IEnumerator)_enumerator).Reset();
}
}

private Dictionary<TKey, TValue> GetReadOnlyDict()
{
var readOnly = _dictReadOnly;
if (readOnly == null)
{
lock (_lockObject)
{
readOnly = _dictReadOnly = _dict;
}
}
return readOnly;
}

private IDictionary<TKey, TValue> GetWritableDict(bool clearDictionary = false)
{
var newDict = new Dictionary<TKey, TValue>(clearDictionary ? 0 : _dict.Count + 1, _comparer);
if (!clearDictionary)
{
// Less allocation with enumerator than Dictionary-constructor
foreach (var item in _dict)
newDict[item.Key] = item.Value;
}
_dict = newDict;
_dictReadOnly = null;
return newDict;
}
}
}

0 comments on commit ee168fb

Please sign in to comment.