none
releasing unmanaged 3rd party dll's blocking thread RRS feed

  • Question

  • I am handling an event raised by a third-party unmanaged DLL. The event is supposed to raise extremely frequently (every milliseconds and more) by the DLL but it seems like the event raised by the unmanaged dll is blocking, by which I mean the unmanaged dll wont raise another event until I am finished processing the  current one. Following is the code and my implementation.

    I am just not sure where exactly the following code release the third-party dll's thread.

    First, third-party unmanaged DLL raises an event which is handled by my unmanaged code. The unmanaged handler calls another static unmanaged function which then raises a callback which is handled by the managed code. The managed code then passes an object by reference through a static function call to unmanaged code which populates the object passed by reference and that concludes the processing of the event.

     

    This is the unmanged event handler in my code for event raised by the unmanaged dll.

    RecordProcesser *m_pUpdateHandler;
    MyRecords::ProcessUpdate(Record &record)
    {
    m_pUpdateHandler->OnRecordUpdate (record)  //Do I release the thread here??
    }


    void RecordProcesser::OnRecordUpdate(Record &record) //function called by the event handler
    {
    EnterCriticalSection(obj)
    Do Some processing
    LeaveCriticalSEction

    m_pCallback((const char *)Record.symbol(), (void*)&Record, (void*)this); //This callback is handled in managed code
    //Do I release the thread here by raising an event which is handled in managed C# code???

    }

     

    This is the managed handler of m_pCallback
    private void OnRecordUpdate(string arg1, IntPtr arg2, IntPtr pThis)
    {
    lock (someobj)
    {

    //do some processing and then call an unmanaged function

    GetRecord( record, obj , pThis) //This is the call to unmanaged static function - the obj is populated by the unmanaged code and is passed as reference here
    //After this line of code the function ends. Is this where the 3rd part dll's thread is released?

    }

    }

     

    Thanks,

    Tuesday, May 26, 2009 2:49 PM

Answers

  • That is correct, the event handler is executed on the same thread on which it is raised. To say the thread is "released" it is not exactly accurate because the thread executes the control flow, it is not owned by it. If I have understood your code correctly then the call stack of the thread when it executes your managed callback will be like this:

    OnRecordUpdate <-- managed code
    <unmanaged-managed transition stack frames>
    RecordProcesser::OnRecordUpdate
    MyRecords::ProcessUpdate <--- method that raises the event
    <other frames>

    As you can see the longer the OnRecordUpdate takes the slower MyRecords::ProcessUpdate will fire because its execution time depends on how long your callback code will take. For example assuming you sit in a loop and fire ProcessUpdate continuously and OnRecordUpdate takes 0.5 sec then the event will be fired max. twice per second, whereas if OnRecordUpdate takes 0.1 secs you will fire (call) ProcessUpdate 10 times per sec. 

    Based on this you goal is to minimize how long OnRecordUpdate takes so if you have some long running code there my suggestion was to move it on a different thread - post an async task to the worker pool so your current thread will not be blocked and will return faster the control to the ProcessUpdate method, hence allowing it to be called again.

    The above optimization was for a single thread of execution. If you have multiple threads your goal is to allow as much paralellism as possible so remove as much locking as possible to allow them to execute freely, not in a serial fashion. Using your code above let's say you have two threads t1 and t2 and they execute the same code:

    - both t1 and t2 call ProcessUpdate
    - both continue execution and call RecordProcesser::OnRecordUpdate
    - 1st lock : RecordProcessor: OnRecordUpdate has a critical section fo if t1 has the lock t2 will wait
    - t1 releases the lock, t2 gets it
    - both eventually call OnRecordUpdate callback
    - 2nd lock : OnRecordUpdate callback will lock so if t1 has the lock t2 will have to wait again.

    Since you have locks your threads will have to wait one for another so instead of scaling and having twice as much events fired (you have two threads running in parallel) they will be actually serialized and you get less optimal performance. If your code would be lock free you will have the optimal case in which both threads fire events at full speed :).



    • Marked as answer by Zhi-Xin Ye Monday, June 1, 2009 1:44 PM
    Tuesday, May 26, 2009 6:34 PM

All replies

  • The managed code callback will give control back to the native dll when the callback call returns so if you want to maximize the frequency with which the events are fired in your managed callback you should offload the work to a separate asynchronous task. You should also try to remove any locks that will possibly serialize the event processing like your lock(someobj) from your OnRecordUpdate method.

    For example OnRecordUpdate could be implemented like this:
    - create a new class/structure in which you copy all data needed for the event to be processed (call GetRecord, fetch the data, copy to task params)
    - schedule a new async task using (for example) ThreadPool.QueueUserWorkItem
    - return

    Another solution that will work well is if all the data necessary to process the event is part of the event arguments and you don't have to go back to the managed code using calls which will possibly serialize the resource access again (using locks, critical sections, etc). In this situation just
    - schedule a new async task with the event args as parameters
    - return

    Tuesday, May 26, 2009 5:04 PM
  • The managed code callback will give control back to the native dll when the callback call returns so if you want to maximize the frequency with which the events are fired in your managed callback you should offload the work to a separate asynchronous task. You should also try to remove any locks that will possibly serialize the event processing like your lock(someobj) from your OnRecordUpdate method.

    For example OnRecordUpdate could be implemented like this:
    - create a new class/structure in which you copy all data needed for the event to be processed (call GetRecord, fetch the data, copy to task params)
    - schedule a new async task using (for example) ThreadPool.QueueUserWorkItem
    - return

    Another solution that will work well is if all the data necessary to process the event is part of the event arguments and you don't have to go back to the managed code using calls which will possibly serialize the resource access again (using locks, critical sections, etc). In this situation just
    - schedule a new async task with the event args as parameters
    - return


    Just so I understand correctly, this line
    m_pCallback((const char *)Record.symbol(), (void*)&Record, (void*)this);
    in RecordProcesser::OnRecordUpdate releases the third-party dll thread?

    Do you think it will be faster if I remove all processing and locks from OnRecordUpdate and just simply raise the call back Vs. the solution you proposed?

    One more thing, In another question  I asked in this forum
    http://social.msdn.microsoft.com/Forums/en-US/clr/thread/86a08ce4-f1d1-43c5-9d3b-e2c61d6bf448

    I was told event handler is executed on the same thread on which it was raised. From that it seems like the 3td party dll thread wont be released until the very end

    My goal is to have 3rd partry dll raise events without having to wait (or with minimal wait time possible) for me to process the update.

    • Edited by bsobaid Tuesday, May 26, 2009 5:43 PM
    Tuesday, May 26, 2009 5:31 PM
  • That is correct, the event handler is executed on the same thread on which it is raised. To say the thread is "released" it is not exactly accurate because the thread executes the control flow, it is not owned by it. If I have understood your code correctly then the call stack of the thread when it executes your managed callback will be like this:

    OnRecordUpdate <-- managed code
    <unmanaged-managed transition stack frames>
    RecordProcesser::OnRecordUpdate
    MyRecords::ProcessUpdate <--- method that raises the event
    <other frames>

    As you can see the longer the OnRecordUpdate takes the slower MyRecords::ProcessUpdate will fire because its execution time depends on how long your callback code will take. For example assuming you sit in a loop and fire ProcessUpdate continuously and OnRecordUpdate takes 0.5 sec then the event will be fired max. twice per second, whereas if OnRecordUpdate takes 0.1 secs you will fire (call) ProcessUpdate 10 times per sec. 

    Based on this you goal is to minimize how long OnRecordUpdate takes so if you have some long running code there my suggestion was to move it on a different thread - post an async task to the worker pool so your current thread will not be blocked and will return faster the control to the ProcessUpdate method, hence allowing it to be called again.

    The above optimization was for a single thread of execution. If you have multiple threads your goal is to allow as much paralellism as possible so remove as much locking as possible to allow them to execute freely, not in a serial fashion. Using your code above let's say you have two threads t1 and t2 and they execute the same code:

    - both t1 and t2 call ProcessUpdate
    - both continue execution and call RecordProcesser::OnRecordUpdate
    - 1st lock : RecordProcessor: OnRecordUpdate has a critical section fo if t1 has the lock t2 will wait
    - t1 releases the lock, t2 gets it
    - both eventually call OnRecordUpdate callback
    - 2nd lock : OnRecordUpdate callback will lock so if t1 has the lock t2 will have to wait again.

    Since you have locks your threads will have to wait one for another so instead of scaling and having twice as much events fired (you have two threads running in parallel) they will be actually serialized and you get less optimal performance. If your code would be lock free you will have the optimal case in which both threads fire events at full speed :).



    • Marked as answer by Zhi-Xin Ye Monday, June 1, 2009 1:44 PM
    Tuesday, May 26, 2009 6:34 PM