Singleton can be made
thread-safe, but not for much benefit. Its members, by definition, cannot be made
reentrant.
Double-checked locking is a pattern that relies on concepts which are quite difficult to achieve in C++ due to most of it relying on hardware details. It was even broken in Java for a decade.
Finally, there is a linking problem, which may result in race condition even without presence of threads. From design perspective, database singleton is inherently stateless. Data is stored elsewhere, connections, due to managed nature, are transient. This implies there is nothing that warrants "just existing", or at very least is a promise that simply cannot be kept.
When talking about concurrency - 12 cores are entering the market, some machines run 4096 cores today in single address space. "Works for me on dual core" is no longer a valid justification.
As far as having one database only - this is the very first fallacy anyone encounters. IT world learned as far back as 2000 why this is a disaster and dropped singletons like old news for this purpose.
There are two design and management problems:
- Swapping test for production database - if everything is hard-coded to one-and-only this becomes impossible
- Configuration and other initialization issues. Things like passing in authentication token or dynamically configuring database location cause no end to complications.
And as a hard rule - unless it is written, in blood, with contract, in detail - it is not thread-safe nor reentrant. Proving these two is very trivial, without even touching on ACID guarantees.