Robert, You have some interesting questions. I'm going to share my impressions of the answers based on reading the website and the book. I am rather new to Clojure myself and I haven't read the source code to it so perhaps any mistakes I make are representative of newbie pitfalls in general or possibly even oversights in the documentation. Perhaps some of those more in-the-know can illuminate us both.
On Jun 9, 2009, at 9:14 PM, Robert Lehr wrote: > Q - What stops Clojure's agent send-off function from becoming a DOS > ("denial of service") vulnerability in a server? Well, the way I see it, there are two possibilities: 1. Threads are so cheap, we can have as many as we want and it isn't a problem (the Erlang solution) 2. Threads are expensive, so they are pooled and can be maxed out. I suspect there is a pool because pmap manages a pool internally and I don't understand why you'd call an object a CachedThreadPool if it just spawned new threads without any regard. If it is a size- constrained pool, this is what's keeping send-off from being a vulnerability. Of course, if you're being DOS'd, pretty much every function which costs anything is a vulnerability, isn't it? :) > Q - How exactly does an agent protect its "state" from concurrency > issues, particularly race conditions since I do not see any locking on > agents. Based on my understanding of how transactions work, refs are protected using MVCC or something like it. My source is the page on refs: <http://clojure.org/refs> "The Clojure MVCC STM is designed to work with the persistent collections, and it is strongly recommended that you use the Clojure collections as the values of your Refs." If MVCC is old news to you, please don't be offended by my poor explanation, and I don't pretend to know how MVCC is actually implemented in Clojure, but basically, there is a global transaction counter. This is just an integer and the only operation on it is to increment it and read it, so there is very low contention for this resource and this is probably the only traditional mutex necessary for MVCC to work. No data is permitted to be modified outside a transaction. When a transaction starts, the transaction counter is incremented and that value is assigned as the start transaction id to this transaction. Also, the IDs of all of the in-progress but not committed transactions are passed to this transaction as an invisible list. A reference is actually more than a reference. The system is keeping track of many different values for a given reference at a time, each different one being marked with a transaction ID. During your transaction, when a ref is read we see the value of the version of this variable with the highest committed transaction ID less than my transaction ID that isn't on the invisible list. This plurality of values for a ref is kept track of using some kind of thread-safe data structure that permits appending. Once added, it can only be quietly removed, not modified. In other words, under MVCC, there is no single value for a variable, so there is no location for threads to race to write. Nobody gets to see the in-progress value until the transaction ID is marked as completed, so there is no race to read it either. Presumably old versions of the ref which aren't referenced anymore get freed. > Q - how exactly is an agent's state changed? My impression is that it works like a ref that belongs to another thread. Also, from your other email, On Jun 9, 2009, at 8:32 PM, Robert Lehr wrote: > My primary concern is that for certain kinds of transactions, use of > 'commute' creates a race condition between transactions that operate > on a common set (non-null) of Refs. I'm pretty sure MVCC takes care of this problem in general. However, in theory (I don't know about Clojure specifically) there are still situations in which deadlock can occur. Some databases (PostgreSQL) have a deadlock detection system which notices when two transactions acquire resources in the wrong order and block each other from committing. Usually these systems kill the younger of the two transactions and restart it after the other one commits or aborts. I don't know whether or not Clojure has this or not or how this interplays with MVCC in general. I think as long as you always acquire resources in the same order you are protected from deadlock. I agree with the book, that the real danger with commute is using it when you have a function that isn't commutative. Anybody want to help with my errors? — Daniel Lyons http://www.storytotell.org -- Tell It! --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---