statics in depth markus götze fau erlangen nürnberg seminar advanced c++ programming 20.06.2011

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

Post on 19-Dec-2015

220 views

Category:

Documents


0 download

TRANSCRIPT

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

Statics in Depth

Markus GötzeFAU Erlangen Nürnberg

Seminar Advanced C++ Programming20.06.2011

Page 2: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

2

Motivation (1)

static int someVar;

void incSomeVar();

int main(){ incSomeVar(); //... incSomeVar(); //... printf("%d\n", someVar); exit(0);}

void incSomeVar(){ ++someVar;}

Personal flashback → C:

Variable whose value must be remembered

Global Static Variable

Page 3: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

3

Motivation (2)

namespace nSpace{ inline int retFour() { return 4; } int global1 = 0; int global2; int global3 = 42; int global4 = retFour();}

This may raise some questions:

• global1 → clear

• what value does global2 have?

• when is global3 set to 42?

• will initialization of global4 even result in a compiler error?

Page 4: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

4

Outline

• Motivation

• Problems

• Solutions approaches

• Conclusion

Page 5: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

5

Outline

• Motivation

• Problems• Order of Construction• Compiler/Linker Depended Ordering• Race conditions with Dynamic Initialization of Static Variable

• Solutions approaches

• Conclusion

Page 6: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

6

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

Page 7: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

7

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?

Page 8: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

8

Problems – Compiler/Linker dependant ordering

//myClass1.cppMyClass myFirstClass();MyClass mySecondClass();

//myClass2.cppMyClass myThirdClass();

//myClass3.cppMyClass myFourthClass();

//MakefilemyClass: $(CXX) myClass1.cpp myClass2.cpp myClass3.cpp -o myClass

Compiler/Linker 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

Page 9: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

9

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

Page 10: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

10

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

Page 11: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

11

Problems – Race Conditions (3)

class MyClass { ... } ;int calculateResult(){ static MyClass mC1; static MyClass mC2; return mC1.calculateSomething() +

mC2.calculateSomething();}

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();}

• one bitfield for both _constructed variables

• multiple RMS-operations on the same data-structure

• still race conditions here

Page 12: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

12

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

Page 13: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

13

Outline

• Motivation

• Problems

• Solutions approaches

• Conclusion

Page 14: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

14

Outline

• Motivation

• Problems

• Solutions approaches• Avoid statics when possible• Singletons• Schwarz Counter• Fight race-conditions

• Conclusion

Page 15: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

15

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

Page 16: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

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"

int main(){ MyClass myClass; p_myClass = &myClass; p_myClass->doSomething(); return 0;}

//MyClass1.hclass MyClass1{ //... void doSomething() {...}};extern MyClass1 *p_myClass1;

Page 17: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

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:

Page 18: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

18

Solution Approaches – Singleton (2)

class MyClass{ public: MyClass &getInstance() { static MyClass instance; return instance; } private: MyClass(const MyClass &other); MyClass &operator =(const MyClass &other);}

Meyers Singleton

• 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!

Page 19: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

19

Solution Approaches – Singleton (3)

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

Alexandrescu Singleton

Page 20: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

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

inline unsigned int GetLongevity(MyClass *) { return 2; }

Page 21: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

21

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

• 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

namespace { MyClass_init myClass_init; } //anonymous namespace

Page 22: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

22

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• this needs to be made thread-safe (like Alexandrescu Singleton)

Page 23: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

23

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

Page 24: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

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

//...

class C_init { private: static int init_count; public: C_init(); ~C_init();};static C_init C_init;

Page 25: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

25

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

Page 26: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

26

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

Page 27: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

27

Solution approaches – Fighting race conditions (2)

• Best solution here: use a spinlockMyClass &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

Page 28: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

28

Outline

• Motivation

• Problems

• Solutions approaches

• Conclusion

Page 29: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

29

Outline

• Motivation

• Problems

• Solutions approaches

• Conclusion

Page 30: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

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

Page 31: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

31

Thank you for your attentionQuestions?

Page 32: Statics in Depth Markus Götze FAU Erlangen Nürnberg Seminar Advanced C++ Programming 20.06.2011

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“