fix(DSM): Fix race condition on DataStreamsWriter disposal#7968
Conversation
| return; | ||
| } | ||
|
|
||
| var allTasks = Task.WhenAll(_processTask, _flushTask); |
There was a problem hiding this comment.
Flush task will return after processTask returns (which returns because we set processExit above). So there will be nothing using the semaphore after this.
Hence, we are safe to dispose the semaphore on L163 after this method returns because no task will be using it.
Execution-Time Benchmarks Report ⏱️Execution-time results for samples comparing This PR (7968) and master. ✅ No regressions detected - check the details below Full Metrics ComparisonFakeDbCommand
HttpMessageHandler
Comparison explanationExecution-time benchmarks measure the whole time it takes to execute a program, and are intended to measure the one-off costs. Cases where the execution time results for the PR are worse than latest master results are highlighted in **red**. The following thresholds were used for comparing the execution times:
Note that these results are based on a single point-in-time result for each branch. For full results, see the dashboard. Graphs show the p99 interval based on the mean and StdDev of the test run, as well as the mean value of the run (shown as a diamond below the graph). Duration chartsFakeDbCommand (.NET Framework 4.8)gantt
title Execution time (ms) FakeDbCommand (.NET Framework 4.8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (7968) - mean (74ms) : 71, 76
master - mean (74ms) : 72, 76
section Bailout
This PR (7968) - mean (78ms) : 76, 79
master - mean (78ms) : 76, 79
section CallTarget+Inlining+NGEN
This PR (7968) - mean (1,054ms) : 987, 1120
master - mean (1,056ms) : 988, 1125
FakeDbCommand (.NET Core 3.1)gantt
title Execution time (ms) FakeDbCommand (.NET Core 3.1)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (7968) - mean (114ms) : 110, 117
master - mean (114ms) : 111, 118
section Bailout
This PR (7968) - mean (116ms) : 113, 119
master - mean (117ms) : 113, 121
section CallTarget+Inlining+NGEN
This PR (7968) - mean (752ms) : 704, 799
master - mean (744ms) : 715, 772
FakeDbCommand (.NET 6)gantt
title Execution time (ms) FakeDbCommand (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (7968) - mean (101ms) : 98, 104
master - mean (100ms) : 97, 104
section Bailout
This PR (7968) - mean (101ms) : 100, 103
master - mean (102ms) : 99, 104
section CallTarget+Inlining+NGEN
This PR (7968) - mean (691ms) : 671, 711
master - mean (692ms) : 669, 714
FakeDbCommand (.NET 8)gantt
title Execution time (ms) FakeDbCommand (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (7968) - mean (101ms) : 98, 103
master - mean (100ms) : 96, 103
section Bailout
This PR (7968) - mean (102ms) : 99, 105
master - mean (100ms) : 98, 103
section CallTarget+Inlining+NGEN
This PR (7968) - mean (660ms) : 637, 683
master - mean (661ms) : 636, 685
HttpMessageHandler (.NET Framework 4.8)gantt
title Execution time (ms) HttpMessageHandler (.NET Framework 4.8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (7968) - mean (195ms) : 190, 199
master - mean (194ms) : 189, 199
section Bailout
This PR (7968) - mean (198ms) : 194, 201
master - mean (197ms) : 194, 201
section CallTarget+Inlining+NGEN
This PR (7968) - mean (1,132ms) : 1055, 1209
master - mean (1,126ms) : 1058, 1194
HttpMessageHandler (.NET Core 3.1)gantt
title Execution time (ms) HttpMessageHandler (.NET Core 3.1)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (7968) - mean (278ms) : 272, 284
master - mean (277ms) : 271, 283
section Bailout
This PR (7968) - mean (277ms) : 273, 282
master - mean (278ms) : 273, 284
section CallTarget+Inlining+NGEN
This PR (7968) - mean (909ms) : 856, 962
master - mean (908ms) : 860, 956
HttpMessageHandler (.NET 6)gantt
title Execution time (ms) HttpMessageHandler (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (7968) - mean (270ms) : 266, 275
master - mean (271ms) : 266, 276
section Bailout
This PR (7968) - mean (270ms) : 266, 273
master - mean (270ms) : 267, 274
section CallTarget+Inlining+NGEN
This PR (7968) - mean (881ms) : 847, 914
master - mean (887ms) : 841, 934
HttpMessageHandler (.NET 8)gantt
title Execution time (ms) HttpMessageHandler (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (7968) - mean (269ms) : 263, 276
master - mean (271ms) : 262, 280
section Bailout
This PR (7968) - mean (270ms) : 267, 273
master - mean (270ms) : 266, 274
section CallTarget+Inlining+NGEN
This PR (7968) - mean (825ms) : 806, 843
master - mean (830ms) : 809, 851
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Summary of changes
Fixes a race condition where we attempt to dispose of a semaphore when it is being held by another task.
Reason for change
I think this is the root cause behind some unit test flake, and some errors when disposing DataStreamsWriter.
Implementation details
There are two tasks that run:
flushTaskandprocessTask.DataStreamsWriter::DisposeAsynccalls_flushSemaphore.Dispose();afterFlushAndCloseAsync().FlushAndCloseAsyncsets an exit flag for the task, but only waits forprocessTaskto complete. IfflushTaskis running and has already acquired the semaphore, theDisposeAsyncwill still dispose of the semaphore even though flushTask is still using it.Fixed by waiting for both tasks to complete, as flushTask will finish once processTask finishes. And we still have the 1 second fallback.
This should also fix the test flake. The unit test immediately calls dispose after adding the data points:
So it seems likely that we might dispose the semaphore before flush has a chance to run.
Test coverage
Other details