Singletons C++/CLI: Different instances in native and managed

Q

quortex

Hi all,

I have a native class which has a single instance controlled via the
singleton pattern. I need to call this from both native C++ and from
mixed mode visual studio 2005 c++ CLI.

At the moment I have a MC++ unit test project which accesses the
singleton. What I seem to be finding is that the singleton accessed
directly from the unit test project is not the same instance as the
singleton accessed from native code.

And this is within the same session I am accessing the singleton from
the managed c++ CLI unit test project but when the same project calls a
native class which accesses the singleton it is a different instance.
When calling from the same locations as a test the singleton works
perfectly.

I understand that in a mixed environment there is the garbage collected
managed heap and the native heap however when accessing native types
from either I would expect them to be in the same memory space?

I am very confused here surely I should be accessing the same singleton
instance?

Kind Regards,
Mark
 
H

Holger Grund

quortex said:
And this is within the same session I am accessing the singleton from
the managed c++ CLI unit test project but when the same project calls a
native class which accesses the singleton it is a different instance.
When calling from the same locations as a test the singleton works
perfectly.

I understand that in a mixed environment there is the garbage collected
managed heap and the native heap however when accessing native types
from either I would expect them to be in the same memory space?

I am very confused here surely I should be accessing the same singleton
instance?

I think it depends. Native non-POD objects are always in unmanaged
memory. Their addresses should be stable. But for instance, in this example:

struct X {
static X& instance() {
static X x_;
return x_;
}
};

things are different based on whether you compile with /clr or not.

With /clr X::instance will be emitted as managed code and you
will get an additional thunk for unmanaged clients.

So the question is: what happens if X::instance() is inline in a
header which you include from both managed and unmanaged
compilands. I must admit I don't know. But it could be that
you get distinct functions (somehow bypassing the ODR?),
which would result in distinct objects.

However, so long as the instance accessor is in a single
compiland, you should be fine.

Can you show the definition of your singleton? Is it in a
shared header?

-hg
 
H

Holger Grund

Holger Grund said:
So the question is: what happens if X::instance() is inline in a
header which you include from both managed and unmanaged
compilands. I must admit I don't know. But it could be that
you get distinct functions (somehow bypassing the ODR?),
which would result in distinct objects.

Nah, stupid me. This should work just fine, too.

-hg
 
Q

quortex

Holger,

Thanks for your replies. It doesn't matter if I eager or lazy load the
singleton I get the same problem. All implementation and static
assignments are in a compilant not in the header

e.g.

CPP:
Singleton* Singleton::s_instance(new Singleton()); // Greedy
initialised at program startup
Singleton* Singleton::s_instance(NULL); // Lazy and then initialise in
Singleton::GetInstance with double-locked singleton pattern

Header:
class Singleton
{
private:
static Singleton* s_instance;
public:
static Singleton* GetInstance();
}

Very standard stuff! In the debugger whatever way I cut it their are
two instances. This is a bit of a major problem for me really need to
work out how to get around this and understand what is going on.

Kind Regards,
Mark
 
H

Holger Grund

quortex said:
Header:
class Singleton
{
private:
static Singleton* s_instance;
public:
static Singleton* GetInstance();
}

Very standard stuff! In the debugger whatever way I cut it their are
two instances. This is a bit of a major problem for me really need to
work out how to get around this and understand what is going on.
Ok, this is very standard. You should really get the same object.
It's really more likely that the debugger tricks you into believing
otherwise.

Can you set a breakpoint at GetInstance. Take a look at the
asm & registers to make sure they values are really different.
As always as small but complete repro case would greatly
simplify things (yeah I know it's typically difficult ;-) )

-hg
 
Q

quortex

Hey,

Thanks for the reply. To be honest I don't have to check the registers
as I know it's not the same instance. This is a singleton class used
for debug notifications. You register listener objects with it so that
debug messages can be picked up and handled as required by more than
one source.

When I add listeners from native and from managed I end up with two
instances with one listener in each rather than one instance with two
listeners as it should be.

Here is my code:

Header
------

//////////////////////////////////////////////////////////////////////////////

//! DebugNotifier
//! Global (singleton based) debug handler.
//! Acts as an debug shell allowing multiple debug handlers to be
plugged in
class DebugNotifier
{
private:
static DebugNotifier* s_instance; //!< Holds the singleton instance
static LockCriticalSection s_singletonGuard; //!< Guards the singleton
Notifier<DebugMessage> _notifier; //!< Notification behaviour

//! Constructor. Private as this object employs the singleton pattern
DebugNotifier();

public:
//! Double-locked Singleton. Apparently still not 100% thread safe!?
static DebugNotifier* GetInstance();

//! Add debug listener (IListener). These are broadcast to upon
notification
void AddListener(IListener<DebugMessage>* listener);

//! Returns the current number of listeners
int GetListenerCount() const;

//! Broadcasts notification to listeners
void Notify(DebugMessage* notification);
};

Source
------

///////////////////////////////////////////////
// Static Prototypes
///////////////////////////////////////////////

//DebugNotifier* DebugNotifier::s_instance(NULL); // Lazy Load
DebugNotifier* DebugNotifier::s_instance(new DebugNotifier());// Eager
Load
LockCriticalSection DebugNotifier::s_singletonGuard;

///////////////////////////////////////////////
// Static
///////////////////////////////////////////////

DebugNotifier* DebugNotifier::GetInstance()
{
if(s_instance == NULL)
{
s_singletonGuard.Enter();
if(s_instance == NULL)
s_instance = new DebugNotifier();
s_singletonGuard.Leave();
}

return s_instance;
}

///////////////////////////////////////////////
// Public
///////////////////////////////////////////////

void DebugNotifier::AddListener(IListener<DebugMessage>* listener)
{
if(listener == NULL)
throw Ex::NullArgumentException("Cannot add a NULL listener",
"DebugNotifier", "AddListener");

_notifier.AddListener(listener);
}

//
int DebugNotifier::GetListenerCount() const
{
return _notifier.GetListenerCount();
}

//
void DebugNotifier::Notify(DebugMessage* notification)
{
_notifier.Notify(notification);
}

///////////////////////////////////////////////
// Private
///////////////////////////////////////////////

DebugNotifier::DebugNotifier()
{
// Nout to do at the moment
}

Any ideas? This is very wierd. Maybe its something to do with the test
environment I will try and knock up a test app rather than using the
team system unit test framework but I have had no problems in other
areas it should be fine.

Kind Regards,
Mark
 
T

Tamas Demjen

quortex said:
Any ideas? This is very wierd. Maybe its something to do with the test
environment I will try and knock up a test app rather than using the
team system unit test framework but I have had no problems in other
areas it should be fine.

I was wondering if you have more than one module in your project. The
static keyword is not system-wide static, it's module-wide. So each DLL
and EXE will have its own instance of your singleton, it's expected
behavior.

If you want a true singleton that's guaranteed to share the same
instance across all modules, you have to export a Singleton*
GetInstance() function from your DLL. If you just #include the class
that you posted, it is guaranteed to be not a singleton, because each
module will have its own copy of the class.

Tom
 
T

Tamas Demjen

shu said:
Use

#pragma unmanaged

before including your singleton.h in your managed .cpp

I was talking about unmanaged DLLs. My point is that if the class is not
exported and imported, but #included, static will be module-static,
meaning each DLL and EXE have their own copy of the singleton.

I didn't see the __declspec(dllimport) in the original poster's code,
and that would explain why he's having multiple instances.

Tom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top