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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With