-
Notifications
You must be signed in to change notification settings - Fork 35
/
InterlockedOperations.tt
132 lines (123 loc) · 5.25 KB
/
InterlockedOperations.tt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ include file="$(SolutionDir)Build\T4\Autogenerated.ttinclude" #>
using System;
using System.Threading;
using JetBrains.Annotations;
namespace CodeJam.Threading
{
/// <summary>
/// Provides a helper class for initializing a values in a thread-safe manner.
/// </summary>
[PublicAPI]
public static class InterlockedOperations
{
<#
var types = new[] { "double", "float", "int", "long" };
foreach (var typeName in types)
{
WriteRegionLine(typeName + " overloads");
EmitOverloadsFor(typeName, false);
}
WriteRegionLine("Generic overloads");
EmitOverloadsFor("T", true);
FlushRegions();
#>
}
}<#+
private void EmitOverloadsFor(string typeName, bool forRefTypes)
{
var t = forRefTypes ? typeName + "?" : typeName;
var tXmlDoc = forRefTypes?
"\r\n\t\t/// <typeparam name=\"" + typeName + "\">Type of value.</typeparam>" :
"";
var tXmlT = forRefTypes? "{" + typeName + "}" : "";
var tT = forRefTypes? "<" + typeName + ">" : "";
var tWhere = forRefTypes? "\r\n\t\t\twhere " + typeName + " : class?" : "";
#>
/// <summary>
/// Initialize the value referenced by <paramref name="target"/> in a thread-safe manner.
/// The value is changed to <paramref name="value"/> only if the current value is default(<#=t#>).
/// </summary><#=tXmlDoc#>
/// <param name="target">Reference to the target location.</param>
/// <param name="value">The value to use if the target is equal to default(<#=t#>).</param>
/// <returns>
/// The new value referenced by <paramref name="target"/>.
/// Note that this is nearly always more useful than the usual
/// return from <see cref="Interlocked.CompareExchange<#=tXmlT#>(ref <#=typeName#>, <#=typeName#>, <#=typeName#>)"/>
/// because it saves another read to <paramref name="target"/>.
/// </returns>
public static <#=t#> Initialize<#=tT#>(ref <#=t#> target, <#=t#> value)<#=tWhere#> =>
Initialize(ref target, value, default(<#=t#>));
/// <summary>
/// Initialize the value referenced by <paramref name="target"/> in a thread-safe manner.
/// The value is changed to <paramref name="initializedValue"/> only if the current value
/// is <paramref name="uninitializedValue"/>.
/// </summary><#=tXmlDoc#>
/// <param name="target">Reference to the target location.</param>
/// <param name="initializedValue">The value to use if the target is currently uninitialized.</param>
/// <param name="uninitializedValue">The uninitialized value.</param>
/// <returns>
/// The new value referenced by <paramref name="target"/>.
/// Note that this is nearly always more useful than the usual
/// return from <see cref="Interlocked.CompareExchange<#=tXmlT#>(ref <#=typeName#>, <#=typeName#>, <#=typeName#>)"/>
/// because it saves another read to <paramref name="target"/>.
/// </returns>
public static <#=t#> Initialize<#=tT#>(ref <#=t#> target, <#=t#> initializedValue, <#=t#> uninitializedValue)<#=tWhere#>
{
DebugCode.AssertArgument(
initializedValue != uninitializedValue,
nameof(initializedValue),
"The values of uninitializedValue and initializedValue should not match");
var oldValue = Interlocked.CompareExchange(ref target, initializedValue, uninitializedValue);
return oldValue == uninitializedValue ? initializedValue : oldValue;
}
/// <summary>
/// Implements lock-free update pattern implementation based on compare-and-swap loop
/// </summary><#=tXmlDoc#>
/// <param name="value">Value to change.</param>
/// <param name="updateCallback">Function to calculate new value.</param>
/// <returns>New value.</returns>
/// <remarks>
/// Uses the same approach that used by c# compiler in event subscription methods
/// </remarks>
// BASEDON: https://github.com/dotnet/roslyn/blob/56f605c41915317ccdb925f66974ee52282609e7/src/Compilers/Core/Portable/InternalUtilities/ThreadSafeFlagOperations.cs
public static <#=t#> Update<#=tT#>(ref <#=t#> value, Func<<#=t#>, <#=t#>> updateCallback)<#=tWhere#>
{
<#=t#> oldState, newState;
do
{
oldState = value;
newState = updateCallback(oldState);
if (newState == oldState)
return newState;
}
while (Interlocked.CompareExchange(ref value, newState, oldState) != oldState);
return newState;
}
/// <summary>
/// Implements lock-free update pattern implementation based on compare-and-swap loop
/// </summary><#=tXmlDoc#>
/// <param name="value">Value to change.</param>
/// <param name="newValue">Precalculated new value.</param>
/// <param name="updateCallback">Function to calculate new value.</param>
/// <returns>New value.</returns>
/// <remarks>
/// Uses the same approach that used by c# compiler in event subscription methods
/// </remarks>
// BASEDON: https://github.com/dotnet/roslyn/blob/56f605c41915317ccdb925f66974ee52282609e7/src/Compilers/Core/Portable/InternalUtilities/ThreadSafeFlagOperations.cs
public static <#=t#> Update<#=tT#>(ref <#=t#> value, <#=t#> newValue, Func<<#=t#>, <#=t#>, <#=t#>> updateCallback)<#=tWhere#>
{
<#=t#> oldState, newState;
do
{
oldState = value;
newState = updateCallback(oldState, newValue);
if (newState == oldState)
return newState;
}
while (Interlocked.CompareExchange(ref value, newState, oldState) != oldState);
return newState;
}
<#+
}
#>