1 / 32

Statics in Depth

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 (); //...

aden
Télécharger la présentation

Statics in Depth

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

  2. 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; }

  3. 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?

  4. Outline • Motivation • Problems • Solutions approaches • Conclusion

  5. Outline • Motivation • Problems • Order of Construction • Compiler/Linker Depended Ordering • Race conditions with Dynamic Initialization of Static Variable • Solutions approaches • Conclusion

  6. Problems – Order of Construction (1) ClassMyClass; externMyClassmyFourthClass; MyClassmyFirstClass(“1”); MyClassmySecondClass(myFirstClass); MyClassmyThirdClass(myFourthClass); MyClassmyFourthClass(“4“); • Objects are created by order of definition, not declaration

  7. 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?

  8. Problems – Compiler/Linker dependant ordering //myClass1.cpp MyClassmyFirstClass(); MyClassmySecondClass(); //myClass2.cpp MyClassmyThirdClass(); //myClass3.cpp MyClassmyFourthClass(); //Makefile myClass: $(CXX)myClass1.cppmyClass2.cppmyClass3.cpp-omyClass

  9. 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

  10. 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

  11. 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

  12. 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

  13. Outline • Motivation • Problems • Solutions approaches • Conclusion

  14. Outline • Motivation • Problems • Solutions approaches • Avoid statics when possible • Singletons • Schwarz Counter • Fight race-conditions • Conclusion

  15. 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); }

  16. 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;

  17. 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:

  18. 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!

  19. 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); } →

  20. 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;}

  21. 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

  22. 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)

  23. 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

  24. 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;

  25. 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

  26. 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

  27. 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

  28. Outline • Motivation • Problems • Solutions approaches • Conclusion

  29. Outline • Motivation • Problems • Solutions approaches • Conclusion

  30. 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

  31. Thank you for your attention Questions?

  32. 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“

More Related