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

Reply via email to