A deadlock may occur when more than one client loads a multithreaded apartment singleton object that you created by using Active Template Library (811591)



The information in this article applies to:

  • Microsoft Visual Studio .NET (2003), Professional Edition
  • Microsoft Visual Studio .NET (2003), Enterprise Architect Edition
  • Microsoft Visual Studio .NET (2003), Enterprise Developer Edition
  • Microsoft Visual Studio .NET (2003), Academic Edition

SYMPTOMS

A deadlock may occur when more than one client creates the same multithreaded apartment (MTA) singleton object by using Microsoft Active Template Library (ATL). When the deadlock occurs, any new calls to receive an instance of the singleton object are also in deadlock.

WORKAROUND

To work around this problem, replace the DECLARE_CLASSFACTORY_SINGLETON definition with a definition that uses a fixed implementation of the CComClassFactorySingleton class. Add the following code before the declaration of the Singleton class:
#undef DECLARE_CLASSFACTORY_SINGLETON
#define DECLARE_CLASSFACTORY_SINGLETON(obj) DECLARE_CLASSFACTORY_EX(CMyComClassFactorySingleton<obj>)

template <class T>
class CMyComClassFactorySingleton : public CComClassFactory
{
public:
	CMyComClassFactorySingleton() : m_hrCreate(S_OK){}
	virtual ~CMyComClassFactorySingleton(){}
	// IClassFactory
	STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
	{
		HRESULT hRes = E_POINTER;
		if (ppvObj != NULL)
		{
			*ppvObj = NULL;
			// Aggregation is not supported in singleton objects.
			ATLASSERT(pUnkOuter == NULL);
			if (pUnkOuter != NULL)
				hRes = CLASS_E_NOAGGREGATION;
			else
			{
				if (m_hrCreate == S_OK && m_spObj == NULL)
				{
					Lock();
					__try
					{
						// Fix:  The following If statement was moved inside the __try statement.
						// Did another thread arrive here first?
						if (m_hrCreate == S_OK && m_spObj == NULL)
						{
							CComObjectCached<T> *p;
							m_hrCreate = CComObjectCached<T>::CreateInstance(&p);
							if (SUCCEEDED(m_hrCreate))
							{
								m_hrCreate = p->QueryInterface(IID_IUnknown, (void**)&m_spObj);
								if (FAILED(m_hrCreate))
								{
									delete p;
								}
							}
						}
					}
					__finally
					{
						Unlock();
					}
				}
				if (m_hrCreate == S_OK)
				{
					hRes = m_spObj->QueryInterface(riid, ppvObj);
				}
				else
				{
					hRes = m_hrCreate;
				}
			}
		}
		return hRes;
	}
	HRESULT m_hrCreate;
	CComPtr<IUnknown> m_spObj;
};

STATUS

Microsoft has confirmed that this is a problem in the Microsoft products that are listed in the "Applies to" section.

MORE INFORMATION

Steps to reproduce the problem

Step 1: Create a multithreaded apartment server object

  1. Create a new ATL project by using Microsoft Visual C++ .NET, and name it MTAServer.
  2. In the ATL Project Wizard - MTAServer dialog box, click Application Settings, and then clear the Attributed check box.
  3. In Solution Explorer, right-click MTAServer, point to Add, and then click Add Class.

    The Add Class - MTAServer dialog box appears.
  4. Under Templates, click ATL Simple Object, and then click Open.

    The ATL Simple Object Wizard - MTAServer dialog box appears.
  5. In the Short name box, type MTAObject.
  6. Click Options, and then click Free.
  7. Add the following code in the FinalConstruct method of the MTAObject.h file:
    ::Sleep(20000);
  8. Add the following code in the public access specifier.
    DECLARE_CLASSFACTORY_SINGLETON(CMTAObject)
  9. Build the project to create the MTAServer.dll file.

Step 2: Create a client for the MTAServer application

  1. Create a Microsoft Windows application by using Microsoft Visual C# .NET, and name it MTAClient.
  2. Add a button control to the Form1 form. By default, the button1 button control is created.
  3. Double-click button1, and then add the following code in the button1_Click method:
            myThreadStart();
    	myThreadStart();
    	myThreadStart();
    	myThreadStart();
    	myThreadStart();
    	myThreadStart();
    	myThreadStart();
    	myThreadStart();
    	myThreadStart();
    	myThreadStart();
    
  4. Add the myThreadStart method and the ThreadProc method before the button1_Click method:
    private void myThreadStart()
    {
    	Thread t = new Thread(new ThreadStart(ThreadProc));
    	t.ApartmentState = ApartmentState.MTA;
    	t.Start();
    }
    
    static void ThreadProc() 
    {
    	MTAServerLib.MTAObjectClass x = new MTAServerLib.MTAObjectClass();
    	System.Diagnostics.Debug.WriteLine("Hello from the thread pool.");
    }
    
  5. Add the following namespace to the beginning of the Form1.cs file:
    using System.Threading;
  6. In Solution Explorer, right-click References, and then click Add Reference.

    The Add Reference dialog box appears.
  7. Click Browse to locate the MTAServer.dll file that you created in the "Step 1: Create a multithreaded apartment (MTA) server object" section, and then build the solution.
  8. Press F5 to run the MTAClient application.
  9. After the Form1 form appears, click button1, and then view the output window.
The MTAClient application starts ten threads, but the output window shows only two threads. The other threads are in the deadlock that is mentioned in the "Symptoms" section.

Modification Type:MinorLast Reviewed:5/20/2005
Keywords:kbATLServer kbtshoot kbbug KB811591 kbAudDeveloper