compare-and-set deep value in hashmap atom

72 views
Skip to first unread message

Jeremy Vuillermet

unread,
Feb 1, 2016, 7:29:39 AM2/1/16
to Clojure
Hello,

I'm making a game where players can choose a box in a grid to discover what's behind it.

My data structure is an atom with {:grid-size 5, :picks {5 "player1}} where 5 is the box position in the grid.

How can I be sure that two players can't pick the same box. From my understanding, checking the box and then swaping if the box is free is not enough.
So I guessed I should use compare and set but I only understand how it works with simple atom like (atom 1).

2 questions then : Is my data structure adapted for that ?

How to compare and set only for a nested path like [:picks 5] ?

Thanks

William la Forge

unread,
Feb 1, 2016, 9:27:49 AM2/1/16
to Clojure
The easy way is the big atom approach. Put the entire structure in an atom and use swap! to make updates.

First, the function passed to swap! should be free of side-effects, as swap! may need to call it more than once in the case of a collision.

Second, within the function passed to swap! is where you check to see if the resource is available. If it is, you take it. Otherwise not.

Now as for suitability. Note that the function passed to swap! must return the replacement value of your immutable structure. So it important
that your structure records who got the resource being requested. And it looks like your structure handles that.

No need to use compare and set. The swap! function is good enough. It is really a convenience function layered over compare-and-set.
Sometimes swap! is not powerful enough, which is when you use compare-and-set.

Jeremy Vuillermet

unread,
Feb 1, 2016, 10:48:21 AM2/1/16
to clo...@googlegroups.com
That's very clear thank you. Some follow up questions: 

In my case, I still need to notify every player that the resource is not available anymore and that is a side effect.
Should I just make it idempotent so it doesn't matter if it's called more than once.

If I check for the resource within the function passed to swap! and do my side effect only when it's available, It should not happen more than once, right ?

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@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+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/X59ZTmPQhZ0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

William la Forge

unread,
Feb 1, 2016, 10:58:38 AM2/1/16
to Clojure
True, but very bad practice. Better to check after the swap! completes to see if it was successful and then send the notifications.

If you do the notifications within the swap! function, you are holding the atom for longer than need be and increasing the chance of contention. Contention means that the other contenders keep looping until they are able to acquire the atom. And that means a potentially significant increase in overhead. compare-and-set! and swap! only work well when the number of contenders is small, and a slow swap! function increases the chance of having other contenders more frequently.

When contention is high, atoms are a poor choice. That's when you switch over to something like async.core. :-)

Matching Socks

unread,
Feb 1, 2016, 7:52:19 PM2/1/16
to Clojure
There is another idiom to consider if you need to involve a side effect like notification.  See http://clojure.org/reference/agents.  You use a ref instead of an atom, you modify the ref and trigger the notification (by sending a function to an agent) inside a transaction, and the agent runs the function (which notifies someone) asynchronously and only if the transaction commits.  That technique reconciles the STM system's need to sometimes run transactions more than once until they commit, with the desire to perform side effects only once.
Reply all
Reply to author
Forward
0 new messages