On Sun, 2008-11-16 at 01:35 +0100, [EMAIL PROTECTED] wrote: > Currently agent errors are only reported when the agent is derefenced > or further actions are dispatched to the agent. It would be great if > one can get immediate notification of agent errors maybe through a > callback.
I also have a need for early error reporting in case of agent errors, so I changed Agent.java to optionally take an addition error handler function that will be used as a callback as soon as exceptions occur. For example: user=> (def agt (agent 0)) #'user/agt user=> (defn trouble [_] (throw (new Exception "Trouble"))) #'user/trouble user=> (send agt trouble) #<Agent [EMAIL PROTECTED]> user=> @agt java.lang.Exception: Agent has errors (NO_SOURCE_FILE:0) user=> (clear-agent-errors agt) nil user=> @agt 0 user=> (set-errorhandler agt (fn [a e] (.printStackTrace e) (clear-agent-errors a))) nil user=> (send agt trouble) java.lang.Exception: Trouble at user$trouble__2.invoke(Unknown Source) at clojure.lang.AFn.applyToHelper(AFn.java:193) at clojure.lang.AFn.applyTo(AFn.java:184) at clojure.lang.Agent$Action.doRun(Agent.java:72) at clojure.lang.Agent$Action.run(Agent.java:117) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619) #<Agent [EMAIL PROTECTED]> user=> @agt 0 user=> (def agt (agent 0 :error-fn (fn [a e] (.printStackTrace e) (clear-agent-errors a)))) #'user/agt #'user/agt user=> (send agt trouble) java.lang.Exception: Trouble at user$trouble__2.invoke(Unknown Source) at clojure.lang.AFn.applyToHelper(AFn.java:193) at clojure.lang.AFn.applyTo(AFn.java:184) at clojure.lang.Agent$Action.doRun(Agent.java:72) at clojure.lang.Agent$Action.run(Agent.java:117) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:619) #<Agent [EMAIL PROTECTED]> user=> @agt 0 The agent function is now keyword based and accepts :error-fn and/or :validate-fn. Alternatively error handlers can be set on an agent later. Since the change of the agent function is breaking existing code it is probably not what you (or Rich) want to have. Overloading it to take 3 parameters might be the better way. Anyway I second your request and would love to see some error reporting functionality in agents, as this really helped me a couple of times. Cheers, Toralf --~--~---------~--~----~------------~-------~--~----~ 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 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 -~----------~----~----~----~------~----~------~--~---
diff --git a/trunk/src/clj/clojure/core.clj b/trunk/src/clj/clojure/core.clj index a6d7ca2..73a2e06 100644 --- a/trunk/src/clj/clojure/core.clj +++ b/trunk/src/clj/clojure/core.clj @@ -1033,14 +1033,20 @@ [sym] (. clojure.lang.Var (find sym))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Refs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(def array-map) + (defn agent "Creates and returns an agent with an initial value of state and an optional validate fn. validate-fn must be nil or a side-effect-free fn of one argument, which will be passed the intended new state on any state change. If the new state is unacceptable, the validate-fn should throw an exception." - ([state] (new clojure.lang.Agent state)) - ([state validate-fn] (new clojure.lang.Agent state validate-fn))) + ([state] + (new clojure.lang.Agent state)) + ([state & fns] + (let [fnmap (apply array-map fns)] + (new clojure.lang.Agent state (:validate-fn fnmap) (:error-fn fnmap))))) (defn ! [& args] (throw (new Exception "! is now send. See also send-off"))) @@ -1127,6 +1133,12 @@ "Gets the validator-fn for a var/ref/agent." [#^clojure.lang.IRef iref] (. iref (getValidator))) +(defn set-errorhandler + [#^clojure.lang.IRef iref errorhandler-fn] (. iref (setErrorHandler errorhandler-fn))) + +(defn get-errorhandler + [#^clojure.lang.IRef iref] (. iref (getErrorHandler))) + (defn commute "Must be called in a transaction. Sets the in-transaction-value of ref to: diff --git a/trunk/src/jvm/clojure/lang/Agent.java b/trunk/src/jvm/clojure/lang/Agent.java index 4c0fc71..324de96 100644 --- a/trunk/src/jvm/clojure/lang/Agent.java +++ b/trunk/src/jvm/clojure/lang/Agent.java @@ -19,6 +19,7 @@ import java.util.Map; public class Agent implements IRef{ volatile Object state; volatile IFn validator = null; +volatile IFn errorhandler = null; AtomicReference<IPersistentStack> q = new AtomicReference(PersistentQueue.EMPTY); AtomicReference<IPersistentMap> watchers = new AtomicReference(PersistentHashMap.EMPTY); @@ -77,11 +78,9 @@ static class Action implements Runnable{ } catch(Exception e) { - //todo report/callback action.agent.errors = RT.cons(e, action.agent.errors); hadError = true; } - if(!hadError) { for(ISeq s = nested.get().seq(); s != null; s = s.rest()) @@ -90,6 +89,9 @@ static class Action implements Runnable{ a.agent.enqueue(a); } } + else + action.agent.handleError(action.agent.errorhandler); + boolean popped = false; IPersistentStack next = null; @@ -117,12 +119,17 @@ static class Action implements Runnable{ } public Agent(Object state) throws Exception{ - this(state,null); + this(state, null, null); } public Agent(Object state, IFn validator) throws Exception{ - this.validator = validator; - setState(state); + this(state, validator, null); +} + +public Agent(Object state, IFn validator, IFn errorhandler) throws Exception{ + this.validator = validator; + this.errorhandler = errorhandler; + setState(state); } boolean setState(Object newState) throws Exception{ @@ -143,6 +150,17 @@ void validate(IFn vf, Object val){ } } +void handleError(IFn errorhandler){ + if(errorhandler != null) + try{ + errorhandler.invoke(this, RT.first(errors)); + } + catch(Exception e) + { + throw new IllegalStateException("Exception in error handler", e); + } +} + public Object get() throws Exception{ if(errors != null) { @@ -160,6 +178,14 @@ public IFn getValidator(){ return validator; } +public void setErrorHandler(IFn eh){ + errorhandler = eh; +} + +public IFn getErrorHandler(){ + return errorhandler; +} + public ISeq getErrors(){ return errors; } diff --git a/trunk/src/jvm/clojure/lang/Compiler.java b/trunk/src/jvm/clojure/lang/Compiler.java index 9b9b019..d83be38 100644 --- a/trunk/src/jvm/clojure/lang/Compiler.java +++ b/trunk/src/jvm/clojure/lang/Compiler.java @@ -4458,7 +4458,7 @@ static public void writeClassFile(String internalName, byte[] bytecode) throws E } } -static void pushNS(){ +public static void pushNS(){ Var.pushThreadBindings(PersistentHashMap.create(Var.intern(Symbol.create("clojure.core"), Symbol.create("*ns*")),null)); }