Confused about a thread-safe singleton example.

J

James Kanze

You can "easily" use `pthread_once()' and compiler specific
directives to construct a fairly "efficient" singleton
pattern;

"easily" really depends. If you're programming on a Windows
platform, using pthread_once may not be that easy, and if you're
programming on a Posix platform, you don't need it to construct
the mutex, because Posix supports static initialization of
mutexes. (Windows Vista does add functionality to do the same
thing---and it is more flexible than the Posix model. But it
means your code can only run on Vista or later.)

I'll admit that I don't see what all the uproar is about. It's
relatively simple to ensure that initialization occurs before
main, so you don't need any locking. And even if you do,
grabbing an uncontended mutex lock just isn't that expensive.
something like this
pseudo-code:
namespace posix {
template<typename T>
class once {
__thread T* g_tls;
static T* g_obj;
static pthread_once_t g_once;
__cdecl void g_init() {
assert(! g_obj);
static T the_instance;
g_obj = &the_instance;
}
static T* instance() {
if (! g_tls) {
pthread_once(&g_once, g_init);
g_tls = g_obj;
assert(g_tls);
}
return g_tls;
}
};

Except that this code isn't thread safe.
template<typename T>
T* once<T>::g_obj = NULL;
 
J

jason.cipriani

NO.  That's what we mean when we speak of the order of
initialization problem.  That's why we have a pointer, which we
test for null, or we use a local static, or some other solution
in which the function constructs the first time it is called.

Thanks!

Jason
 
M

Maxim Yegorushkin

No.  A singleton is a type, not a variable.  What characterizes
a type as a singleton is that there can only be zero or one
instances of the type in a program.  The restriction on the
number of instances is part of the abstraction of the type.

I see now, it is about enforcing the number of instances on type
level.

So, a global variable can be an instance of a singleton class, can it
not?
 
N

Noah Roberts

Maxim said:
I see now, it is about enforcing the number of instances on type
level.

So, a global variable can be an instance of a singleton class, can it
not?

The important thing to realize about patterns is not how they are
implemented in their description, but what causes them and what they are
meant to accomplish. In the case you showed before you did all the
things that a singleton pattern is meant to accomplish (although there
seems to be problems?) and simply used a global reference variable as
the access method. The global reference variable itself is not a
singleton, it's the accessor to one.
 
C

Chris M. Thomasson

"easily" really depends. If you're programming on a Windows
platform, using pthread_once may not be that easy, and if you're
programming on a Posix platform, you don't need it to construct
the mutex, because Posix supports static initialization of
mutexes. (Windows Vista does add functionality to do the same
thing---and it is more flexible than the Posix model. But it
means your code can only run on Vista or later.)
I'll admit that I don't see what all the uproar is about. It's
relatively simple to ensure that initialization occurs before
main, so you don't need any locking. And even if you do,
grabbing an uncontended mutex lock just isn't that expensive.

I was attempting to address POSIX platforms; I added quotes around "easily"
in jest, sorry about that...

:^/



something like this
pseudo-code:
namespace posix {
[...]

} // namespace posix
 
C

Chris M. Thomasson

You can "easily" use `pthread_once()' and compiler specific
directives to construct a fairly "efficient" singleton
pattern;
[...]

I'll admit that I don't see what all the uproar is about. It's
relatively simple to ensure that initialization occurs before
main, so you don't need any locking. And even if you do,
grabbing an uncontended mutex lock just isn't that expensive.


The overhead associated with uncontended locks for frequently accessed
singleton data-structures can be quite significant indeed. For instance,
there can be a #StoreLoad memory order constraint _and_ an atomic RMW
instruction attached to every lock acquisition. Also, there can be a
#LoadStore memory order constraint and atomic RMW along side every lock
release.


Yikes!


[...]
 
K

Kai-Uwe Bux

Chris said:
news:962e1281-0dd3-4571-b941-1de092ff63ed@j32g2000yqn.googlegroups.com...
You can "easily" use `pthread_once()' and compiler specific
directives to construct a fairly "efficient" singleton
pattern;
[...]

I'll admit that I don't see what all the uproar is about. It's
relatively simple to ensure that initialization occurs before
main, so you don't need any locking. And even if you do,
grabbing an uncontended mutex lock just isn't that expensive.


The overhead associated with uncontended locks for frequently accessed
singleton data-structures can be quite significant indeed. For instance,
there can be a #StoreLoad memory order constraint _and_ an atomic RMW
instruction attached to every lock acquisition. Also, there can be a
#LoadStore memory order constraint and atomic RMW along side every lock
release.


Yikes!

Are you concerned about each call to instance() triggering the locking while
only the process of constructing the object actually needs a lock? If so,
what about something like this (of course, FakeLocker would be a real
locker that locks and unlocks a mutex).

#include <iostream>
#include <ostream>

struct Mutex {};

struct FakeLocker {

FakeLocker ( Mutex & ) {
std::cout << "lock\n";
}

~FakeLocker ( void ) {
std::cout << "unlock\n";
}

};

template < typename T >
class once {

struct T_wrapper {

T * ptr;
Mutex the_mutex;

T_wrapper ( void ) {
FakeLocker lock ( the_mutex );
ptr = new T;
}

};

public:

static
T & instance ( void ) {
static T_wrapper dummy;
return ( * dummy.ptr );
}

};

int main ( void ) {
once<int>::instance();
once<int>::instance();
}


Best

Kai-Uwe Bux
 
C

Chris M. Thomasson

Kai-Uwe Bux said:
Chris said:
You can "easily" use `pthread_once()' and compiler specific
directives to construct a fairly "efficient" singleton
pattern;
[...]

I'll admit that I don't see what all the uproar is about. It's
relatively simple to ensure that initialization occurs before
main, so you don't need any locking. And even if you do,
grabbing an uncontended mutex lock just isn't that expensive.


The overhead associated with uncontended locks for frequently accessed
singleton data-structures can be quite significant indeed. For instance,
there can be a #StoreLoad memory order constraint _and_ an atomic RMW
instruction attached to every lock acquisition. Also, there can be a
#LoadStore memory order constraint and atomic RMW along side every lock
release.


Yikes!

Are you concerned about each call to instance() triggering the locking
while
only the process of constructing the object actually needs a lock? If so,
what about something like this (of course, FakeLocker would be a real
locker that locks and unlocks a mutex).

[...]

I am concerned with crappy `pthread_once()' implementations that happen to
acquire/release some synchronization object on a per-call basis. The impl of
`pthread_once()' can be efficient hybrid of double checked-locking and
per-thread pointers (e.g., TSD). Or it can be horrid 100% strictly
serialized impl. Oh well, at least pthread version is portable to compilers
which support proper extensions, and also support POSIX threading...
Hopefully, the implementation of `pthread_once()' on your POSIX platform of
choice is both scaleable, and efficient.
 
J

James Kanze

I see now, it is about enforcing the number of instances on
type level.

Yes. In the case of C++, it's also often about managing order
of initialization issues, but that's really more of a pragmatic
consideration than something fundamental to the concept of a
singleton.
So, a global variable can be an instance of a singleton class,
can it not?

It would depend on how the singleton is implemented. The
oldest, traditional implementation (use a static counter,
incremented in the constructor, and assert that it is never
greater than 1 after the incrementation) certainly allows it.
Most of the C++ implementations wouldn't allow it, requiring you
to go through a special function. More generally, I think, this
is usually the case in other languages as well, since by making
the constructor private, and requiring the function to be
called, makes it impossible to write compilable code which would
instantiate more than one instance.

It's not a perfect solution, however---as I think you
mentionned, it doesn't allow derivation, at least not easily.
And all those calls to instance() ARE a real pain in the neck.
 
J

James Kanze

"James Kanze" <[email protected]> wrote in message
On Dec 4, 5:45 am, "Chris M. Thomasson" <[email protected]>
wrote:
I'll admit that I don't see what all the uproar is about.
It's relatively simple to ensure that initialization occurs
before main, so you don't need any locking. And even if you
do, grabbing an uncontended mutex lock just isn't that
expensive.
The overhead associated with uncontended locks for frequently
accessed singleton data-structures can be quite significant
indeed. For instance, there can be a #StoreLoad memory order
constraint _and_ an atomic RMW instruction attached to every
lock acquisition. Also, there can be a #LoadStore memory order
constraint and atomic RMW along side every lock release.

Have you actually measured it? I was pleasantly surprised when
I did (on a Sun Sparc, under Solaris). It's true that you need
the fences, and that they do increase execution time, but
they're unavoidable in any working solution anyway. (The code
you posted is NOT thread safe, and will not work on any number
of architectures, including Sparc, Alpha, Itanium...)
 
C

Chris M. Thomasson

On Dec 4, 5:45 am, "Chris M. Thomasson" <[email protected]>
wrote:
You can "easily" use `pthread_once()' and compiler
specific directives to construct a fairly "efficient"
singleton pattern;
[...]
I'll admit that I don't see what all the uproar is about.
It's relatively simple to ensure that initialization occurs
before main, so you don't need any locking. And even if you
do, grabbing an uncontended mutex lock just isn't that
expensive.
The overhead associated with uncontended locks for frequently
accessed singleton data-structures can be quite significant
indeed. For instance, there can be a #StoreLoad memory order
constraint _and_ an atomic RMW instruction attached to every
lock acquisition. Also, there can be a #LoadStore memory order
constraint and atomic RMW along side every lock release.
Have you actually measured it?
Yes.




I was pleasantly surprised when
I did (on a Sun Sparc, under Solaris). It's true that you need
the fences, and that they do increase execution time, but
they're unavoidable in any working solution anyway.

There not unavoidable for certain lock implementations (asymmetric Dekker
algorithm for one):

http://groups.google.com/group/comp.programming.threads/browse_frm/thread/22b2736484af3ca6


See?



(The code
you posted is NOT thread safe, and will not work on any number
of architectures, including Sparc, Alpha, Itanium...)

The pseudo-code I posted is thread-safe. It uses POSIX Thread
`pthread_once()' for the initialization of the Meyers singleton.
`pthread_once()' synchronizes the memory.
 
M

Maxim Yegorushkin

On Dec 4, 4:58 pm, Maxim Yegorushkin <[email protected]>
wrote:
[]
So, a global variable can be an instance of a singleton class,
can it not?

It would depend on how the singleton is implemented.  The
oldest, traditional implementation (use a static counter,
incremented in the constructor, and assert that it is never
greater than 1 after the incrementation) certainly allows it.
Most of the C++ implementations wouldn't allow it, requiring you
to go through a special function.  More generally, I think, this
is usually the case in other languages as well, since by making
the constructor private, and requiring the function to be
called, makes it impossible to write compilable code which would
instantiate more than one instance.

It's not a perfect solution, however---as I think you
mentionned, it doesn't allow derivation, at least not easily.

It was mentioned by Alan Johnson.

Personally, I prefer deriving from interface classes only, for
anything else composition seems to me to be a superior approach, so
whether something can be derived from is the last thing I would care.
And all those calls to instance() ARE a real pain in the neck.

The problem, IMO, is that it is supposed to limit the number of
instances on the type level. Yet, there is a price to be paid at run-
time for every usage of ::instance() call (albeit small). On the other
hand, accessing good old global variables (or global references to
abstract interfaces) has zero price and seem to work quite well when I
need one instance of something and it does not require special syntax
for accessing that instance (less typing at the least).

I have my one instance and I just can't be bothered to enforce that by
making the class a singleton, for the very same reason that I can't be
bothered to prohibit derivation from classes that are not supposed to
be derived. If someone wants to shoot his foot off I won't mess with
the person's good will, experience work best when it is obtained first
hand ;).
 
J

James Kanze

On Dec 4, 4:58 pm, Maxim Yegorushkin <[email protected]>
wrote:
[]
So, a global variable can be an instance of a singleton class,
can it not?
It would depend on how the singleton is implemented. The
oldest, traditional implementation (use a static counter,
incremented in the constructor, and assert that it is never
greater than 1 after the incrementation) certainly allows
it. Most of the C++ implementations wouldn't allow it,
requiring you to go through a special function. More
generally, I think, this is usually the case in other
languages as well, since by making the constructor private,
and requiring the function to be called, makes it impossible
to write compilable code which would instantiate more than
one instance.
It's not a perfect solution, however---as I think you
mentionned, it doesn't allow derivation, at least not easily.
It was mentioned by Alan Johnson.
Personally, I prefer deriving from interface classes only, for
anything else composition seems to me to be a superior
approach, so whether something can be derived from is the last
thing I would care.

You prefer deriving.../whether something can be derived from is
the last thing you care. Seems like a contradiction.

There are clearly times when deriving from something that isn't
a pure interface is justified -- the template method pattern, or
mixins, for example -- although it's probably overused. But
that begs the question: there's no real reason why a singleton
shouldn't be a pure interface, in the classical sense of the
word. In the Unix world, for example, it's usual to have a
singleton factory class for the creation of GUI components. All
the user sees is an interface, however, since there are
different derived classes according to which look and feel is
wanted (Motif, Athena, etc.). Which derived instance is present
is determined at runtime (usually by controlled dynamic
linking), but there can be at most only one of them.
The problem, IMO, is that it is supposed to limit the number
of instances on the type level. Yet, there is a price to be
paid at run- time for every usage of ::instance() call (albeit
small). On the other hand, accessing good old global variables
(or global references to abstract interfaces) has zero price
and seem to work quite well when I need one instance of
something and it does not require special syntax for accessing
that instance (less typing at the least).

It's not so much the runtime cost which bothers me, as the
clutter in the source files. Increased typing time, and also
increased reading time, and time necessary to find what you're
looking for. When it comes down to it, the function call is
noice.
I have my one instance and I just can't be bothered to enforce
that by making the class a singleton, for the very same reason
that I can't be bothered to prohibit derivation from classes
that are not supposed to be derived. If someone wants to shoot
his foot off I won't mess with the person's good will,
experience work best when it is obtained first hand ;).

The problem in this case is that you're not alone. So you have
to coordinate who defines the unique instance, etc. It creates
a (small) management problem.

And there is, of course, the order of initialization issue.
 
J

James Kanze

"James Kanze" <[email protected]> wrote in message

[...]
There not unavoidable for certain lock implementations
(asymmetric Dekker algorithm for one):

See?

No. I don't see. Some sort of fences or membar instructions
are certainly necessary if you expect the other threads to see
your writes.
The pseudo-code I posted is thread-safe. It uses POSIX Thread
`pthread_once()' for the initialization of the Meyers singleton.
`pthread_once()' synchronizes the memory.

Except that there were control flows which used the variables
without going through pthread_once:

static T* instance() {
if (! g_tls) {
pthread_once(&g_once, g_init);
g_tls = g_obj;
assert(g_tls);
}
return g_tls;
}

All you've done is replace the scoped mutex lock in double
checked locking with pthread_once; that doesn't solve anything.
If a thread sees g_tls non-null, there's no guarantee that it
will see a fully constructed object.
 
J

jason.cipriani

On Dec 4, 4:58 pm, Maxim Yegorushkin <[email protected]>
wrote: []
So, a global variable can be an instance of a singleton class,
can it not?
It would depend on how the singleton is implemented.  The
oldest, traditional implementation (use a static counter,
incremented in the constructor, and assert that it is never
greater than 1 after the incrementation) certainly allows
it.  Most of the C++ implementations wouldn't allow it,
requiring you to go through a special function.  More
generally, I think, this is usually the case in other
languages as well, since by making the constructor private,
and requiring the function to be called, makes it impossible
to write compilable code which would instantiate more than
one instance.
It's not a perfect solution, however---as I think you
mentionned, it doesn't allow derivation, at least not easily.
It was mentioned by Alan Johnson.
Personally, I prefer deriving from interface classes only, for
anything else composition seems to me to be a superior
approach, so whether something can be derived from is the last
thing I would care.

You prefer deriving.../whether something can be derived from is
the last thing you care.  Seems like a contradiction.

There are clearly times when deriving from something that isn't
a pure interface is justified -- the template method pattern, or
mixins, for example -- although it's probably overused.  But
that begs the question: there's no real reason why a singleton
shouldn't be a pure interface, in the classical sense of the
word.  In the Unix world, for example, it's usual to have a
singleton factory class for the creation of GUI components.  All
the user sees is an interface, however, since there are
different derived classes according to which look and feel is
wanted (Motif, Athena, etc.).  Which derived instance is present
is determined at runtime (usually by controlled dynamic
linking), but there can be at most only one of them.
The problem, IMO, is that it is supposed to limit the number
of instances on the type level. Yet, there is a price to be
paid at run- time for every usage of ::instance() call (albeit
small). On the other hand, accessing good old global variables
(or global references to abstract interfaces) has zero price
and seem to work quite well when I need one instance of
something and it does not require special syntax for accessing
that instance (less typing at the least).

It's not so much the runtime cost which bothers me, as the
clutter in the source files.  Increased typing time, and also
increased reading time, and time necessary to find what you're
looking for.  When it comes down to it, the function call is
noice.


I generally find that I'm rarely in a situation where I frequently
have to call instance(), and almost always have the option of caching
the instance pointer in some variable, e.g. a local variable in a
thread loop, a member of a class, even a *local* static in a
frequently called function. It gets rid of the clutter and overhead,
and caching it also means you don't have to concentrate so much on
making your thread-safe implementation of instance() blazing fast
(well, of course, "blazing fast" being relative, e.g. a speedy 2.0
usec instead of a snail-like 2.3 usec with the overhead of acquiring a
mutex or something).


Jason
 
C

Chris M. Thomasson

[...]
No. I don't see. Some sort of fences or membar instructions
are certainly necessary if you expect the other threads to see
your writes.

Na. Everything is taken care of by `pthread_once()'.



Except that there were control flows which used the variables
without going through pthread_once:
static T* instance() {
if (! g_tls) {
pthread_once(&g_once, g_init);
g_tls = g_obj;
assert(g_tls);
}
return g_tls;
}
All you've done is replace the scoped mutex lock in double
checked locking with pthread_once; that doesn't solve anything.
If a thread sees g_tls non-null, there's no guarantee that it
will see a fully constructed object.

No. Did you notice that g_tls exists in TSD? Notice the `__thread'
decoration in front of the `g_tls' variable:


template<typename T>
class once {
__thread T* g_tls;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

static T* g_obj;
static pthread_once_t g_once;

[...]






Here is how it works:

static T* instance() {
1: if (! g_tls) {
2: pthread_once(&g_once, g_init);
3: g_tls = g_obj;
4: assert(g_tls);
}
5: return g_tls;
}


1. If calling threads local pointer, `g_tls', is NULL, that means its the
first time the calling thread has ever accessed the singleton. Therefore, it
needs to funnel itself through `pthread_once()' in order to properly
synchronize with the static initialization. If calling threads local pointer
in not NULL, goto step 5...


2. First time threads go though synchronization point!


3. First time threads set their local pointer to the _fully_ visible global
pointer and the object it points to will also be visible; thanks to
`pthread_once()'...


4. Make sure the static initialization worked.


5. Return calling threads local instance pointer.



The fast-path occurs when a thread calls the singleton more than once; the
slow-path is thread first use which in turn sets its thread local pointer to
instance. Second use (the fast-path) goes directly to the local pointer and
skips the call into `pthread_once()'. Each thread call `pthread_once()'
exactly ONE time per singleton type. The thread-local pointer ensures that a
thread only ever makes one call to `pthread_once()'. Examine the code
again...



The only caveats for this pseudo-code would be all the rules that pertain to
Meyers singleton object destructors... Cannot access a singleton from a dtor
of a singleton.
 
C

Chris M. Thomasson

Here is a working example code for MSVC++ 8>:
______________________________________________________________________________



/* POSIX Threads (pthread-win32) / MSVC Singleton
____________________________________________________________________*/
#include <pthread.h>
#include <cassert>
#include <cstdio>


#if defined(_MSC_VER)
# define DECLSPEC_THREAD static __declspec(thread)
# define DEFSPEC_THREAD __declspec(thread)
# define DECLSPEC_CDECL __cdecl
# define DEFSPEC_CDECL __cdecl
#else
# define DECLSPEC_THREAD __thread
# define DEFSPEC_THREAD
# define DECLSPEC_CDECL __attribute__((cdecl))
# define DEFSPEC_CDECL __attribute__((cdecl))
#endif


namespace posix {
namespace thread {

template<typename T>
class once {
DECLSPEC_THREAD T* g_tls;
static T* g_instance;
static pthread_once_t g_once;
static void DECLSPEC_CDECL prv_init();

public:
static T& instance();
};

template<typename T>
DEFSPEC_THREAD T* once<T>::g_tls = NULL;

template<typename T>
T* once<T>::g_instance = NULL;

template<typename T>
pthread_once_t once<T>::g_once = PTHREAD_ONCE_INIT;

template<typename T>
void DECLSPEC_CDECL once<T>::prv_init() {
static T g_obj;

assert(! g_instance);
g_instance = &g_obj;

std::printf("INSTANCE SLOW-PATH STATIC INIT! <(%p)->g_instance>\n",
(void*)g_instance);
}

template<typename T>
T& once<T>::instance() {
if (! g_tls) {
int const status = pthread_once(&g_once, prv_init);

if (status) {
assert(! status);
throw;
}

g_tls = g_instance;

if (! g_tls) {
assert(g_tls);
throw;
}

std::printf("INSTANCE SLOW-PATH! <(%p)->g_tls == (%p)->g_instance>\n",
(void*)g_tls, (void*)g_instance);

} else {
std::printf("INSTANCE FAST-PATH! <(%p)->g_tls == (%p)->g_instance>\n",
(void*)g_tls, (void*)g_instance);

assert(g_tls == g_instance);
}
return *g_tls;
}


// abstract thread
class thread_base {
pthread_t m_handle;
virtual void on_active() = 0;

static void* DEFSPEC_CDECL prv_active(void* state) {
static_cast<thread_base*>(state)->on_active();
return 0;
}

public:
virtual ~thread_base() = 0;

void active_run() {
pthread_create(&m_handle, NULL, prv_active, this);
}

void active_join() {
pthread_join(m_handle, NULL);
}
};

thread_base::~thread_base() {}

}} // namespace posix::thread

// active helper
template<typename T>
struct active : public T {
active() : T() {
active_run();
}

template<typename P1>
active(P1 p1) : T(p1) {
active_run();
}

~active() {
active_join();
}
};




/* Sample Application
____________________________________________________________________*/

// abstract the namespace
namespace mythread = posix::thread;


template<unsigned TYPE>
struct foo {
foo() {
std::printf("(%p)->foo<%u>::foo()\n", (void*)this, TYPE);
}

~foo() {
std::printf("(%p)->foo<%u>::~foo()\n", (void*)this, TYPE);
std::puts("hit <ENTER> to continue...");
std::fflush(stdout);
std::getchar();
}

void do_something(unsigned thread_id) {
std::printf("(%p)->foo<%u>::do_something() called by (%u)\n",
(void*)this, TYPE, thread_id);
}
};


class worker : public mythread::thread_base {
unsigned const m_id;

void on_active() {
std::printf("(%p)->worker(%u)::eek:n_active() - enter\n", (void*)this,
m_id);
for (unsigned i = 0; i < 10; ++i) {
if (! (i % 2)) {
mythread::eek:nce<foo<0> >::instance().do_something(m_id);
mythread::eek:nce<foo<1> >::instance().do_something(m_id);
mythread::eek:nce<foo<2> >::instance().do_something(m_id);
mythread::eek:nce<foo<3> >::instance().do_something(m_id);
mythread::eek:nce<foo<3> >::instance().do_something(m_id);
mythread::eek:nce<foo<2> >::instance().do_something(m_id);
mythread::eek:nce<foo<1> >::instance().do_something(m_id);
mythread::eek:nce<foo<0> >::instance().do_something(m_id);
} else {
mythread::eek:nce<foo<4> >::instance().do_something(m_id);
mythread::eek:nce<foo<5> >::instance().do_something(m_id);
mythread::eek:nce<foo<6> >::instance().do_something(m_id);
mythread::eek:nce<foo<7> >::instance().do_something(m_id);
mythread::eek:nce<foo<7> >::instance().do_something(m_id);
mythread::eek:nce<foo<6> >::instance().do_something(m_id);
mythread::eek:nce<foo<5> >::instance().do_something(m_id);
mythread::eek:nce<foo<4> >::instance().do_something(m_id);
}
}
std::printf("(%p)->worker(%u)::eek:n_active() - exit\n", (void*)this,
m_id);
}

public:
worker(unsigned const id) : m_id(id) {}
};


int main(void) {
{
active<worker> workers[] = { 123, 456, 789, 213, 645, 978, 543, 1024 };
}

return 0;
}
______________________________________________________________________________




This should compile fine. You need the `pthread-win32' library for this to
work on windows. No reason why this would not work on pure POSIX system with
compiler that had similar extensions.
 
J

James Kanze

On Dec 5, 12:05 pm, James Kanze <[email protected]> wrote:

[...]
[Re singletons]
I generally find that I'm rarely in a situation where I
frequently have to call instance(), and almost always have the
option of caching the instance pointer in some variable, e.g.
a local variable in a thread loop, a member of a class, even a
*local* static in a frequently called function.

So you have the clutter of an extra variable, rather than the
clutter of an extra function call. In the end, it comes out to
the same thing (although if I'm using the singleton more than
once or twice in a function, I'll use a local variable to hold
the reference).
It gets rid of the clutter and overhead, and caching it also
means you don't have to concentrate so much on making your
thread-safe implementation of instance() blazing fast (well,
of course, "blazing fast" being relative, e.g. a speedy 2.0
usec instead of a snail-like 2.3 usec with the overhead of
acquiring a mutex or something).

It can certainly be an optimization issue in the case of
multithreading, and the possibility is one of the reasons why
all these attempts of making double-checked locking work are
just intellectual masturbation.
 
J

James Kanze

"James Kanze" <[email protected]> wrote in message
On Dec 5, 10:32 am, "Chris M. Thomasson" <[email protected]>
wrote:
    [...]
No.  I don't see.  Some sort of fences or membar instructions
are certainly necessary if you expect the other threads to see
your writes.
Na. Everything is taken care of by `pthread_once()'.

Which has the fences or whatever in it.
No. Did you notice that g_tls exists in TSD? Notice the
`__thread' decoration in front of the `g_tls' variable:

No. What's __thread? I can't find it, either in Posix nor in
C++. (Looks like a Microsoft'ism to me, but that can't be the
case with pthread_once.)

OK, I know that you can make double checked locking work using
thread local storage, acquiring the lock the first time you
enter the function in each thread. Depending on the
implementation of thread local storage, that's likely to be more
expensive than just acquiring the lock. (I've not used thread
local storage recently, so I can't say, but I know that some
early implementations were nothing more than basically a map,
indexed by the thread id, and protected by a mutex.)

In the end, from an engineering standpoint, there are really
only two (or two and a half:)) valid solutions:

T&
Singleton::instance()
{
ScopedLock l( ourMutex ) ;
if ( ourInstance == NULL ) {
ourInstance = new Singleton ;
}
return *ourInstance ;
}

and

T&
Singleton::instance()
{
if ( ourInstance == NULL ) {
ourInstance = new Singleton ;
}
return *ourInstance ;
}

with documentation stating that the instance function must be
called before multithreading is started. (The half, above, is
that in most such cases, I'll add code to ensure that instance
is called from a static initializer, so instance is guaranteed
to be called during static initialization.)

Of course, if the Singleton is mutable, the client needs a lock
to access it, and it is fairly simple, using smart pointers, to
use the same lock for the test and for accessing the instance.
  template<typename T>
  class once {
    __thread T* g_tls;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Which doesn't compile with my (Posix compliant) compiler.

[...]
The only caveats for this pseudo-code would be all the rules
that pertain to Meyers singleton object destructors... Cannot
access a singleton from a dtor of a singleton.

That's because you insist on destructing the singleton. I never
do.
 
C

Chris M. Thomasson

On Dec 5, 10:32 am, "Chris M. Thomasson" <[email protected]>
wrote:
"James Kanze" <[email protected]> wrote in message
[...]
I did (on a Sun Sparc, under Solaris). It's true that you
need the fences, and that they do increase execution time,
but they're unavoidable in any working solution anyway.
There not unavoidable for certain lock implementations
(asymmetric Dekker algorithm for one):
http://groups.google.com/group/comp.programming.threads/browse_frm/th...
See?
No. I don't see. Some sort of fences or membar instructions
are certainly necessary if you expect the other threads to see
your writes.
Na. Everything is taken care of by `pthread_once()'.
Which has the fences or whatever in it.
No. What's __thread? I can't find it, either in Posix nor in
C++. (Looks like a Microsoft'ism to me, but that can't be the
case with pthread_once.)
http://gcc.gnu.org/onlinedocs/gcc-3.3.1/gcc/Thread-Local.html




OK, I know that you can make double checked locking work using
thread local storage, acquiring the lock the first time you
enter the function in each thread. Depending on the
implementation of thread local storage, that's likely to be more
expensive than just acquiring the lock. (I've not used thread
local storage recently, so I can't say, but I know that some
early implementations were nothing more than basically a map,
indexed by the thread id, and protected by a mutex.)
In the end, from an engineering standpoint, there are really
only two (or two and a half:)) valid solutions:
[...]

Which doesn't compile with my (Posix compliant) compiler.

I bet your POSIX compliant compiler has TSD extensions. Also, implementation
of TSD on x86 can be as simple as accessing the FS segment. That's pretty
efficient indeed.




[...]
That's because you insist on destructing the singleton. I never
do.

Humm...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,061
Latest member
KetonaraKeto

Latest Threads

Top