Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Relationship between ManagedThreadID and Operating System ThreadID

I'm working on a multi-threaded C# Windows application that makes frequent calls into a native dll. These are blocking calls which can sometimes last quite a long time.

In certain situations, I'd like to cancel these blocking calls on some worker threads from the main thread The native API I'm using provides a function for this purpose:

HRESULT CancelBlockingCall(DWORD ThreadID)

Although the documentation for the CancelBlockingCall() isn't terribly clear, I believe I need to pass the ThreadID for the OS-level thread which is blocking on the call. Based on the return codes I'm getting from CancelBlockingCall(), I realized that Thread.ManagedThreadID is not what I need. I found the following on msdn (see the Note):

An operating-system ThreadId has no fixed relationship to a managed thread, because an unmanaged host can control the relationship between managed and unmanaged threads. Specifically, a sophisticated host can use the CLR Hosting API to schedule many managed threads against the same operating system thread, or to move a managed thread between different operating system threads.

Does this mean that there is no way for me to properly call CancelBlockingCall() for a managed thread? Is it impossible to determine the ThreadId of the OS-level thread in which a managed thread is currently executing?

like image 899
Odrade Avatar asked Aug 14 '09 20:08

Odrade


1 Answers

As other people mentioned, you could try p/invoking GetCurrentThreadId before calling the blocking native function and registering that id somewhere, but this is a timebomb — some day your managed thread will get pre-empted and re-scheduled to a different OS-level thread between the two p/invoke calls. The only reliable way I can suggest is to write a tiny unmanaged proxy dll, which will call first GetCurrentThreadId (writing it into an out IntPtr someplace where it will be visible to managed code) and then your native blocking function. A callback into managed code instead of out IntPtr might work too; CLR can hardly re-schedule threads while there are unmanaged frames on the stack.

Edit: apparently you're not the first person to have such problems: there are two handy methods in System.Threading.Thread which allow one to defuse the timebomb I mentioned and p/invoke GetCurrentThreadId(): Thread.BeginThreadAffinity() and Thread.EndThreadAffinity(). CLR host will not re-schedule a managed thread to a different native thread between these calls. Your code needs to run at a high trust level to call these methods, though.

like image 60
Anton Tykhyy Avatar answered Sep 22 '22 02:09

Anton Tykhyy