prometheus-net
140 строк · 4.3 Кб
1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3
4using System.Diagnostics;
5
6namespace Prometheus;
7
8// Copy-pasted from https://github.com/dotnet/efcore/blob/main/src/Shared/NonCapturingLazyInitializer.cs
9// Crudely modified to inline dependencies and reduce functionality down to .NET Fx compatible level.
10internal static class NonCapturingLazyInitializer
11{
12public static TValue EnsureInitialized<TParam, TValue>(
13ref TValue? target,
14TParam param,
15Func<TParam, TValue> valueFactory)
16where TValue : class
17{
18var tmp = Volatile.Read(ref target);
19if (tmp != null)
20{
21DebugAssert(target != null, $"target was null in {nameof(EnsureInitialized)} after check");
22return tmp;
23}
24
25Interlocked.CompareExchange(ref target, valueFactory(param), null);
26
27return target;
28}
29
30public static TValue EnsureInitialized<TParam1, TParam2, TValue>(
31ref TValue? target,
32TParam1 param1,
33TParam2 param2,
34Func<TParam1, TParam2, TValue> valueFactory)
35where TValue : class
36{
37var tmp = Volatile.Read(ref target);
38if (tmp != null)
39{
40DebugAssert(target != null, $"target was null in {nameof(EnsureInitialized)} after check");
41return tmp;
42}
43
44Interlocked.CompareExchange(ref target, valueFactory(param1, param2), null);
45
46return target;
47}
48
49public static TValue EnsureInitialized<TParam1, TParam2, TParam3, TValue>(
50ref TValue? target,
51TParam1 param1,
52TParam2 param2,
53TParam3 param3,
54Func<TParam1, TParam2, TParam3, TValue> valueFactory)
55where TValue : class
56{
57var tmp = Volatile.Read(ref target);
58if (tmp != null)
59{
60DebugAssert(target != null, $"target was null in {nameof(EnsureInitialized)} after check");
61return tmp;
62}
63
64Interlocked.CompareExchange(ref target, valueFactory(param1, param2, param3), null);
65
66return target;
67}
68
69public static TValue EnsureInitialized<TParam, TValue>(
70ref TValue target,
71ref bool initialized,
72TParam param,
73Func<TParam, TValue> valueFactory)
74where TValue : class?
75{
76var alreadyInitialized = Volatile.Read(ref initialized);
77if (alreadyInitialized)
78{
79var value = Volatile.Read(ref target);
80DebugAssert(target != null, $"target was null in {nameof(EnsureInitialized)} after check");
81DebugAssert(value != null, $"value was null in {nameof(EnsureInitialized)} after check");
82return value;
83}
84
85Volatile.Write(ref target, valueFactory(param));
86Volatile.Write(ref initialized, true);
87
88return target;
89}
90
91public static TValue EnsureInitialized<TValue>(
92ref TValue? target,
93TValue value)
94where TValue : class
95{
96var tmp = Volatile.Read(ref target);
97if (tmp != null)
98{
99DebugAssert(target != null, $"target was null in {nameof(EnsureInitialized)} after check");
100return tmp;
101}
102
103Interlocked.CompareExchange(ref target, value, null);
104
105return target;
106}
107
108public static TValue EnsureInitialized<TParam, TValue>(
109ref TValue? target,
110TParam param,
111Action<TParam> valueFactory)
112where TValue : class
113{
114var tmp = Volatile.Read(ref target);
115if (tmp != null)
116{
117DebugAssert(target != null, $"target was null in {nameof(EnsureInitialized)} after check");
118return tmp;
119}
120
121valueFactory(param);
122
123var tmp2 = Volatile.Read(ref target);
124DebugAssert(
125target != null && tmp2 != null,
126$"{nameof(valueFactory)} did not initialize {nameof(target)} in {nameof(EnsureInitialized)}");
127#pragma warning disable CS8603 // Possible null reference return.
128return tmp2;
129#pragma warning restore CS8603 // Possible null reference return.
130}
131
132[Conditional("DEBUG")]
133private static void DebugAssert(bool condition, string message)
134{
135if (!condition)
136{
137throw new Exception($"Check.DebugAssert failed: {message}");
138}
139}
140}
141