[API Proposal]: Thread.BeginThreadAffinity(out ProcessThread)
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
### Background and motivation
For certain diagnostics tasks, such as monitoring the CPU time and priorities of threads from within an application, the `ProcessThread` class must be used, as it contains the necessary diagnostic properties. However, the support for obtaining an instance of this class is very poor on .NET Core+, especially due to #63535 (`AppDomain.GetCurrentThreadId` returning an incorrect value), and one has to resort to calling platform-specific functions like `gettid`.
There are also other various issues with obtaining a `ProcessThread` instance:
* `ProcessThread.Id` is an `int`, however certain platforms (Mac) use a 64-bit value, which makes the existing API incorrect. Reported as #21270 but closed.
* `Process.GetCurrentProcess().Threads` constructs an array of `ProcessThread` instances, wrapped in a `ProcessThreadCollection`. Having to go through the whole collection when only a single thread is needed is inefficient.
* The mapping between `Thread` and `ProcessThread` is fundamentally unstable. `Thread.BeginThreadAffinity` must be called for correct behaviour.
However, the last point also serves as a great starting point for improving the situation: since there is no other place than between the calls to `BeginThreadAffinity` and `EndThreadAffinity` to work with a `ProcessThread` for the current thread, it can also serve to retrieve that instance. This is in line with the general attitude of moving methods that manipulate an arbitrary thread (such as `Thread.Abort`) to mechanisms usable only from within the thread, such as `ControlledExecution.Run`.
### API Proposal
```csharp
using System.Diagnostics;
namespace System.Threading;
class Thread
{
///
/// Notifies a host that managed code is about to execute instructions that depend on the identity of the current physical operating system thread,
/// and retrieves the instance corresponding to that system thread.
///
///
/// When this method returns, contains the instance which can be used to observe the system thread running the code.
/// This instance is guaranteed to be valid and reporting accurate values up to the call ending the thread affinity requirement.
///
///
/// Retrieving the current system thread is not possible on this platform.
///
public void BeginThreadAffinity(out ProcessThread systemThread);
}
```
### API Usage
```csharp
Thread.BeginThreadAffinity(out ProcessThread thread);
try
{
var startTime = thread.TotalProcessorTime;
for(int i = 0; i < 10000000; i++)
{
// CPU-intensive code
}
var totalTime = thread.TotalProcessorTime - startTime;
Console.WriteLine($"The thread spent {totalTime} on the CPU.");
}
finally
{
Thread.EndThreadAffinity();
}
```
### Alternative Designs
Another option is a property like `ProcessThread.CurrentThread`, however that has the same correctness issues like using the thread ID without `Thread.BeginThreadAffinity`.
It is also possible to copy all useful properties on `ProcessThread` to the `Thread` instance.
### Risks
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.