Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass an interface pointer to a thread?

Note:

  • Using raw Win32 CreateTheard() API
  • No MFC
  • An interface is simply a pointer to a vtable

Question:

  • How to pass an interface pointer to a thread?

Illustration:

IS8Simulation *pis8 = NULL;

...

CoCreateInstance(
                 clsid,
                 NULL,
                 CLSCTX_LOCAL_SERVER,
                 __uuidof(IS8Simulation),
                 (void **) &pis8);



...

hThread = CreateThread(
                NULL,
                0,
                SecondaryThread,
                //interface pointer pis8
                0,
                &dwGenericThreadID);

...

DWORD WINAPI SecondaryThread(LPVOID iValue)
{
    //using iValue accordingly
    //E.g.: iValue->Open

Regards

like image 613
Aaron Avatar asked Feb 04 '09 10:02

Aaron


3 Answers

As was stated below, passing a COM interface pointer between threads in not safe.

Assuming you know what you are doing:

hThread = CreateThread(
                NULL,
                        0,
                        SecondaryThread,
                        (LPVOID) pis8
                        0,
                        &dwGenericThreadID);

DWORD WINAPI SecondaryThread(LPVOID iValue)
{
   ((IS8Simulation*) iValue)->Open();
}

Thread safe version:

void MainThread()
{
    IStream* psis8;
    HRESULT res = CoMarshalInterThreadInterfaceInStream (IID_IS8SIMULATION, pis8, &psis8);
    if (FAILED(res))
         return;
    hThread = CreateThread(
                NULL,
                0,
                SecondaryThread,
                (LPVOID) psis8
                0,
                &dwGenericThreadID
          );
}

DWORD WINAPI SecondaryThread(LPVOID iValue)
{
   IS8Simulation* pis8;
   HRESULT res = CoGetInterfaceAndReleaseStream((IStream*) iValue, IID_IS8SIMULATION, &pis8);
   if (FAILED(res))
      return (DWORD) res;
   pis8->Open();
}
like image 68
Quassnoi Avatar answered Sep 21 '22 20:09

Quassnoi


If the interface in your question is a COM interface, the approach given by Quassnoi might not be sufficient. You have to pay attention to the threading-model of the COM object in use. If the secondary thread will join a separate COM apartment from the one that your COM object was created in, and if that object is not apartment-agile, you'll need to marshal that interface pointer so that the secondary thread gets a proxy, and not a direct pointer to the object.

A COM object is normally made apartment-agile by using a special implementation of IMarshal. The simplest approach is to aggregate the Free Threaded Marshaler.

Some useful links...

  • CoMarshalInterThreadInterfaceInStream
  • GlobalInterfaceTable (GIT)
  • CoCreateFreeThreadedMarshaler

Update: About the Free-threaded Marshaler...

It's clear from comments on this topic that some people would recommend that you never touch the FTM. While "Effective COM" is an excellent book, I think some of its recommendations are open to interpretation. Item 33 says "Beware the FTM"; it does not say "Never use the FTM". Very wisely it advises caution particularly when your apartment-agile object holds references to other objects, because they might not be apartment-agile. So really the advice is: think carefully when building apartment-agile objects, whether or not they use the FTM to achieve their agility. If you're sure you can build an apartment-agile object, I see no reason why you wouldn't use the FTM to achieve that.

like image 35
Martin Avatar answered Sep 22 '22 20:09

Martin


You basically need to do the following:

  • CoMashalInterThreadInterfaceInStream ==> you get an IStream interface.
  • pass that IStream to the thread, e.g. as Quassnoi said.
  • in SecondaryThread, call CoGetInterfaceAndReleaseStream to get the interface (or a proxy to it, if necessary).

Do not release the IStream interface unless creating the thread fails, and don't exit the thread until yu have called CoGetInterfaceAndReleaseStream.

COM runtime will create the proxy for you automatically. The proxy ensures that e.g. an apartment-threaded COM component is called on the thread that created it. However, this also requires that:

  • The interface is IDispatch, or proxy/stub components are registered for the interface
  • the threadthat created the component has a message loop and processes messages
like image 2
peterchen Avatar answered Sep 18 '22 20:09

peterchen