Statics in depth
Download
1 / 32

Statics in Depth - PowerPoint PPT Presentation


  • 150 Views
  • Uploaded on

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

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about 'Statics in Depth' - aden


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.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
Statics in depth

Statics in Depth

Markus Götze

FAU Erlangen Nürnberg

Seminar Advanced C++ Programming

20.06.2011


Motivation 1
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
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
Outline

  • Motivation

  • Problems

  • Solutions approaches

  • Conclusion


Outline1
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
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
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
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
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
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
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
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


Outline2
Outline

  • Motivation

  • Problems

  • Solutions approaches

  • Conclusion


Outline3
Outline

  • Motivation

  • Problems

  • Solutions approaches

    • Avoid statics when possible

    • Singletons

    • Schwarz Counter

    • Fight race-conditions

  • Conclusion


Solution approaches avoid statics
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 statics1
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
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
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
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 31
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
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
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
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
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
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
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
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


Outline4
Outline

  • Motivation

  • Problems

  • Solutions approaches

  • Conclusion


Outline5
Outline

  • Motivation

  • Problems

  • Solutions approaches

  • Conclusion


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



References
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“