Skip to content
This repository was archived by the owner on Dec 18, 2023. It is now read-only.

Commit 4241320

Browse files
ndrwrbgsSergeyKanzhelev
authored andcommitted
Demonstrate concurrency bug in CurrentStatsState. (#29)
* Demonstrate concurrency bug in CurrentStatsState. In reality, we probably want a ReaderWriterLock for less contention though * Undo
1 parent e4e4c85 commit 4241320

2 files changed

Lines changed: 68 additions & 11 deletions

File tree

src/OpenCensus/Impl/Stats/CurrentStatsState.cs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,22 @@ internal StatsCollectionState Internal
5252
// otherwise.
5353
internal bool Set(StatsCollectionState state)
5454
{
55-
if (this.isRead)
55+
lock (this.lck)
5656
{
57-
throw new ArgumentException("State was already read, cannot set state.");
58-
}
57+
if (this.isRead)
58+
{
59+
throw new ArgumentException("State was already read, cannot set state.");
60+
}
5961

60-
if (state == this.currentState)
61-
{
62-
return false;
63-
}
64-
else
65-
{
66-
this.currentState = state;
67-
return true;
62+
if (state == this.currentState)
63+
{
64+
return false;
65+
}
66+
else
67+
{
68+
this.currentState = state;
69+
return true;
70+
}
6871
}
6972
}
7073
}

test/OpenCensus.Tests/Impl/Stats/CurrentStatsStateTest.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
namespace OpenCensus.Stats.Test
1818
{
1919
using System;
20+
using System.Linq;
21+
using System.Threading;
22+
using System.Threading.Tasks;
2023
using Xunit;
2124

2225
public class CurrentStatsStateTest
@@ -47,5 +50,56 @@ public void PreventSettingStateAfterReadingState()
4750
var st = state.Value;
4851
Assert.Throws<ArgumentException>(() => state.Set(StatsCollectionState.DISABLED));
4952
}
53+
54+
/// <summary>
55+
/// This test relies on timing, and as such may not FAIL reliably under some conditions
56+
/// (e.g. more/less machine load, faster/slower processors).
57+
/// It will not incorrectly fail transiently though.
58+
/// </summary>
59+
[Fact]
60+
61+
public async Task PreventSettingStateAfterReadingState_IsThreadSafe()
62+
{
63+
for (int i = 0; i < 10; i ++)
64+
{
65+
var state = new CurrentStatsState();
66+
67+
using (var cts = new CancellationTokenSource())
68+
{
69+
var _ = Task.Run(
70+
async () =>
71+
{
72+
while (!cts.IsCancellationRequested)
73+
{
74+
try
75+
{
76+
state.Set(StatsCollectionState.DISABLED);
77+
state.Set(StatsCollectionState.ENABLED);
78+
}
79+
catch
80+
{
81+
// Throw is expected after the read is performed
82+
}
83+
}
84+
},
85+
cts.Token);
86+
87+
await Task.Delay(10);
88+
89+
// Read the value a bunch of times
90+
var values = Enumerable.Range(0, 20)
91+
.Select(__ => state.Value)
92+
.ToList();
93+
94+
// They should all be the same
95+
foreach (var item in values)
96+
{
97+
Assert.Equal(item, values[0]);
98+
}
99+
100+
cts.Cancel();
101+
}
102+
}
103+
}
50104
}
51105
}

0 commit comments

Comments
 (0)