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 [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---