### Slides - Informatik 10

```Statics in Depth
Markus Götze
FAU Erlangen Nürnberg
20.06.2011
Motivation (1)
static int someVar;
Personal flashback → C:
void incSomeVar();
Variable whose value must be remembered
int main()
{
incSomeVar();
//...
incSomeVar();
//...
printf("%d\n", someVar);
exit(0);
}
Global Static Variable
void incSomeVar()
{
++someVar;
}
2
Motivation (2)
This may raise some questions:
namespace nSpace
{
inline int retFour()
{
return 4;
}
int
int
int
int
global1 = 0;
global2;
global3 = 42;
global4 = retFour();
• global1 → clear
• what value does global2 have?
• when is global3 set to 42?
• will initialization of global4 even result in a
compiler error?
}
3
Outline
• Motivation
• Problems
• Solutions approaches
• Conclusion
4
Outline
• Motivation
• Problems
• Order of Construction
• Race conditions with Dynamic Initialization of Static Variable
• Solutions approaches
• Conclusion
5
Problems – Order of Construction (1)
Class MyClass;
extern MyClass myFourthClass;
MyClass myFirstClass(“1”);
MyClass mySecondClass(myFirstClass);
MyClass myThirdClass(myFourthClass);
MyClass myFourthClass(“4“);
• Objects are created by order of definition, not declaration
6
Problems – Order of Construction (2)
class MyClass
{
private:
int i;
public:
MyClass(int num)
{
std::cout << "In Constructor" << std::endl;
i = num;
}
}
• one famous static object is std::cout
• does cout exist when static object MyClass is created?
7
//myClass1.cpp
MyClass myFirstClass();
MyClass mySecondClass();
//myClass2.cpp
MyClass myThirdClass();
//myClass3.cpp
MyClass myFourthClass();
Order
Borland C/C++ 5.6
1, 2, 3, 4
Code Warrior 8
1, 2, 3, 4
Digital Mars 8.38
4, 3, 1, 2
GCC 3.2
4, 3, 1, 2
Intel C/C++ 6.0
1, 2, 3, 4
Visual C++ 6.0
1, 2, 3, 4
Watcom C/C++ 12
4, 3, 1, 2
//Makefile
myClass:
\$(CXX) myClass1.cpp myClass2.cpp myClass3.cpp -o myClass
8
Problems – Race Conditions (1)
• Especially static members result in race conditions
• The following example will show what can go wrong in
detail:
int calculateResult()
{
static int result =
calculateSomething();
return result;
}
int calculateResult()
{
static bool result_computed = false;
static int result;
if (!result_computed)
{
result_computed = true;
result = calculateSomething();
}
return result;
}
• If one thread will be pre-empted after having set result_computed to true,
next thread will pass if-block and see uninitialized variable
9
Problems – Race Conditions (2)
class MyClass {...} ;
int calculateResult()
{
static MyClass myClass;
return myClass.
calculateSomething();
}
class MyClass {...} ;
int calculateResult()
{
static bool myClass_constructed = false;
static MyClass myClass; // uninitialized
if (!myClass_constructed)
{
myClass.constructed = true;
new(&myClass) myClass;
atexit(DestructmyClass)
}
return myClass.calculateSomething();
}
• as before, myClass could be used before it has been initialized
• additionally, myClass could be double-constructed and double-destructed
10
Problems – Race Conditions (3)
class MyClass { ... } ;
int calculateResult()
{
static MyClass mC1;
static MyClass mC2;
return mC1.calculateSomething() +
mC2.calculateSomething();
}
• one bitfield for both _constructed
variables
• multiple RMS-operations on the same
data-structure
• still race conditions here
class MyClass { ... };
int calculateResult()
{
static char constructed = 0;
static uninitialized MyClass mC1;
if (!(constructed & 1)) {
constructed |= 1;
new(&mC1) MyClass;
atexit(DestructmC1);
}
static uninitialized MyClass mC2;
if (!(constructed & 2)) {
constructed |= 2;
new(&mC2) MyClass;
atexit(DestructmC2);
}
return mC1.calculateSomething() +
mC2.calculateSomething();
}
11
Problems – Race Conditions (4)
int calculateResult()
{
lock();
static int result = calculateSomething();
unlock();
return result;
}
• Proposition: put computation in a synchronized block
• Problem: What if calculateSomething() somewhere somehow calls
calculateResult() ?
→
the thread already has the lock, may enter the critical section and
once again see an uninitialized object result
12
Outline
• Motivation
• Problems
• Solutions approaches
• Conclusion
13
Outline
• Motivation
• Problems
• Solutions approaches
•
•
•
•
Avoid statics when possible
Singletons
Schwarz Counter
Fight race-conditions
• Conclusion
14
Solution approaches – Avoid Statics
• Generally speaking, avoid static when you can!
• Use references as parameters instead of global variables
static MyClass myClass;
void someFunc()
{
myClass.doSomething();
}
int main()
{
//...
someFunc();
}
void someFunc(MyClass *myClass)
{
myClass->doSomething();
}
int main()
{
MyClass myClass;
someFunc(&myClass);
}
15
Solution approaches – Avoid Statics
• Generally speaking, avoid static when you can!
• Change global objects to stack objects within main (kind
of a hack)
• We gain full control over lifetime, but have to use all
global variables by pointer
• Loss in efficiency
• Inconvenient syntax
//MyClass1.h
class MyClass1
{
//...
void doSomething() {...}
};
extern MyClass1 *p_myClass1;
//main.cpp
#include "MyClass1.h"
#include "MyClass2.h"
int main()
{
MyClass myClass;
p_myClass = &myClass;
p_myClass->doSomething();
return 0;
}
16
Solution Approaches – Singleton (1)
• Looking back at techniques we already learned, one idiom
lets us gain control over lifetime
• Singleton!
• Let‘s look back for a short moment:
17
Solution Approaches – Singleton (2)
Meyers Singleton
class MyClass
{
public:
MyClass &getInstance()
{
static MyClass instance;
return instance;
}
private:
MyClass(const MyClass &other);
MyClass &operator =(const MyClass &other);
}
• Object is created first time getInstance() is called
→ we have control over creation time of object
• we still do not have control over destruction time; only guarantee: „during process
shutdown“
• local static object -> race conditions!
18
Solution Approaches – Singleton (3)
Alexandrescu Singleton
class MyClass
{
public:
MyClass &getInstance()
{
if (instance == null)
{
instance = new MyClass();
Infrastructure::register(instance, ...);
}
return *instance;
}
private:
static MyClass *instance;
MyClass(const MyClass &other);
MyClass &operator =(const MyClass &other);
}
19
Solution Approaches – Singleton (3)
Alexandrescu Singleton
• by providing the GetLongevity methods, the programmer can specify the
relative destruction order
• Downsides:
• a lot of effort if new Singletons are inserted into the system
• „easy to get it wrong“
• errors are very hard to detect
inline unsigned int GetLongevity(MyClass *) { return 2; }
20
Solution Approaches – Schwarz Counter (1)
//MyClass.h
class MyClass{...};
extern MyClass *p_myClass;
class MyClass_init
{
private:
static int init_count; //zero-initialized
public:
MyClass_init();
~MyClass_init();
};
static MyClass_init
myClass_init;
// file static
scope
namespace
{ MyClass_init
myClass_init;
} //anonymous
namespace
• myClass_init has file static scope (anonymous namespace), so there will exist as
many variables as files that include MyClass.h
• only one init_count
21
Solution Approaches – Schwarz Counter (2)
//MyClass_init c'tor und d'tor
MyClass *p_myClass;
MyClass_init::MyClass_init()
{
if (++init_count > 0)
return;
p_myClass = new MyClass;
// other needed initializations
}
MyClass_init::~MyClass_init()
{
if (--init_counter > 0)
return;
delete p_myClass;
// other destruction
}
• use a reference count to gain control over time of destruction
22
Solution Approaches – Schwarz Counter (3)
// OtherClass.h
#include "MyClass.h"
class OtherClass
{
private:
MyClass myClass;
public:
// ...
};
• Classes must be declared before objects or members of that type can be
declared, meaning:
• MyClass.h is included above class definition of OtherClass
• Declaration of MyClass_init will therefore always happen before any
OtherClass object is created
• since construction order is reversed for destruction, MyClass_init will
always be destructed after OtherClass
23
Solution Approaches – Schwarz Counter (4)
• Special Case: class C wants to make use of MyClass
without containing any instance of it
• For example: Class wants to use I/O without containing a
stream object
//C.h
//...
class C_init
{
private:
static int init_count;
public:
C_init();
~C_init();
};
static C_init C_init;
24
Solution Approaches – Schwarz Counter (5)
//C_init c'tor and d'tor
#include "C.h"
#include "MyClass.h"
static MyClass_init *minit;
C_init::C_init()
{
if (++init_count > 1)
return;
minit = new MyClass_init;
}
C::init::~C_init()
{
if (--init_counter > 0)
delete minit;
}
• C_init is declared higher in file than any declaration
of C, so C_init constructor will be called before C
constructor
• That guarantees that MyClass will be initialized by
MyClass_init before any C constructor
25
Solution approaches – Fighting race conditions (1)
• Going back to race conditions
MyClass &getInstance()
{
static MyClass myClass;
return myClass;
}
MyClass &getInstance()
{
static bool __bMyClassInitialized__ = false;
static byte __myClassBytes__[sizeof(MyClass)];
if (!__bMyClassInitialized__)
{
new(__myClassBytes__) MyClass();
__bMyClassInitialized__ = true;
}
return *reinterpret_cast<MyClass*>
(__myClassBytes__);
}
• two or more threads could see __bMyClassInitiliazed__ is false and go on to
construct it
26
Solution approaches – Fighting race conditions (2)
• Best solution here: use a spinlock
MyClass &getInstance()
{
static int guard; // zero-initialized
spin_mutex(&guard);
lock_scope<spin_mutex> lock(smx);
static MyClass instance;
return instance;
}
• use of spinlock is costly, but only if:
• high degree of quarrel
• guarded section is long
• both cases here are tolerable
27
Outline
• Motivation
• Problems
• Solutions approaches
• Conclusion
28
Outline
• Motivation
• Problems
• Solutions approaches
• Conclusion
29
Conclusion
• Static objects bring along very serious problems!
• Hence avoid them where you can!
• If you must use them, remember to solve the two
problems for your very special problem
• Construction order
• Possible solutions to these problems may be
• Singletons, Schwarz Counter
• Suitable lock mechanisms, for example spinlock
30
Questions?
31
References
• Matthew Wilson “Imperfect C++, Practical Solutions for
Real-Life Programming”
• Stephen C. Dewhurst “C++ Gotchas, Avoiding Common
Problems in Coding and Design”
• Stanley B. Lippman “C++ Gems”
• http://blogs.msdn.com/b/oldnewthing/archive/2004/03/
08/85901.aspx
• http://en.allexperts.com/q/C-1040/Constructors-GlobalObject.htm
• Florian Krautwurm „C++ Design Patterns: Singleton in
Detail“
32
```