Ken Wesson <kwess...@gmail.com> writes:

> It looks an awful lot like swap! itself is implemented with a polling
> sleeplock instead of using the language's own lock. :)

I suppose it's similar to a spinlock simply because it keeps retrying,
like a spinlock keeps retrying to acquire.  Very different semantics
though: an atom (or any subset of the swap! code) is not by itself a
drop in replacement for a general-purpose lock.

>> Locking is semantically correct for this situation.  Imagine the webapp
>> case, you start up the webapp and want to initialize something on
>> the first request.  What happens if 3 requests come in simultaneously
>> and all hit your initialize?  You want two of the threads to block and
>> wait while one of them does the initialization.  That's locking (mutual
>> exclusion).
>
> Most of the various proposals here would have that effect. I expect
> they may differ as to their efficiency, though, and perhaps different
> ones would be more efficient under a) high contention and b) low
> contention. I expect that there's been research done on such matters
> by theorists, but I'm not sure to what extent that's informed the
> design of either Java or Clojure...

Well all the ones that are semantically correct (don't have the retry
problems) enforce mutual exclusion on the function as that's part of the
definition of defonce as a problem.

It also depends on how long the init function takes, if it's something
that's going to take a few seconds to do, may as well block.  If it's
quick then it's cheaper to spin.  Often language/OS lock takes this into
account in some fashion, like spinning for a while and eventually
sleeping, or spinning until the thread that holds the lock is context
switched.

>> But yes, there are potential performance benefits, depending on
>> contention.  For example, if f does some calculation and ends up often
>> returning the value unchanged then multiple threads can do that
>> simultaneously without blocking.
>
> I expect the compare it does uses .equals rather than ==?

Not even that, it's a CPU instruction so it'll be identical?.  As just
pointed out the other thread though stuff like this is true due to
structural sharing, which would help in practice:

     (let [m {:x 1, :y 2}]
       (identical? m (assoc m :x 1)))
     
>> Finally, the atom is a safer concurrency primitive, it's impossible to
>> deadlock (unless you implement a lock with one, of course).
>
> It seems to me that it is still possible to "livelock" with this sort
> of thing (and with dosync as well) if the system ends up spending a
> very large percentage of time in retries. On the other hand, with a
> deadlock it would just plain hang and avoiding deadlocks can be very
> complicated; whereas every time several transactions try to go through
> at the same time at least *one* of them should succeed, so the system
> will always be making progress and won't completely hang, and
> adjusting things like the granularity of your refs can reduce
> contention while at the same time you don't have to worry about
> locking order and deterministic symmetry-breaking and all that stuff
> to try to keep deadlocks from happening.

Yep it could certainly suffer from starvation and lead to livelock.
Imagine there's two threads hitting an atom really hard.  One of them
sends an update function that's really quick, the other sends an update
function that takes say, 2 seconds.  The thread with the slow update
function is going to be starved and never get a chance to execute.  If
the fast thread depends on some change the slow thread makes then that's
a livelock situation.

Generally I guess though if there's a lot of contention you're probably
not getting a good deal of benefit from concurrency anyway so you
probably need to try a different approach.  I'd certainly want to avoid
an atom update function that took any significant amount of time.

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to