Implementing the Singleton pattern in C++ Klaus Sembritzki FAU Erlangen N¨ urnberg

Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Implementing the Singleton pattern in C++
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Klaus Sembritzki
FAU Erlangen Nürnberg
7. Juni 2010
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
1 / 39
Table of Contents
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
1 Introduction
Definition
Where to use it
2 Log class
Static members
Static instance
Static instance pointer
3 Pure Singleton implementations
Static instance pointer
Meyers Singleton
Phoenix Singleton
4 Multithreaded access
Coherent cache
Non-coherent cache
Compiler-dependent solutions
5 Resources
2 / 39
What is a Singleton
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Definition
A Singleton is a class/object that has at most one instance, that one
instance can be accessed globally.
More requirements
Singleton creates itself when used for the first time.
Singleton destroys itself some time.
⇒ A little bit more than a global variable
Resources
3 / 39
Warning
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Singletons considered harmful (compare to global variables)
Refactoring required when more than one instance needed
Methods should only work on input arguments
Accesses scattered all over the application
Use Singleton if
You will never need more than one instance
Passing a parameter would be a pain
Accesses will be scattered anyway
Resources
4 / 39
Usage examples
Implementing the
Singleton pattern
in C++
Provide access to hardware ressources as they only exists once
Klaus Sembritzki
Keyboard
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Display
System clock
Other examples
Print manager
Database access
Log file
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Anti-pattern ?
Singletons are used very frequently
Resources
5 / 39
Table of Contents
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
1 Introduction
Definition
Where to use it
2 Log class
Static members
Static instance
Static instance pointer
3 Pure Singleton implementations
Static instance pointer
Meyers Singleton
Phoenix Singleton
4 Multithreaded access
Coherent cache
Non-coherent cache
Compiler-dependent solutions
5 Resources
6 / 39
Static members
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
1
2
3
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
4
5
6
7
8
9
10
11
12
// Header f i l e L o g F i l e . h
class LogFile {
public :
s t a t i c v o i d w r i t e ( c o n s t s t d : : s t r i n g &message ) {
f i l e << message << s t d : : e n d l ;
}
s t a t i c void w r i t e S e p a r a t o r L i n e () {
f i l e << ”−−−−−” << s t d : : e n d l ;
}
private :
static std : : ofstream f i l e ;
};
13
14
15
// S o u r c e f i l e L o g F i l e . cpp
s t d : : o f s t r e a m L o g F i l e : : f i l e ( ” Log1 . t x t ” ) ;
16
17
18
// S o u r c e f i l e Main . cpp
LogFile : : w r i t e ( ” a l l your base ” ) ;
7 / 39
Static members (2)
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Problems
Time of initialization unclear
Introduction
Definition
Where to use it
Log class
Time of destruction unclear
Static members
Static instance
Static instance
pointer
Hard to check for errors (did file-open succeed?)
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
1
2
3
s t d : : o f s t r e a m L o g F i l e : : f i l e ( ” Log1 . t x t ” ) ;
// I n i t i a l i z a t i o n o f s o m e O b j e c t d e p e n d s on f i l e
SomeClass L o g F i l e : : someObject ( L o g F i l e : : f i l e ) ;
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
8 / 39
Static instance
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Header f i l e Log . h
c l a s s Log {
public :
s t a t i c Log& i n s t a n c e ( ) {
return inst ance ;
}
v o i d w r i t e ( c o n s t s t d : : s t r i n g &message ) { . . . }
private :
Log ( ) : f i l e ( ” Log3 . t x t ” ) {}
s t a t i c Log i n s t a n c e ;
std : : ofstream f i l e ;
};
// S o u r c e f i l e Log . cpp
Log Log : : i n s t a n c e ;
// S o u r c e f i l e Main . cpp
Log : : i n s t a n c e ( ) . w r i t e ( ”msg” ) ;
Resources
9 / 39
Static instance (2)
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Problems
Time of initialization unclear
Time of destruction unclear
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Consequence of unclear initialization time
Log constructor accesses another Singleton object
⇒ might use uninitialized data
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
10 / 39
Static instance pointer
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
1
2
3
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Header f i l e Log . h
c l a s s Log {
public :
s t a t i c Log& i n s t a n c e ( ) {
i f ( i n s t a n c e == 0 )
i n s t a n c e = new Log ( ) ;
return ∗ instance ;
}
v o i d w r i t e ( c o n s t s t d : : s t r i n g &message ) { . . . }
private :
Log ( ) : f i l e ( ” Log4 . t x t ” ) {}
s t a t i c Log ∗ i n s t a n c e ;
std : : ofstream f i l e ;
};
// S o u r c e f i l e Log . cpp
Log ∗ Log : : i n s t a n c e = 0 ;
// S o u r c e f i l e Main . cpp
Log : : i n s t a n c e ( ) . w r i t e ( ” h e l l o ” ) ;
11 / 39
Table of Contents
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
1 Introduction
Definition
Where to use it
2 Log class
Static members
Static instance
Static instance pointer
3 Pure Singleton implementations
Static instance pointer
Meyers Singleton
Phoenix Singleton
4 Multithreaded access
Coherent cache
Non-coherent cache
Compiler-dependent solutions
5 Resources
12 / 39
Improvements
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Enforce uniqueness, prevent clients from
calling the Singleton’s default constructor
using the copy constructor
using the assignment operator (only one Singleton object can
exist)
Automatically destroy on program exit
Disallow destruction
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
13 / 39
Static instance pointer
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// H e a d e r f i l e S i n g l e t o n . h
class Singleton {
public :
s t a t i c S i n g l e t o n& i n s t a n c e ( ) {
i f ( i n s t a n c e == 0 ) {
i n s t a n c e = new S i n g l e t o n ( ) ;
s t d : : a t e x i t (& d e l e t e S i n g l e t o n ) ;
}
return ∗ instance ;
}
private :
S i n g l e t o n ( ) {}
S i n g l e t o n ( c o n s t S i n g l e t o n &) ;
S i n g l e t o n &o p e r a t o r =( c o n s t S i n g l e t o n &) ;
˜ S i n g l e t o n ( ) {}
s t a t i c void d e l e t e S i n g l e t o n () {
delete instance ;
}
static Singleton ∗instance ;
};
// S o u r c e f i l e S i n g l e t o n . cpp
Singleton∗ Singleton : : instance = 0;
Resources
14 / 39
Meyers Singleton
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
1
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
2
3
4
5
6
7
8
9
10
11
12
13
// Header f i l e S i n g l e t o n . h
class Singleton {
public :
s t a t i c S i n g l e t o n& i n s t a n c e ( ) {
static Singleton instance ;
return instance ;
}
private :
S i n g l e t o n ( ) {}
S i n g l e t o n ( c o n s t S i n g l e t o n &) ;
S i n g l e t o n &o p e r a t o r =( c o n s t S i n g l e t o n &) ;
˜ S i n g l e t o n ( ) {}
};
Resources
15 / 39
Atexit contradiction
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
1
v o i d Log : : d e s t r o y ( ) {}
2
3
4
5
6
void Display : : destroy () {
Log : : i n s t a n c e ( )
a t e x i t (&Log : : d e s t r o y ) ;
}
7
8
9
10
11
v o i d main ( ) {
Display : : instance ()
a t e x i t (& D i s p l a y : : d e s t r o y ) ;
}
Contradiction
According to standard: last in, first called
Not possiple here
⇒ Some compilers crash, do not call bar, ... (g++ 4.3.4 worked)
Resources
16 / 39
Dead reference problem
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
atexit and static destructors work in LIFO order.
Introduction
Definition
Where to use it
Program with two classes, Display (access to rendering device) and
the Log class
Log class
Static members
Static instance
Static instance
pointer
1
Pure Singleton
implementations
3
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
2
4
5
6
Display : : instance ()
a t e x i t (& D i s p l a y : : d e s t r o y ) ;
Log : : i n s t a n c e ( )
a t e x i t (&Log : : d e s t r o y ) ;
˜ Log ( )
˜ Display ()
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
˜Display() writes to log ⇒ undefined behaviour
Resources
17 / 39
Dead reference problem, throw exception
Detect the dead reference and throw an exception
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
1
2
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
3
4
5
6
7
8
9
10
11
12
13
// S i n g l e t o n . h
class Singleton {
public :
s t a t i c S i n g l e t o n& i n s t a n c e ( ) {
static Singleton inst ;
i f ( destroyed )
throw s t d : : r u n t i m e e r r o r ( ”Dead r e f e r e n c e
detected ”) ;
return i n s t ;
}
private :
˜ Singleton () { destroyed = true ; }
s t a t i c bool d e s t r o y e d ;
};
14
15
16
// S i n g l e t o n . cpp
bool S i n g l e t o n : : d e s t r o y e d
= false ;
18 / 39
Dead reference problem, Phoenix Singleton
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// S i n g l e t o n . h
class Singleton {
public :
s t a t i c S i n g l e t o n& i n s t a n c e ( ) {
static Singleton inst ;
i n s t a n c e = &i n s t ;
i f ( destroyed ) {
new ( i n s t a n c e ) S i n g l e t o n ( ) ;
std : : a t e x i t ( destroySingleton ) ;
destroyed = false ;
}
return inst ;
}
private :
˜ Singleton () { destroyed = true ; }
s t a t i c v o i d d e s t r o y S i n g l e t o n ( ) { i n s t a n c e −>˜S i n g l e t o n ( ) ; }
s t a t i c bool destroyed ;
static Singleton ∗instance ;
};
// S i n g l e t o n . cpp
Singleton ∗Singleton : : instance = 0;
bool Singleton : : destroyed = f a l s e ;
Resources
19 / 39
Dead reference problem, Phoenix Singleton (2)
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Remember the Log example
Constructor clears whole log file.
⇒ Repeated Phoenix constructor calls will delete logging
information.
Possible solutions
Priority based atexit function (non-standard)
Create Singletons manually at start of main() (hack)
Destroy manually at the end of main() (hack)
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
20 / 39
Table of Contents
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
1 Introduction
Definition
Where to use it
2 Log class
Static members
Static instance
Static instance pointer
3 Pure Singleton implementations
Static instance pointer
Meyers Singleton
Phoenix Singleton
4 Multithreaded access
Coherent cache
Non-coherent cache
Compiler-dependent solutions
5 Resources
21 / 39
The volatile keyword
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Properties of “volatile” variables
Access directly translated into assembler “mov” instruction. ⇒
not kept in a register.
Accesses are not reordered.
But: Accesses to non-volatile variables can still be reordered with
accesses to volatile variables.
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
22 / 39
Cache coherence
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
cpu 0
cpu 1
Log class
Static members
Static instance
Static instance
pointer
cache 0 coherent? cache 1
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
ram
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
23 / 39
Cache coherence (2)
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Cache coherence
If hardware enforces cache coherence local caches of all processores
are automatically synchronized.
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
⇒ Programmer does not need to care about the existance of caches
at all (except for performance reasons).
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
24 / 39
Cache coherence (3)
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Let us first assume that our hardware always provides a coherent
cache (which is the case on x86 hardware)
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Note that we still need to care about the existence of registers.
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
25 / 39
Simple implementation
Implementing the
Singleton pattern
in C++
1
Klaus Sembritzki
2
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
3
4
5
6
7
8
9
10
11
12
13
14
class Singleton {
public :
s t a t i c S i n g l e t o n& i n s t a n c e ( ) {
// l o c k ’ s c o n s t r u c t o r e n t e r s c r i t i c a l s e c t i o n
Lock l o c k ;
i f ( i n s t a n c e == 0 ) {
i n s t a n c e = new S i n g l e t o n ( ) ;
}
return ∗ instance ;
// l o c k ’ s d e s t r u c t o r l e a v e s c r i t i c a l s e c t i o n
}
private :
static Singleton ∗ volatile instance ;
};
The code works.
Note the position of the volatile keyword (compare to const).
26 / 39
Double-checked locking
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Singleton {
public :
s t a t i c S i n g l e t o n& i n s t a n c e ( ) {
i f ( i n s t a n c e == 0 ) {
Lock l o c k ;
// v o l a t i l e n e s s f o r c e s r e −r e a d
i f ( i n s t a n c e == 0 ) {
// p r o b l e m a t i c , s e e n e x t s l i d e
S i n g l e t o n ∗ v o l a t i l e tmp = new S i n g l e t o n ( ) ;
i n s t a n c e = tmp ;
}
}
return ∗ instance ;
}
private :
Singleton () { x = 7; }
static Singleton ∗ volatile instance ;
int x ;
};
27 / 39
Double-checked locking, inlined constructor
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
1
2
3
4
5
6
7
8
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
9
10
11
12
13
14
15
16
17
class Singleton {
public :
s t a t i c S i n g l e t o n& i n s t a n c e ( ) {
i f ( i n s t a n c e == 0 ) {
Lock l o c k ;
i f ( i n s t a n c e == 0 ) {
// a c c e s s t o non− v o l a t i l e members may be r e o r d e r e d .
S i n g l e t o n ∗ v o l a t i l e tmp =
s t a t i c c a s t <S i n g l e t o n ∗>( o p e r a t o r
new ( s i z e o f ( S i n g l e t o n ) ) ) ;
tmp−>x = 7 ;
i n s t a n c e = tmp ;
}
}
return ∗ instance ;
}
private :
static Singleton ∗ volatile instance ;
int x ; };
28 / 39
Double-checked locking, volatile idea
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Idea
Make pointer contents volatile
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Problem
Like const-objects that only become const after their constructors
have run to completion, volatile objects only become volatile after
their constructors finished.
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
29 / 39
Double-checked locking, volatile pointer
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Singleton {
public :
s t a t i c v o l a t i l e S i n g l e t o n& i n s t a n c e ( ) {
i f ( i n s t a n c e == 0 ) {
Lock l o c k ;
i f ( i n s t a n c e == 0 ) {
v o l a t i l e S i n g l e t o n ∗ v o l a t i l e tmp = new
volatile Singleton () ;
i n s t a n c e = tmp ;
}
}
return ∗ instance ;
}
private :
Singleton () { x = 7; }
// c o n t e n t s o n l y v o l a t i l e b e c a u s e tmp c o u l d
o t h e r w i s e n o t be c a s t e d
static volatile Singleton ∗ volatile instance ;
int x ; };
30 / 39
Double-checked locking, cast to volatile
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Singleton {
public :
s t a t i c S i n g l e t o n& i n s t a n c e ( ) {
i f ( i n s t a n c e == 0 ) {
Lock l o c k ;
i f ( i n s t a n c e == 0 ) {
S i n g l e t o n ∗ v o l a t i l e tmp = new S i n g l e t o n ( ) ;
i n s t a n c e = tmp ;
}
}
return ∗ instance ;
}
private :
S i n g l e t o n ( ) { s t a t i c c a s t <v o l a t i l e i n t &>(x ) = 7 ; }
static Singleton ∗ volatile instance ;
int x ; };
This solution works.
31 / 39
Double-checked locking, barrier
Implementing the
Singleton pattern
in C++
1
Klaus Sembritzki
2
3
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Singleton {
public :
s t a t i c S i n g l e t o n& i n s t a n c e ( ) {
i f ( i n s t a n c e == 0 ) {
Lock l o c k ;
i f ( i n s t a n c e == 0 ) {
S i n g l e t o n ∗tmp = new S i n g l e t o n ( ) ;
// ( . . . ) memory b a r r i e r
i n s t a n c e = tmp ;
}
}
return ∗ instance ;
}
private :
Singleton () { x = 7; }
static Singleton ∗ volatile instance ;
int x ; };
This solution works.
32 / 39
Non-coherent caches and volatile
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Reminder
Volatileness simply produces ordered “mov” assembler instructions,
those can result in writing to the cache or the main memory.
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
⇒ Volatileness is useless if the caches are not coherent.
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
33 / 39
Simple implementation, barrier
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
1
2
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
3
4
5
6
7
8
9
10
11
12
13
s t a t i c S i n g l e t o n& i n s t a n c e ( ) {
Lock l o c k ;
// ( . . . ) r e a d i n s t a n c e and members from memory
// why r e a d t h e members
//
− memory m i gh t h a v e been m a l l o c e d by
//
a n o t h e r o b j e c t and f r e e d t h e n
//
− cache l i n e s
i f ( i n s t a n c e == 0 ) {
S i n g l e t o n ∗ i n s t a n c e = new S i n g l e t o n ( ) ;
// ( . . . ) w r i t e i n s t a n c e and i t s members t o main
memory
}
return ∗ inst ance ;
}
This solution works.
Resources
34 / 39
Double-checked locking, barrier
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
1
2
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
3
4
5
6
7
8
9
10
11
12
13
14
s t a t i c S i n g l e t o n& i n s t a n c e ( ) {
// ( . . . ) r e a d i n s t a n c e and members from memory
i f ( i n s t a n c e == 0 ) {
Lock l o c k ;
// ( . . . ) r e a d i n s t a n c e and members from memory
i f ( i n s t a n c e == 0 ) {
S i n g l e t o n ∗tmp = new S i n g l e t o n ( ) ;
// ( . . . ) w r i t e members o f tmp t o main memory
i n s t a n c e = tmp ;
// ( . . . ) w r i t e i n s t a n c e t o main memory
}
}
return ∗ inst ance ;
}
This solution works.
Resources
35 / 39
Compiler-dependent solutions
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
gcc
supports thread-safe static local variables not covered by the
standard. ⇒ optimized double-checked Meyers Singleton
possible.
volatile is not thread-safe
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
The Visual Studio compiler
does not support thread-safe static local variables.
Volatile is implemented in a thread safe fashion not covered by
the standard.
Resources
36 / 39
Recommendation
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Use fencing (= barriers) with boost, switch to C++0x library when
available.
Multithreaded Singleton Implementations with boost are can be
found online
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
37 / 39
Table of Contents
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
Resources
1 Introduction
Definition
Where to use it
2 Log class
Static members
Static instance
Static instance pointer
3 Pure Singleton implementations
Static instance pointer
Meyers Singleton
Phoenix Singleton
4 Multithreaded access
Coherent cache
Non-coherent cache
Compiler-dependent solutions
5 Resources
38 / 39
Resources
Implementing the
Singleton pattern
in C++
Klaus Sembritzki
Introduction
Definition
Where to use it
Modern C++ Design, Andrei Alexandrescu
ISBN 0-201-70431-5
Log class
Static members
Static instance
Static instance
pointer
Pure Singleton
implementations
Static instance
pointer
Meyers Singleton
Phoenix
Singleton
Multithreaded
access
Coherent cache
Non-coherent
cache
Compilerdependent
solutions
http://www.codeproject.com/KB/threads/DoubleCheckedLocking.as
http://blogs.msdn.com/b/kangsu/archive/2007/07/16/volatileacquire-release-memory-fences-and-vc2005.aspx
http://software.intel.com/en-us/blogs/2007/11/30/volatilealmost-useless-for-multi-threaded-programming/
http://www.cs.umd.edu/ pugh/java/memoryModel/DoubleCheckedLocking.html
Resources
39 / 39