Jump to content
  • Advertisement
Sign in to follow this  
dpadam450

static variable in function

This topic is 981 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I remember a couple years ago someone speaking of putting a singleton variable as a function static instead of a static pointer variable as a private member of the class.

 

However, since statics in functions aren't constructed until they are first approached, then somehow the code has to prevent it from reconstructing the singleton every single time a user calls GetInstance(). So what happens to the code?

 

static RenderManager& RenderManager::GetInstance()

{

   static RenderManager renderManager;

   return renderManager;

}

 

 

//Call GetInstance()

//Construct local static variable at some globally defined address

//return instance

 

//Call GetInstance() again

//---------- what instruction would go here? is it replaced with a "no-op"?

//Return instance

Share this post


Link to post
Share on other sites
Advertisement

I really need to learn x86 assembly...

 

It is doing a jump on condition that the compare was "less than". What did it actually compare there?

Share this post


Link to post
Share on other sites
Here's my understanding of it (correct me if I'm wrong anywhere):

C6A708h is your EXE/DLL's _tls_index (the stored TlsAlloc return value that the startup code (MSVCRT?) reserved for your EXE/DLL's thread local storage index. It allows EXEs and the DLLs to know where their own dedicated area of the TLS is.)

ecx = fs:[2Ch] is the Win32-specific way to get the thread's TLS pointer.
edx = [ecx+eax*4] is looking up your EXE/DLL-specific area of thread local storage using the _tls_index.
[edx+104h] is accessing a member of the structure it points at. The constant offset is determined at link time - each thread static gets a different constant. The earlier use of _tls_index is what allows this to be constant.

C6A1E0h is the address of a variable indicating whether it has performed initialization yet or not.

I'm not familiar with the __Init_thread_header/footer functions.

C6A1DCh is the address of your actual static variable. Edited by Nypyren

Share this post


Link to post
Share on other sites
Yes, it is instantiated the first time it is hit.

However, it is still a bad practice generally for many reasons.

It is poor management of object lifetime since you aren't really controlling when it gets created and you do not have any control over when it gets cleaned up.

It is a hidden dependency that cannot be substituted out; there is no way to make another object derived from it for use, no way to make a proxy or fake object for testing, no way to extend the type.

It will likely expose mutable state and mutable data. Some patterns of shared items can work if you have strict policies that keep the data immutable. Said differently, you need to be certain no code changes values at unexpected times. You need strict rules to ensure the values are always what you expect. It is a tradeoff made by many games to have a well-known object but to strictly control what happens with it and when that happens Even with strict controls this is a common source of bugs. Don't make that tradeoff without considering the problems.


Usually a much better alternative if you are still going with a common instance is to have a global pointer to an instance. And instead of an actual concrete type it should usually follow the dependency inversion rule, using an abstract type that includes all the functions you need that is also implemented by the RenderManager class. This lets you still have your easy-to-find object for use everywhere, but also lets you substitute it out for testing or extension, and lets you control exactly when it gets created and destroyed.

Share this post


Link to post
Share on other sites
To hopefully put this in easier to read terms...

The compiler does the equivalent of this (not valid C++ code):
 
static bool __renderManagerInitialized = false;
static RenderManager& RenderManager::GetInstance()
{
   //static RenderManager renderManager;
   if (!__renderManagerInitialized)
   {
       new (&renderManager) RenderMananger(); // placement-new if you haven't seen it, creates an object at a pre-allocated memory location
       __renderManagerInitialized;
   }
   return renderManager;
}
Depending on your compiler, it may or may not implement this in a thread-safe manner. C++11 requires thread-safe static variable initialization, but check your compiler to see if it supports it (i.e. MSVC doesn't support this until 2013 or later)

Edit: Frob's points are also very valid - you should avoid singletons wherever possible, and, if you do use them, try to make sure you specifically set up the order in which they are created, usually via static Init/Shutdown functions on the class that manipulate a pointer that the QInstance() can return. Edited by SmkViper

Share this post


Link to post
Share on other sites

While I agree with you frob, I was merely looking to understand what happens in the code for statics in functions.

 

 

 

C6A1E0h is the address of a variable indicating whether it has performed initialization yet or not.

 

That's what I was wondering. It incurs a cost of a 'flag' variable indicating it was initialized. That is the only thing I could think it would compare.

 

Thanks.

Share this post


Link to post
Share on other sites

Depending on your compiler, it may or may not implement this in a thread-safe manner. C++11 requires thread-safe static variable initialization, but check your compiler to see if it supports it (i.e. MSVC doesn't support this until 2013 or later)

Edit: Frob's points are also very valid - you should avoid singletons wherever possible, and, if you do use them, try to make sure you specifically set up the order in which they are created, usually via static Init/Shutdown functions on the class that manipulate a pointer that the QInstance() can return.

 
Just as an addendum to this: the introduction of thread-safe statics introduces a new reason for this pattern to be a bad practice. Consider what happens if RenderManager::GetInstance() is called from somewhere inside of RenderManager's constructor. Then you have a callstack that looks like:

>RenderManager::GetInstance()
>ThingThatNeedsTheRenderer()
>...
>RenderManager::RenderManager()
>RenderManager::GetInstance()

Then, because in some implementations RenderManager::GetInstance() locks a mutex to be sure that only one thread can access __renderManagerInitialized at a time, the second call will see that the singleton is both not constructed and the mutex needed to access it is locked, causing the thread that initially called RenderManager::GetInstance() to deadlock. Edited by Oberon_Command

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!