From 50292e77590951e6cbb3b0c02051a847dca0d9f5 Mon Sep 17 00:00:00 2001 From: Oski Kervinen Date: Wed, 25 Sep 2024 18:20:26 +0300 Subject: [PATCH] Fixed: SymWriter COM object is not released on exception (#955) * Fixed: SymWriter COM object is not released on exception The Problem: Problem observed when being used in coverlet: https://github.com/coverlet-coverage/coverlet/issues/1471 If an exception caused `NativePdbWriter.Write` to never be called, it would not call `SymWriter.Close`, which in turn meant `Marshal.ReleaseComObject` was left uncalled. The garbage collector will eventually destroy the object and thereby release the COM object, but that happens non-deterministically. The COM object is holding to a file handle, which prevents other operations on the written file until it is released. The result was random file access issues. The Solution: Luckily NativePdbWriter is IDisposable. I added a call to `writer.Close` there. But now it could be called twice, so I had to add a boolean to SymWriter to avoid releasing everything twice. * Code style --------- Co-authored-by: Jb Evain --- symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs | 1 + symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs b/symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs index 7bb9c6f15..9597d7fa4 100644 --- a/symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs +++ b/symbols/pdb/Mono.Cecil.Pdb/NativePdbWriter.cs @@ -267,6 +267,7 @@ public void Write () public void Dispose () { + writer.Close (); } } diff --git a/symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs b/symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs index f309f2f6c..fcc92611c 100644 --- a/symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs +++ b/symbols/pdb/Mono.Cecil.Pdb/SymWriter.cs @@ -30,6 +30,7 @@ static extern int CoCreateInstance ( readonly ISymUnmanagedWriter2 writer; readonly Collection documents; + bool closed = false; public SymWriter () { @@ -78,6 +79,10 @@ public void DefineConstant2 (string name, object value, int sigToken) public void Close () { + if (closed) + return; + + closed = true; writer.Close (); Marshal.ReleaseComObject (writer);