21-May-2013 02:02, Idan Arye пишет:
On Monday, 20 May 2013 at 19:15:34 UTC, Dmitry Olshansky wrote:
If you need synchronization and coordination based on what the
reference happens to be right now then there are tools far better fit
for the job - mutexes, semaphore, condition vars etc.
First and foremost - this is library library code, so it should expose
as much interface as possible without exposing or breaking the
implementation.
A-ha. That's it and it's totally wrong. Exposing as much interface as
possible is a disaster. Libraries (esp standard) take great deal of
deliberation in picking what to expose. Exposing less is a common theme
in interfaces. "Doing more" is a common theme in wrappers, helpers and
has a proverbial "kitchen sink" effect.
Personally, I think `hasInstance()` would be mainly used in the second
use case you suggested. It would be useful if you have a long running
loop(for example - a game loop) that needs to interact with the
singleton instance if it exists, but can't or shouldn't instantiate it.
Message passing, queues, condition variables. Singleton pattern assumes
you have an object with unknown initialization order and who ever
touches it first gets to create it.
As for the first use case, you are right - it is a bad design to
busy-wait for a singleton to be instantiated somewhere else. I should
probably add another method that waits for the instance using a condition.
And your are falling into a trap I decidedly put in my example - this is
a no use case for singletons. Want to wait on smth to happen? - use
*separate* condition var, event pump etc. Want to check on some external
state? - same answer.
Though you might provide separate class for waitable singleton that
incorporates
condition variable. Could be useful sometimes(?) in case there is no
other logic
to define order of initialization.
BTW Why not just make a template Singleton!T instead of mixin?
The last but not least is the fact that LowLock returns TLS reference
to a (__g)shared instance make me worry about how the users code now
is full of hidden race conditions anyway. This applies to the pattern
as presented not only your implementation of it.
So the singleton itself would need some synchronization... and for
that it really should be marked shared. The alternative is to have a
per-thread singleton without any locking.
There is also a thread local version called `ThreadLocalSingleton`. If I
made a shared version, would that solve those possible hidden race
conditions?
It would make people do something about it - shared allows only calling
shared methods of a class and prohibits all basic operations on the
fields. Points of race become fairly obvious - they need casts and lack
of locks in the vicinity.
Every time I see __gshared I think "lazy, old and broken" and usually at
least one of these is true.
Is there a point in using the TLS Low Lock method for shared
singletons?
Good question. Yes, it is as it will allow you to have lazy
initialization from any thread on any multicores w/o always taking a
lock. If you can't figure out how to make order of initialization
deterministic (or who creates what and when) then these lazy-init
singletons are good idea.
I personally despise singletons and the code patterns they introduce.
IMHO lazy initialization (not to mistake with lazy loading/caching) is
so rarely *required* that you may as well avoid it, and in D we have
static this/shared static this.
--
Dmitry Olshansky