Library Zone Articles
External Articles
Byte Size

Discovery Zone Catalogue
Diary
Links
Bookstore
Interactive Zone Ask the Gurus
Discussion Groups
Newsletters
Feedback
Etc Cartoons
Humour
COMpetition
Advertising
Site Builder ASP Web Ring ASP Web Ring

iDevJobs.com - Jobs for Professional Developers
The Developer's Resource & Community Site
COM XML ASP Java & Misc. NEW: VS.NET
International This Week Forums Author Central Find a Job

COM Singletons - A Dangerous Animal

What is a singleton?

A singleton is a design pattern that allows all clients to create an apparent shared, single instance of an object. Consider holding session information within an application: all objects need to be able to access the same session information. To avoid having to use some documented path to the information to which all components must adhere, it is much safer and more flexible to be able to create a session object which, to all intents and purposes, is the session information.

One way of implementing this in COM is to use a COM singleton - a single COM identity that all calls to CoCreateInstance return. This involves the class factory always returning the same instance from all calls to IClassFactory::CreateInstance.

ATL does this by creating the single identity when it creates the class factory and handing out a reference when CreateInstance is called. You achieve this behaviour by placing the DECLARE_CLASSFACTORY_SINGLETON in your class definition, i.e.


Class ATL_NO_VTABLE CSingletonFoo :
	Public CComObjectRootEx<CComSingleThreadModel>,
	Public CComCoClass<CSingletonFoo, &CLSID_SingletonFoo>,
	Public IDispatchImpl<ISingletonFoo, &IID_ISingletonFoo, &LIBID_SINGLETONSLib>
{
public:
	CsingletonFoo()
	{
	}

DECLARE_REGISTRY_RESOURCEID(IDR_SINGLETONFOO)

DECLARE_CLASSFACTORY_SINGLETON(CSingletonFoo)
DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CSingletonFoo) COM_INTERFACE_ENTRY(ISingletonFoo) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() // IsingletonFoo public: };

However, COM Singletons are, at best, a flawed design approach and, at worst, potentially lethal to your application.

In-Proc Singletons - Highly Dangerous, Do Not Approach

So, why are in-proc singletons dangerous? To explain this we have to look at their usage, let's consider an STA based non-singleton object, Foo: Figure 1 shows a sequence of events:

Figure 1
Figure 1

  1. STA based component A creates Foo - gets direct access
  2. Object living in another STA creates Foo - gets Foo in own apartment.

Now lets make Foo a COM Singleton: Figure 2 shows the sequence of events:

Figure 2
Figure 2

  1. STA based component A creates Foo - gets direct access
  2. Object living in another STA creates Foo - Foo's class factory hands back a direct pointer - object gets direct (non-proxy based) access to Foo.

So why is this so bad?

Foo is an STA based component. It is, therefore, unlikely to have been written thread-safe. Now, one thread runs in STA1 and another in STA2 - but both apartments have direct access to Foo which means that there is nothing to stop both apartments accessing Foo concurrently - for a non-threadsafe object, this is not good news. So, OK we can make Foo threadsafe, is this enough to make sure everything works as we expect - sadly, no.

Consider Foo having a member variable that is an interface pointer to another object, say Bar. We have two possibilities:

  1. Foo has direct access to Bar without going through a proxy. Well, Foo is allowing multiple threads to have concurrent access to itself, so what's to stop these threads accessing Bar concurrently? Nothing. Bar is an STA object that probably had no idea that someone was going to allow concurrent access to it - so it won't be threadsafe. This, obviously, must be avoided.
  2. Foo access Bar via a proxy. COM has a chance to get involved in non-direct access and can enforce it's own rules. The interface pointer to Bar was marshalled for a particular apartment (in our case STA1). If another apartment's thread attempts to access the proxy it will return RPC_E_WRONGTHREAD. This, obviously, must be avoided.

So, interface pointers as member variables will go horribly wrong unless we do something about it. Luckily, the Global Interface Table (GIT) comes to the rescue.

The Global Interface Table

The Global Interface Table (GIT) was introduced in NT4 service pack 3 and DCOM95 1.1. The GIT is a system provided COM Object for marshalling interface pointers, between apartments, within a process. Prior to the GIT, there was no way to marshal any interface pointer such that it could be unmarhsalled many times. CoMarshalInterThreadinterfaceInStream would give you a single opportunity to unmarshal, but for a member variable we need to be able to hold an interface pointer such that we can unmarshal it many times- every time it is requested, say via a property. The GIT allows us to do this and is created as follows:


IGlobalInterfaceTable* pGIT = NULL;
HRESULT hr = CoCreateInstance( CLSID_StdGlobalInterafceTable,
				NULL,
				CLSCTX_INPROC_SERVER,
				IID_IglobalInterfaceTable,
				reinterpret_cast<void**>(&pGit) );

IGlobalInterfaceTable has 3 methods: RegisterInterfaceInGlobal, GetInterfaceFromGlobal and RevokeInterfaceFromGlobal and the IDL for it looks like this:


Interface IGlobalInterfaceTable : Iunknown
{
HRESULT RegisterInterfaceInGlobal( [in]  IUnknown *pUnk, [in]  REFIID riid,
				[out] DWORD *pdwCookie );
HRESULT RevokeInterfaceFromGlobal( [in] DWORD dwCookie );
HRESULT GetInterfaceFromGlobal( [in] DWORD dwCookie, [in] REFIID riid,
				[out, iid_is(riid)] void **ppv );
};

An interface is registered in the GIT by calling RegisterInterfaceInGlobal, this returns a DWORD cookie which can be stored globally as it is not bound to a particular apartment (unlike an interface pointer). When an apartment needs the interface pointer, it calls GetInterfaceFromGlobal which returns an interface pointer which is marshalled correctly for the callers apartment. When there is no more need to hold an interface globally, RevokeInterfaceFromGlobal must be called to remove the interface pointer from the GIT.

Making an object threadsafe, accessing interface pointers via the GIT, some of you may have noticed that STA based inproc singletons seem to have to be coded the same way as components that aggregate the FTM - and you'd be absolutely right. But aggregating the FTM is explicitly providing this behaviour, creating a singleton is providing this by accident.

So if inproc STA based singletons are unpleasant, are there any COM singletons that behave OK? Well, inproc objects which are Free or Single threaded behave OK as they are bound to a single apartment (MTA and main STA repectively); also, out of proc singletons behave OK as well. So, is it generally OK to use singletons?

COM Singletons, a Flaw in the Design

Lets look at the concept of a singleton. A singleton is meant to provide a single view of an object to the world. Implementing a singleton as a single COM identity forces a number of design decisions:

  1. There is no requirement to hold any per client state. There is only one state for all clients - any per client state has to be handcrafted by getting clients to "register" with the singleton, receiving a cookie which is then passed to access the per client state. That no per client state is required is a very bold decision to take at initial design time.
  2. The "scope" of the singleton will be fixed. If you build an in-proc COM singleton, it is only a singleton in respect of the process into which it is loaded. If you build an out-of-proc singleton, it is only a singleton in respect of the machine on to which it is installed. Setting up a singleton as the sole point of contact on a network is possible with a certain amount of configuration on client and server, but the problems with this will become apparent shortly.
  3. The component will not be required to run under MTS or as a configured component under COM+. MTS introduced the concept of objects living within contexts (the details of which are beyond the scope of this article). This concept is expanded upon within COM+. A singleton must exist across contexts (as all components get direct access to it). It therefore cannot be transactional, synchronised (in a COM+ sense) or any of the other declarative attributes that can be applied to components under COM+.
  4. The component will probably have to be thread safe at the object level. Apart from the case of out-of-proc STA singletons, all of a singleton's member data must be protected from concurrent access. In-proc STA ATL singletons will need to use CComMultiThreadModel instead of CComSingleThreadModel.
  5. The component is not required to scale to any degree. If the component is STA based then it will serialise all calls to itself and so be a point of contention for all clients. If the component is MTA based then the thread synchronisation code will be a point of contention for all clients. If a network singleton then it will be a point of contention for all clients across the entire network.

So, given that COM singletons cause problems both in terms of design, and implementation, is there any way singleton like behaviour can be exposed without being bound to a single COM identity? To answer this we have to look at what a singleton gives us - the ability for clients to access shared behaviour and state.

The Shared State / Multiple Identity Model

Imagine an object that dispenses resources, which are of a finite number. These resources are acquired and handed back by the clients (resources could be threads in a thread pool, serial ports, etc.). When a client is finished with the resource it releases it back to the pool. A singleton could be used to manage the finite pool, keeping the number of acquired resources in a member variable of the object.


class ResourceManager : public ImanageResources
{
	DWORD m_dwNumAvailableResources;
	HRESULT AcquireResource()
	{
		if(m_dwNumAvailableResources == 0)
			return E_FAIL;
		m_dwNumAvailableResources--;
		return S_OK;
	}
	HRESULT ReleaseResource()
	{
		m_dwNumAvailableResources++;
		return S_OK;
	}
};

However, there is another way to implement this:


class ResourceManager : public ImanageResources
{
	static DWORD s_dwNumAvailableResources;
	DWORD& m_dwNumAvailableResources;
	ResourceManager()
	: m_dwNumAvailableResources(s_dwNumAvailableResources)
	{
	}
	HRESULT AcquireResource()
	{
		if(m_dwNumAvailableResources == 0)
			return E_FAIL;
		m_dwNumAvailableResources--;
		return S_OK;}
	HRESULT ReleaseResource()
	{
		m_dwNumAvailableResources++;
		return S_OK;
	}
};

Using a static variable to hold the number of available resources allows multiple instances of the ResourceManager object to access a common state. This gives a much more flexible model where the location of the common state can be altered with no knowledge of the client that the scope of the 'singleton' behaviour has been extended. For example, with an in-proc server: we can hold the shared state in static variables and have per process singleton scope; we can hold state in shared memory and have machine wide singleton scope; we can hold the state in a database and have network wide singleton scope - all with no semantic change as regards the client.

Also, these objects have state that doesn't have to be shared by all clients, for example the reference count of the object. With a singleton, as previously discussed, it is often the case that access to all state must be synchronised. With the shared state multiple identity model we only need protect the shared state. In other words, singletons must be thread-safe at the object level, whereas shared state multiple identity model objects need only be thread-safe at the class level. This gives two advantages of the shared state multiple identity model over the singleton: there is less thread synchronisation code to write; there is less contention for resources (and so theoretically more throughput).

Now suppose that we've sold a lot of these resource dispensers, but it's become apparent that the licensing agreement is being abused. We decide, not to stop people using the product when they exceed their license count, but to send an automatic email to ourselves so we can charge more. With a singleton we have problems: the only time we know that a new client wants an object is via the class factory, so we have can increment a count in CreateInstance to reflect the number of clients. However, how do we know that a client has released the resource? Unfortunately there is only one stub for this object so we don't see every Release(). There is no sure-fire way of finding out the usage of the component without the client's cooperation (say through a Register, Unregister function pair). With a shared state multiple identity model component we can simply increment and decrement the usage count in the constructor / destructor (or FinalConstruct / FinalRelease), or in fact IObjectControl::Activate / IObjectControl::Deactivate. This is achieved with no change to the client code and so we can implement our new licensing model without the clients explicit cooperation (implicitly, they will have to install the new code). It is impossible to pre-empt completely the way an object may need to evolve. With the single state multiple identity model a far higher degree of flexibility is available.

The Bottom Line

OK, I've argued that COM singletons are bad but their functionality can be implemented in a much more flexible way using the Shared State multiple identity model. Does that mean you should never use a singleton? If the problems with singletons that I've outlined are of no concern to you, by all means use a singleton, just don't say you haven't been warned. I often use singletons in testing or samples, as a way to allow multiple clients to hit the same state, as they are very quick to implement in ATL. However, I would not now implement a singleton in production code (I have in the past - I too was seduced by the DECLARE_CLASSFACTORY_SINGLETON macro in ATL). There is also a half-way house I which you internally use a singleton but wrap it in another 'normal' object so clients never 'see' the singleton which manages the shared part of the state. However, this does seem like using a singleton out of pure obstinacy.

Sharing state amongst multiple COM identities provides a much more flexible model than singletons and achieves the same result. There is normally at bit more code to write, but I would argue that given the problems and restrictions that it brings DECLARE_CLASSFACTORY_SINGLETON is one of the most dangerous lines of code in ATL.


Richard Blewett is a freelance consultant and part of the DevelopMentor COM+ team. He has been involved in systems development for 10 years and got his first exposure to COM in 1995. He is currently working for Securicor Information Systems as a middle tier architect, designing and writing business objects in ATL. You can contact him at [email protected].


iDevJobs.com - Jobs for Professional Developers

Contribute to IDR:

To contribute an article to IDR, a click here.

To contact us at IDevResource.com, use our feedback form, or email us.

To comment on the site contact our webmaster.

Promoted by CyberSavvy UK - website promotion experts

All content © Copyright 2000 IDevResource.com, Disclaimer notice

Visit the IDR Bookstore!

Code Project

WTL Introduction

Join the Developers Webring

WTL Architecture by Richard Grimes