Home » Community » Forums » » The One: A Singleton Discussion
  Intel sponsors gamedev.net search:   
[Control Panel] [Register] [Bookmarks] [Who's Online] [Active Topics] [Stats] [FAQ] [Search]

Add Forum to Favorites |  Send Topic To a Friend | View Forum FAQ | Track this topic

Page:   1 2 »»

 Last Thread Next Thread 
 The One: A Singleton Discussion
Post Reply 
Regarding Thread Safety

In a generic template-based singleton the only assumption you can reasonably make about dealing with thread safety is the creation of the singleton itself.

The double-checked locking pattern combined with a mutex/critical section can solve this problem.

I agree you can't make assumptions about how to lock the singleton object itself - that is implementation specific.

Besides, using a scope-based locking technique inside of GetInstance() would only last for the duration of the call - so it would not protect the singleton once the method call returns.

One way maybe you could consider doing it would be to return a kind of smart-pointer/mutex object from the GetInstance() method. That way each call to GetInstance would return a temporary automatic variable which locked the mutex until the call to the real class's method ( accessed via operator-> ) occurred.

This is a variation of the prefix/suffix technique described by Stroustrup in an article ( not sure right now if it was C++ Report or C/C++ Users Journal )


 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

quote:
Original post by SteveC
Besides, using a scope-based locking technique inside of GetInstance() would only last for the duration of the call - so it would not protect the singleton once the method call returns.


Exactly. It's like trying to make an internally-synchronized container; the container has no way of knowing over which calls each thread needs exclusive access. It must be externally synchronized.

quote:

One way maybe you could consider doing it would be to return a kind of smart-pointer/mutex object from the GetInstance() method. That way each call to GetInstance would return a temporary automatic variable which locked the mutex until the call to the real class's method ( accessed via operator-> ) occurred.


Possible, but then you force that overhead for single-thread apps, and there's the chance that a user will save the handle, retaining the lock forever. Plus, the lock shouldn't be released on the first method call--how is the singleton object to know over what series of calls a given thread needs exclusive access? Same problem as above.

If a user needs a singleton that assures exclusive access over a call period, the singleton can offer a handle object that grabs the mutex on creation, allows access to the object (through ->), and releases it on destruction. Then the user just needs to be sure to only hold the handles on the stack, e.g. release when done.




 User Rating: 1100   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

quote:
Original post by Stoffel Exactly. It's like trying to make an internally-synchronized container; the container has no way of knowing over which calls each thread needs exclusive access. It must be externally synchronized.



Actually, that would solve the creation problem fine but it becomes a performance problem.


[edited by - void on May 25, 2002 9:14:05 AM]

 User Rating: 1017   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quoted from the article:

quote:

Practical experience suggests than it is often possible to redesign so the singleton becomes a member variable or local static variable. Indeed, this should be a worthy goal to strive for.

Do we really need the graphics renderer to be global? Can it be a member variable of an Application object instead?



I did not understand this part. In the example, the graphics renderer is a member of an Application object. But, isn't this Application object a singleton itself?



 User Rating: 974   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

The application does not have to be a singleton. At the top upper most level, it can be just an auto variable.

ie.


int main() // or winmain
{
Application app; // holds renderer

while(1)
{
app.Update();
app.Render();
}
}


If you need to pass renderer to subsystems, let them take a reference.

There are certain situations where a singleton would really be useful. For the renderer, IMO, it doesn't qualify.

 User Rating: 1017   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Maybe I am just missing the point entirely, but whatever ever happened to good old fashion explicit creation/destruction? Its very simple to add an explicit static Create/Destroy function to your singletons which then creates/destroys the static instance of the class.

A singleton is simply a class that exists once, there really are no hard set rules on how they are created or destroyed (if you want to get in to the nitty gritty of OO that is). Sure, this requires you to make an extra call our two at application init/de-init, but how many singletons do you plan to have anyway?


 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

quote:
Original post by Void
The application does not have to be a singleton. At the top upper most level, it can be just an auto variable.

If you need to pass renderer to subsystems, let them take a reference.

There are certain situations where a singleton would really be useful. For the renderer, IMO, it doesn't qualify.


Can someone give some examples of these situations?

 User Rating: 974   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

quote:
Original post by Kerion
Maybe I am just missing the point entirely, but whatever ever happened to good old fashion explicit creation/destruction? Its very simple to add an explicit static Create/Destroy function to your singletons which then creates/destroys the static instance of the class.



Exception safety. You can't have robust code if you do an explicit creation/destruction. Or at least not letting it turn into a maintanance nightmare down the road.

 User Rating: 1017   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

quote:
Original post by Outworlder
Can someone give some examples of these situations?


Which situation is not clear? The passing reference?

The application holds renderer, sound, input etc. Those classes that require a renderer stores a reference member varibles instead of assuming it was a singleton. These classes should be at high level enough so the application can store them as member variables.


 User Rating: 1017   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

quote:

There are certain situations where a singleton would really be useful. For the renderer, IMO, it doesn't qualify.



I understand how to use member variables, and how to create and use singletons. I was asking for some examples of situations where a singleton would be preferred to, say, a global or a member variable.

Or, even better, advantages and disadvantages of each approach.

Until now, my managers, the renderer and a couple of other classes are implemented as a singleton. I can't see any disadvantages of doing so in my program.

 User Rating: 974   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Crap, I typed a really long reply and the server lost the message! I'm not gonna type it again

I highlighted the problems with singleton here. If you didn't understand, that means I failed

Look at cout. You need it to be valid in the whole program. But VC++ 6 version uses meyers singleton and it suffers from destruction order problem.

Anyway, check out "An exceptional quest" in the articles section and Modern C++ Design Abstract Factory pattern for why you need singletons.

 User Rating: 1017   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

uh... i'm sort of confuzed by the output the nifty counter program gave me:
Main INIT
MyClass CTOR
Method called
Main EXIT
Method called
Press any key to continue
 


and yes, i'm SURE that it's not breakmeyer, but niftycounter!

i don't see the MyClass dtor being called. :-\ that's really weird...

---
shurcool
wwdev


 User Rating: 1137   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

ok, I have replied you in your message too. Thanks for bringing it to my attention.

If you are using VC++ 6, the nifty counter output may not show MyClass DTOR as expected.

This is because VC++ 6 STL cout implementation uses the meyer's singleton, and it is destroyed before the nifty counter.

The solution is to switch STL implementation ( I use www.dinkumware.com VC++ 3.08 STL), use printfs/OutputDebugMessage instead of cout, or use the debugger to set a breakpoint at the myclass dtor to verify that it is actually being called.

 User Rating: 1017   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

quote:
Original post by SteveC
The double-checked locking pattern combined with a mutex/critical section can solve this problem.



The double-checked locking pattern does not work correctly in most (all?) implementations:
http://groups.google.de/groups?q=g:thl3178003383d&dq=&hl=de&lr=&ie=UTF-8&selm=MPG.191d9257dfa5ebf79896db%40news.hevanet.com&rnum=1

[edited by - Fire-Clankiller on February 24, 2004 7:59:19 AM]

[edited by - Fire-Clankiller on February 24, 2004 8:22:39 AM]

 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

would it be a bad idea to make a whole class static, as in all the methods and variables static ? Im thinking of doing this with my renderer library which plugs into the engine for API-independant gfx ?

thanks

 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Hi everyone,

I have just discovered the "Nifty Counter" type of singleton in the article being discussed and I almost thought that this would the solution to a common order of destruction problem. Unfortunately, it relies on the static instance of the nifty counter class to be created before any other static object in a translation unit that might call MyClassNiftyCounter::Get_Instance() in its destructor. Take a look at the following example of a translation unit:

struct X
{
    ~X();
};

X x;

#include "MyClass.h"

X::~X()
{
    MyClassNiftyCounter::Get_Instance().Method();
}


In the translation unit shown above, the non-local static object of type X will probably be created first. Then, by including "MyClass.h", the static object of type MyClassNiftyCounter will be created and finally be destroyed before the object of type X, thus causing X::~X() to call MyClassNiftyCounter::Get_Instance() when the object of type MyClass has already been destroyed.

As order of construction/destruction is undefined across translation units, there is no guarantee that there will be static instance of the nifty counter class in another translation unit that has not been destroyed yet and that can thus prevent the disaster.

 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Thanks for bringing back a very old article. Honestly, you can do everything you need too without a singleton. There are SO few instances a singleton is the RIGHT or best solution. It normally means you're thinking in procedural ways, which is fine for a procedural language like C, but if you're using C++ please stop it :)

A singleton, at the end of the day, is just a global. Globals are, for the most part, bad design. I have a complete and working 3D engine for a commercial game and not a single singleton in sight.

For Christmas, all I want is articles like these to fade away as better programming practices take hold! Oh and peace on Earth!

 User Rating: 1015    Report this Post to a Moderator | Link

Quote:
Initialization of static objects is well defined with each translation unit (ie. .cpp file), but not across multiple files. This is why we need singletons.



Thats got to be the worst justification for using a singleton I've heard yet.

 User Rating: 1035   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

The article discusses various types of singletons, but it does not tell you when and how often to use them. I rarely use singletons myself, but if I decide to do so, then why not do it right? That's where the article might be helpful.

 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

I've honestly never come across a time where a singleton was the best solution. Sometimes it's the laziest way (aka best at the time) to go. I used to make my loggers a singleton, then I realized global functions within namespaces is a much, much better solution then to using a singleton.

What are your situations when you think a singleton is the best solution? I'm sure the brains here can come up with much better, less procedual and more OOP to boot!

 User Rating: 1015    Report this Post to a Moderator | Link

I am using a singleton for a logger, too. It's a memory tracking mechanism that stores information about allocated memory in a database object, which offers methods like

TrackNew( T* ptr, const char* file, int line );
TrackDelete( T* ptr, const char* file, int line );

These methods are called from macros like

#define NEW( T ) MemoryTracker::Instance().TrackNew( new T, __FILE__, __LINE__ )
#define DELETE( ptr ) delete MemoryTracker::Instance().TrackDelete( ptr, __FILE__, __LINE__ )

How would you implement that with global functions within namespaces? And isn't that pretty much the same, because a class also acts somewhat (or in this case even exactly) like a namespace?

 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:
Original post by Eisenbart
Hi everyone,

I have just discovered the "Nifty Counter" type of singleton in the article being discussed and I almost thought that this would the solution to a common order of destruction problem.


Ok, this is violating how the nifty counter is meant to be used. The idea behind a nifty counter singleton is the header generates static local variable in global scope (if I remember correctly, the term is glocals) which keeps a reference count. This means nifty counter must not depend on other singleton classes and should be included before other glocals.

What you are doing is valid C++, but it violates "good" programming habits, you should import headers before define local definitions.

 User Rating: 1017   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Quote:
Original post by Eisenbart
I am using a singleton for a logger, too. It's a memory tracking mechanism that stores information about allocated memory in a database object, which offers methods like

TrackNew( T* ptr, const char* file, int line );
TrackDelete( T* ptr, const char* file, int line );
These methods are called from macros like

#define NEW( T ) MemoryTracker::Instance().TrackNew( new T, __FILE__, __LINE__ )
#define DELETE( ptr ) delete MemoryTracker::Instance().TrackDelete( ptr, __FILE__, __LINE__ )
How would you implement that with global functions within namespaces? And isn't that pretty much the same, because a class also acts somewhat (or in this case even exactly) like a namespace?


The global functions fire off a message to the event handler asking for a pointer to the Memory Manager class. They then use that and log the information into that the database. No singleton required. More code? Yes. Better design then a singleton? Hell yes.

Someone might argue that you cannot remove your memory manager and place it into another application easily. Mine also has the ability to use a local copy of the manager if my engine library isn't detected. It can also save/read from a file all the data also. It logs pointers etc to the file. Bit more work but its a very robust memory manager.

 User Rating: 1015    Report this Post to a Moderator | Link

Quote:
Original post by Anonymous Poster
The global functions fire off a message to the event handler asking for a pointer to the Memory Manager class. They then use that and log the information into that the database.

I don't quite see the difference towards my implementation. I am retrieving a pointer (well, actually a reference) to the Memory Manager, too. What does the use of an event handler have to do with the use of a singleton? And what's the lifetime of the Memory Manager object whose pointer you are retrieving?

Quote:
Original post by Anonymous Poster
Someone might argue that you cannot remove your memory manager and place it into another application easily. Mine also has the ability to use a local copy of the manager if my engine library isn't detected. It can also save/read from a file all the data also. It logs pointers etc to the file. Bit more work but its a very robust memory manager.

Does your Memory Manager also have the ability NOT to log its data into file, but to keep everything in memory and only write the result (e.g. remaining allocations not having been deleted) into a file? The problem is that logging each allocation or deallocation into a file immediately can significantly slow down an application, it can make it almost entirely freeze up for a while. I know that because my first implementations of a Memory Manager did log everything into a file right away.

If you keep all the data about allocations and deallocations in memory until the application terminates (as does my implementation of a Memory Manager), you cannot use a local copy of it. This is why I am using a singleton.

 User Rating: 1015   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link
Page:   1 2 »»
All times are ET (US)

Post Reply
 Last Thread Next Thread 
Forum Rules:
You may not post new threads
You may post replies
You may not edit your posts
You may not use HTML in your posts
Jump To:
Administrative Options: