On Jun 10, 2009, at 11:23 PM, Robert Lehr wrote:
>> As you noted, send-off uses a cached thread pool, so there is no >> protection from creating more threads than your system can handle > > OK - that's one person that agrees w/ me. I don't think anyone disagrees on that point. >> In general cached thread pools should be used only for short lived >> tasks. >> The DOS protection must come from somewhere else in your system. > > Right - as long as it *IS* solved elsewhere in the system. What on earth could constitute in-language DOS prevention? > The potential has to be recognized first which is impossible unless > it is > documented or one reads the source code. Some aspects of programming are best performed by programmers. If you want to call a thread pool a DOS threat, I'd like to introduce you to my friends automatic memory management, the call stack and I/O in general. :) > Yeah...I saw that. Except that the code assumes that the agent will > be modified by only the agent's callback mechanism. I think we all > will recognize that that is not the strongest protection. It is > perhaps satisfactory b/c it is idiomatic Clojure to modify an agent's > state ONLY via the callback. I don't think there are any other ways to modify an agent. I get a class cast exception or unable to alter root binding exception on all of these: user> (set! a 4) user> (dosync (set! a 4)) user> (alter a inc) user> (dosync (alter a inc)) user> (ref-set a 4) user> (dosync (ref-set a 4)) I don't know Java very well but reading the source code of Agent.java seems to indicate that the only way to change its state is by setting it directly or calling setState(), but neither of those options are public (I'm not aware of a way to set a property from Clojure anyway, and not sure it can be done in Java either, though I don't know). This fails because it can't find the method: user> (.setState a 5) The bean function doesn't even return the current value: user> (bean a) {:watches {}, :validator nil, :queueCount 0, :errors nil, :class clojure.lang.Agent} In other words, send and send-off are not merely idiomatic, they are the law. The only other thing you can do is to re-def the name of the agent with a new agent, but if you did this, any live references to the old agent such as closures or other threads created from this thread prior to the def would be unaffected and any actions pending for the old agent (targetted at that var or not) will continue to process for that var rather than being reassigned to the new one. You'd have to go through quite a bit of work to circumvent these effects, which are all consistent with pure FP. Let me illustrate: user> (defn increment-me-and-a-friend-slowly [n friend] (Thread/sleep 3000) (send-off friend inc) (inc n)) The idea here is to call this function with send-off and another agent, and it'll increment the friend agent and then itself after sleeping for 3 seconds. Now let's have some agents. They have the same value for illustrative purposes. user> (def a (agent 1)) #'user/a user> (def b (agent 1)) #'user/b For fun, let's make an alias to a called c. user> (def c a) #'user/c Same memory address, so it must be the same one: user> a #<ag...@3c7169: 1> user> b #<ag...@511470: 1> user> c #<ag...@3c7169: 1> Now let's try it and see what happens: user> (send-off a increment-me-and-a-friend-slowly b) #<ag...@3c7169: 1> user> a #<ag...@3c7169: 2> user> b #<ag...@511470: 2> user> c #<ag...@3c7169: 2> Looks good to me. Now let's try it but redefine something right away after the send off before 3 seconds have elapsed: user> (do (send-off a increment-me-and-a-friend-slowly b) (def a (agent -5))) #'user/a user> a #<ag...@6d82a: -5> user> b #<ag...@511470: 3> user> c #<ag...@3c7169: 3> user> (= a c) false To whit: if you screw around with def, you're going to have way more than the occasional hiccup. Your app will be dramatically and obviously wrong. Which leaves you with send and send-off. As an aside, I think your main problem is that you have an imperative conception of what a variable is. Variables in FP are just named values, not fixed pointers to raw hunks of memory that can be manipulated directly. In C++-ish jargon, think of every variable in Clojure as being a volatile pointer to a const piece of data of the appropriate type. A ref is an indirection on that, but it's more like an object with an API for changing the value which places certain demands on the caller, namely that a transaction be running. Agents are like that but their API consists of send and send-off. The language simply doesn't support getting the address of where these things really are in memory nor does it provide you with tools for manipulating them if you could get there. And a great deal of this is prevented not just by Clojure but by the JVM itself. There just isn't a lower level that you can get to from here like you're accustomed to in C++. — 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 -~----------~----~----~----~------~----~------~--~---