none
Can two functions in same DLL be called by two threads?

    Question

  • I write a DLL MyDLL.dll with Visual C++ 2008, as follows:

    (1)          MFC static linked

    (2)          Using multi-thread runtime library.

    In the DLL, this is a global data m_Data shared by two export functions, as follows:

                                    ULONGLONG WINAPI MyFun1(LPVOID *lpCallbackFun1)

                                    {

                                                    ...

                                                    Write m_Data(using Critical section to protect)

                                                   

                                   

                                                    return   xxx;

                                    }

                                   

                                    ULONGLONG WINAPI MyFun2(LPVOID *lpCallbackFun2)

                                    {

                                                    ...

                                                    Suspend MyThread1 to prevent conflict.

                                                    Read m_Data(using Critical section to protect)

                                                    Resume MyThread1.

                                                   

                                   

                                                    return   xxx;

                                    }

    In in my main application, it will first call LoadLibrary to load MyDLL.dll, then get the address of MyFun1 and MyFun2, then do the following thing:

    (1)          Start a new thread MyThread1, which will invoke MyFun1 to do a time-consuming task.

    (2)          Start a new thread MyThread2, which will invoke MyFun2 for several times, as follows:

                                    for (nIndex = 0; nIndex = 20; nIndex)

                                    {

                                    nResult2 = MyFun2(lpCallbackFun2);

                                    NextStatement2;            

                                    }

    Although MyThread1 and MyThread2 using critical section to protect the shared data m_Data, I will still suspend MyThread1 before accessing the shared data, to prevent any possible conflicts.

    The problem is:

    (1)          When the first invoke of MyFun2, everything is OK, and the return value of MyFun2(that is nResult2) is 1 , which is expected.

    (2)          When the second, third and fourth invoke of MyFun2, the operations in MyFun2 are executed successfully, but the return value of MyFun2(that is nResult2) is a random value instead of the expected value 1. I try to using Debug to trace into MyFun2, and confirm that the last return statement is just return a value of 1, but the invoker will receive a random value instead of 1 when inspecting nResult2.

    (3)          After the fourth invoke of MyFun2 and return back to the next statement follow MyFun2, I will always get a “buffer overrun detected” error, whatever the next statement is.

    I think this looks like a stack corruption, so try to make some tests:

    1.             I confirm the /GS (Stack security check) feature in the compiler is ON.
    2.             If MyFun2 is invoked after MyFun1 in MyThread1 is completed, then everything will be OK.
    3.             In debug mode, the codeline in MyFun2 that reads the shared data m_Data will not cause any errors or exceptions. Neither will the codeline in MyFun1 that writes the shared Data.

    So, how to solve this problem

    Thank you!

    Wednesday, August 27, 2014 10:54 PM

Answers

  • Try:

    typedef ULONGLONG (WINAPI *MyFun1) (*lpCallbackFun1);

    typedef ULONGLONG (WINAPI *MyFun2) (*lpCallbackFun2);

    Just because a certain calling sequence does not generate a stack problem doesn't mean a stack problem doesn't exist.

    • Marked as answer by chcw Thursday, August 28, 2014 4:01 AM
    Thursday, August 28, 2014 1:45 AM

All replies

  • Do not suspend and resume the thread to attempt to synchronize. That is the wrong way.

    If the critical section is not working then show us the actual code you use that is related to the critical section. It will do the job if you use it correctly.

    Wednesday, August 27, 2014 11:40 PM
  • It sounds like calling convention of the exported DLL functions is incorrect.  How do you prototype the function pointers you get with GetProcAddress?
     
    And if you use a critical section, you don’t need to suspend and resume MyThread1.  This seems an error waiting to happen.  For example, in MyThread1, do you similarly suspend/resume MyThread2 to prevent it from accessing m_Data while MyThread1 writes it?  I would simply use a critical section in both threads to protect access to m_Data.
     
    Thanks,
    David
    Thursday, August 28, 2014 12:06 AM
  • Actually I have commented out the suspend & resume codelines, but the problem still occurs.

    The critical section should working properly, I think, since there are no errors during reading/writing the m_Data.

    Actually m_Data is a CMap dervied class, I do as follows:

    class CMyMap: public CMap<ULONGLONG, ULONGLONG&, CMapEntry *, CMapEntry *>

    {

    private:

        mutable CCritialSection m_CritialSection;

    }

    And I override each function in CMap so that because invoke the base class function, it will first call

    m_CriticalSection.Lock();

    and when finished, call

    m_CriticalSection.Unlock();

    Thursday, August 28, 2014 12:55 AM
  • At first I also guess this is a convention error. However, if MyFun2 is invoked from MyThread2 after MyFun1 is completed in MyThread1, then everything will be OK.

    I defines MyFun1 and MyFun2 as:

    typedef ULONGLONG (*MyFun1) (*lpCallbackFun1);

    typedef ULONGLONG (*MyFun2) (*lpCallbackFun2);

    Then use type cast to get the corresponding function, as follows:

    MyFun1 m_MyFun1 = (MyFun1)GetProcAddress(...)

    In MyThread1, I do not suspend/resume MyThread2 when write to m_Data. Actually I also try to comment out the suspend/resume code in MyThread2 but the problem still occurs.

    Thank you very much!

    Thursday, August 28, 2014 1:02 AM
  • Try:

    typedef ULONGLONG (WINAPI *MyFun1) (*lpCallbackFun1);

    typedef ULONGLONG (WINAPI *MyFun2) (*lpCallbackFun2);

    Just because a certain calling sequence does not generate a stack problem doesn't mean a stack problem doesn't exist.

    • Marked as answer by chcw Thursday, August 28, 2014 4:01 AM
    Thursday, August 28, 2014 1:45 AM
  • Thank you so much. The problem is solved by adding the WINAPI modifier.
    Thursday, August 28, 2014 4:01 AM