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));
 }

Reply via email to