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

Reply via email to