330 likes | 412 Views
Statics in Depth. Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011. Motivation (1). Personal flashback → C : Variable whose value must be remembered Global Static Variable. static int someVar ; void incSomeVar (); int main() { incSomeVar (); //...
E N D
Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011
Motivation (1) Personal flashback → C: Variable whose value must be remembered Global Static Variable staticintsomeVar; voidincSomeVar(); intmain() { incSomeVar(); //... incSomeVar(); //... printf("%d\n",someVar); exit(0); } voidincSomeVar() { ++someVar; }
Motivation (2) This may raise some questions: namespacenSpace { inlineintretFour() { return4; } intglobal1=0; intglobal2; intglobal3=42; intglobal4=retFour(); } • global1 → clear • what value does global2 have? • when is global3 set to 42? • will initialization of global4 even result in a • compiler error?
Outline • Motivation • Problems • Solutions approaches • Conclusion
Outline • Motivation • Problems • Order of Construction • Compiler/Linker Depended Ordering • Race conditions with Dynamic Initialization of Static Variable • Solutions approaches • Conclusion
Problems – Order of Construction (1) ClassMyClass; externMyClassmyFourthClass; MyClassmyFirstClass(“1”); MyClassmySecondClass(myFirstClass); MyClassmyThirdClass(myFourthClass); MyClassmyFourthClass(“4“); • Objects are created by order of definition, not declaration
Problems – Order of Construction (2) classMyClass { private: inti; public: MyClass(intnum) { std::cout<<"In Constructor"<<std::endl; i=num; } } • one famous static object is std::cout • does cout exist when static object MyClass is created?
Problems – Compiler/Linker dependant ordering //myClass1.cpp MyClassmyFirstClass(); MyClassmySecondClass(); //myClass2.cpp MyClassmyThirdClass(); //myClass3.cpp MyClassmyFourthClass(); //Makefile myClass: $(CXX)myClass1.cppmyClass2.cppmyClass3.cpp-omyClass
Problems – Race Conditions (1) • Especially static members result in race conditions • The following example will show what can go wrong in detail: intcalculateResult() { staticboolresult_computed=false; staticintresult; if(!result_computed) { result_computed=true; result=calculateSomething(); } returnresult; } intcalculateResult() { staticintresult= calculateSomething(); returnresult; } • If one thread will be pre-empted after having set result_computed to true, • next thread will pass if-block and see uninitialized variable
Problems – Race Conditions (2) classMyClass{...}; intcalculateResult() { staticboolmyClass_constructed=false; staticMyClassmyClass;// uninitialized if(!myClass_constructed) { myClass.constructed=true; new(&myClass)myClass; atexit(DestructmyClass) } returnmyClass.calculateSomething(); } classMyClass{...}; intcalculateResult() { staticMyClassmyClass; returnmyClass. calculateSomething(); } • as before, myClass could be used before it has been initialized • additionally, myClass could be double-constructed and double-destructed
Problems – Race Conditions (3) classMyClass{...}; intcalculateResult() { staticcharconstructed=0; staticuninitializedMyClassmC1; if(!(constructed&1)){ constructed|=1; new(&mC1)MyClass; atexit(DestructmC1); } staticuninitializedMyClass mC2; if(!(constructed&2)){ constructed|=2; new(&mC2)MyClass; atexit(DestructmC2); } returnmC1.calculateSomething()+mC2.calculateSomething(); } classMyClass{...}; intcalculateResult() { staticMyClassmC1; staticMyClassmC2; returnmC1.calculateSomething() +mC2.calculateSomething(); } • one bitfield for both _constructed • variables • multiple RMS-operations on the same • data-structure • still race conditions here
Problems – Race Conditions (4) intcalculateResult() { lock(); staticintresult=calculateSomething(); unlock(); returnresult; } • 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
Outline • Motivation • Problems • Solutions approaches • Conclusion
Outline • Motivation • Problems • Solutions approaches • Avoid statics when possible • Singletons • Schwarz Counter • Fight race-conditions • Conclusion
Solution approaches – Avoid Statics • Generally speaking, avoid static when you can! • Use references as parameters instead of global variables staticMyClassmyClass; voidsomeFunc() { myClass.doSomething(); } intmain() { //... someFunc(); } voidsomeFunc(MyClass*myClass) { myClass->doSomething(); } intmain() { MyClassmyClass; someFunc(&myClass); }
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 //main.cpp #include "MyClass1.h" #include "MyClass2.h" intmain() { MyClassmyClass; p_myClass=&myClass; p_myClass->doSomething(); return0; } //MyClass1.h classMyClass1 { //... voiddoSomething(){...} }; externMyClass1*p_myClass1;
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:
Solution Approaches – Singleton (2) Meyers Singleton classMyClass { public: MyClass&getInstance() { staticMyClassinstance; returninstance; } private: MyClass(constMyClass&other); MyClass&operator=(constMyClass&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“ • This may lead to Dead Reference Problem • local static object -> race conditions!
Solution Approaches – Singleton (3) Alexandrescu Singleton classMyClass { public: MyClass&getInstance() { if(instance==null) { instance=newMyClass(); Infrastructure::register(instance,...); } return*instance; } private: staticMyClass*instance; MyClass(constMyClass&other); MyClass&operator=(constMyClass&other); } →
Solution Approaches – Singleton (3) Alexandrescu Singleton • This singleton can be made thread-safe • 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 inlineunsignedintGetLongevity(MyClass*){return2;}
Solution Approaches – Schwarz Counter (1) //MyClass.h classMyClass{...}; externMyClass*p_myClass; classMyClass_init { private: staticintinit_count;//zero-initialized public: MyClass_init(); ~MyClass_init(); }; staticMyClass_initmyClass_init;// file static scope namespace{MyClass_initmyClass_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
Solution Approaches – Schwarz Counter (2) //MyClass_initc'tor und d'tor MyClass*p_myClass; MyClass_init::MyClass_init() { if(++init_count>0) return; p_myClass=newMyClass; // other needed initializations } MyClass_init::~MyClass_init() { if(--init_counter>0) return; deletep_myClass; // other destruction } • use a reference count to gain control over time of destruction • this needs to be made thread-safe (like Alexandrescu Singleton)
Solution Approaches – Schwarz Counter (3) // OtherClass.h #include "MyClass.h" classOtherClass { private: MyClassmyClass; 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
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 //... classC_init { private: staticintinit_count; public: C_init(); ~C_init(); }; staticC_initC_init;
Solution Approaches – Schwarz Counter (5) //C_initc'tor and d'tor #include "C.h" #include "MyClass.h" staticMyClass_init*minit; C_init::C_init() { if(++init_count>1) return; minit=newMyClass_init; } C::init::~C_init() { if(--init_counter>0) deleteminit; } • 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
Solution approaches – Fighting race conditions (1) • Going back to race conditions MyClass&getInstance() { staticbool__bMyClassInitialized__=false; staticbyte__myClassBytes__[sizeof(MyClass)]; if(!__bMyClassInitialized__) { new(__myClassBytes__)MyClass(); __bMyClassInitialized__=true; } return*reinterpret_cast<MyClass*> (__myClassBytes__); } MyClass&getInstance() { staticMyClassmyClass; returnmyClass; } • two or more threads could see __bMyClassInitiliazed__ is false and go on to • construct it
Solution approaches – Fighting race conditions (2) MyClass&getInstance() { staticintguard;// zero-initialized spin_mutex(&guard); lock_scope<spin_mutex>lock(smx); staticMyClassinstance; returninstance; } • Best solution here: use a spinlock • use of spinlock is costly, but only if: • high degree of quarrel • guarded section is long • both cases here are tolerable
Outline • Motivation • Problems • Solutions approaches • Conclusion
Outline • Motivation • Problems • Solutions approaches • Conclusion
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 • Thread safety • Possible solutions to these problems may be • Singletons, Schwarz Counter • Suitable lock mechanisms, for example spinlock
Thank you for your attention Questions?
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-Global-Object.htm • Florian Krautwurm „C++ Design Patterns: Singleton in Detail“