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
-~----------~----~----~----~------~----~------~--~---

Reply via email to