[API Proposal]: Consider implementing IDisposable on Lock.Scope
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 35/100
- Issue type
- Feature
- Clarity
- Clearly specified
- Activity status
- Stale
- Tech stack
- csharp, fsharp
- Domain
- backend-api-design
Research direction
Start by locating the System.Threading.Lock.Scope declaration and reviewing its existing Dispose method. Verify the proposed IDisposable addition against the ref struct API constraints and relevant API compatibility checks; done means F# can use Lock.EnterScope through use without changing Lock itself.
Written by the indexing model from the issue text.
Description
Background and motivation
The Lock.Scope type is (according to the API design review session for Lock) intended to "play nicely with language constructs". But it doesn't work in F# because the use keyword in F# requires types to implement IDisposable. (It works fine in C# because when it comes to a ref struct, C#'s using will accept any method called Dispose whether or not it's part of an IDisposable implementation. But since C# offers intrinsic support for Lock, the fact that you can also use it via using isn't particularly useful.)
Note this is not a request to make Lock itself disposable. This is about Lock.Scope.
Early design proposals for Lock.Scope had it implement IDiposable. As far as I can tell, this interface was dropped when Lock.Scope was turned into a ref struct back in 2023. At the time, ref struct types were not allowed to implement interfaces, so it would have been necessary to remove the IDisposable interface at that time.
However, Lock didn't ship until .NET 9.0, by which time the CLR and C# 13 did allow ref struct types to implement interfaces.
Making Lock.Scope implement IDisposable wouldn't affect any existing users: code already calling Dispose directly today would continue to work. But it would enable F# code to use Lock.Scope through its use statement. (It wouldn't work for F#'s using because that doesn't work with ref struct types. use is happy to use a ref struct though.)
API Proposal
The nested Lock.Scope type exists today, and it is a ref struct that does not implement any interfaces:
namespace System.Threading;
public sealed partial class Lock
{
...
public ref struct Scope
{
...
}
}
The change would be to declare that it implements IDisposable:
namespace System.Threading;
public sealed partial class Lock
{
...
public ref struct Scope : IDisposable
{
...
}
}
Scope already defines a suitable Dispose method, so there is no need to add any new code other than this one line change.
API Usage
This code, which does not work today in F#:
let myLock = new System.Threading.Lock()
let f =
use _ = Lock.EnterScope(myLock)
printfn "Hello from use"
would work after this change.
This is according to the discussion in the original design review of the Lock API, precisely what Lock.Scope is meant for. As Immo said in that meeting:
the only reason Scope exists it to play nicely with language constructs
Today, it fails to achieve this for F#. It succeeds in C# but since C# added intrinsic support for Lock in the same release that Lock first became available, that's not much use. (In the original design review, the .NET team thought there was a high chance that C# would not provide this intrinsic support, at which point the design they came up with would have been more useful because it would have provided C# support that would otherwise have been missing.)
Alternative Designs
The alternative is to leave the API unchanged. The F# team could provide intrinsic support themselves.
Risks
It would be possible to write code today that checked via reflection whether Lock.Scope implements IDisposable, and which deliberately breaks if it does, so technically this could be considered a breaking change, but this is a far fetched scenario.
This does rely on the JIT not changing its behaviour just because the Dispose method is now part of an interface.
(Note that this does not create a risk of the Lock.Scope being boxed, because it continues to be a ref struct, and you can't box those.)
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from dotnet/runtime
-
agentic-workflows untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
area-System.Reflection blocking-clean-ci-optional Known Build Error os-mac-os-x untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
area-CodeGen-coreclr untriaged
Difficulty 1/5 Under an hour Newbie friendliness 92/100
-
agentic-workflows untriaged
Difficulty 1/5 Under an hour Newbie friendliness 78/100
-
area-VM-meta-mono untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
-
:watch: Not Triaged 11.0 fundamentals/subsvc
Difficulty 2/5 1-3 hours Newbie friendliness 92/100
dotnet/AspNetCore.Docs#37699 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
SubtitleEdit/subtitleedit#15108 · 1 comment ·
-
area/docs-content Bug pulumi/docs
Difficulty 1/5 1-3 hours Newbie friendliness 94/100
-
Create parent directories only after the containment check in InstallHelper.TryExtractToDirectory Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
PowerShell/PSResourceGet#2056 ·