summaryrefslogtreecommitdiff
path: root/src/clj
diff options
context:
space:
mode:
Diffstat (limited to 'src/clj')
-rw-r--r--src/clj/clojure/core.clj89
-rw-r--r--src/clj/clojure/core_print.clj8
2 files changed, 87 insertions, 10 deletions
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 245c3643..c253fc1b 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -1329,14 +1329,29 @@
:validator validate-fn
+ :error-handler handler-fn
+
+ :error-mode mode-keyword
+
If metadata-map is supplied, it will be come the metadata on the
agent. 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
- return false or throw an exception."
- ([state] (new clojure.lang.Agent state))
+ return false or throw an exception. handler-fn is called if an
+ action throws an exception or if validate-fn rejects a new state --
+ see set-error-handler! for details. The mode-keyword may be either
+ :continue (the default if an error-handler is given) or :fail (the
+ default if no error-handler is given) -- see set-error-mode! for
+ details."
([state & options]
- (setup-reference (agent state) options)))
+ (let [a (new clojure.lang.Agent state)
+ opts (apply hash-map options)]
+ (setup-reference a options)
+ (when (:error-handler opts)
+ (.setErrorHandler a (:error-handler opts)))
+ (.setErrorMode a (or (:error-mode opts)
+ (if (:error-handler opts) :continue :fail)))
+ a)))
(defn send
"Dispatch an action to an agent. Returns the agent immediately.
@@ -1388,16 +1403,73 @@
[#^clojure.lang.IRef reference key]
(.removeWatch reference key))
+(defn agent-error
+ "Returns the exception thrown during an asynchronous action of the
+ agent if the agent is failed. Returns nil if the agent is not
+ failed."
+ [#^clojure.lang.Agent a] (.getError a))
+
+(defn restart-agent
+ "When an agent is failed, changes the agent state to new-state and
+ then un-fails the agent so that sends are allowed again. If
+ a :clear-actions true option is given, any actions queued on the
+ agent that were being held while it was failed will be discarded,
+ otherwise those held actions will proceed. The new-state must pass
+ the validator if any, or restart will throw an exception and the
+ agent will remain failed with its old state and error. Watchers, if
+ any, will NOT be notified of the new state. Throws an exception if
+ the agent is not failed."
+ [#^clojure.lang.Agent a, new-state & options]
+ (let [opts (apply hash-map options)]
+ (.restart a new-state (if (:clear-actions opts) true false))))
+
+(defn set-error-handler!
+ "Sets the error-handler of agent a to handler-fn. If an action
+ being run by the agent throws an exception or doesn't pass the
+ validator fn, handler-fn will be called with two arguments: the
+ agent and the exception."
+ [#^clojure.lang.Agent a, handler-fn]
+ (.setErrorHandler a handler-fn))
+
+(defn error-handler
+ "Returns the error-handler of agent a, or nil if there is none.
+ See set-error-handler!"
+ [#^clojure.lang.Agent a]
+ (.getErrorHandler a))
+
+(defn set-error-mode!
+ "Sets the error-mode of agent a to mode-keyword, which must be
+ either :fail or :continue. If an action being run by the agent
+ throws an exception or doesn't pass the validator fn, an
+ error-handler may be called (see set-error-handler!), after which,
+ if the mode is :continue, the agent will continue as if neither the
+ action that caused the error nor the error itself ever happened.
+
+ If the mode is :fail, the agent will become failed and will stop
+ accepting new 'send' and 'send-off' actions, and any previously
+ queued actions will be held until a 'restart-agent'. Deref will
+ still work, returning the state of the agent before the error."
+ [#^clojure.lang.Agent a, mode-keyword]
+ (.setErrorMode a mode-keyword))
+
+(defn error-mode
+ "Returns the error-mode of agent a. See set-error-mode!"
+ [#^clojure.lang.Agent a]
+ (.getErrorMode a))
(defn agent-errors
- "Returns a sequence of the exceptions thrown during asynchronous
+ "DEPRECATED: Use 'agent-error' instead.
+ Returns a sequence of the exceptions thrown during asynchronous
actions of the agent."
- [#^clojure.lang.Agent a] (. a (getErrors)))
+ [a]
+ (when-let [e (agent-error a)]
+ (list e)))
(defn clear-agent-errors
- "Clears any exceptions thrown during asynchronous actions of the
+ "DEPRECATED: Use 'restart-agent' instead.
+ Clears any exceptions thrown during asynchronous actions of the
agent, allowing subsequent actions to occur."
- [#^clojure.lang.Agent a] (. a (clearErrors)))
+ [#^clojure.lang.Agent a] (restart-agent a (.deref a)))
(defn shutdown-agents
"Initiates a shutdown of the thread pools that back the agent
@@ -2104,7 +2176,8 @@
(defn await
"Blocks the current thread (indefinitely!) until all actions
dispatched thus far, from this thread or agent, to the agent(s) have
- occurred."
+ occurred. Will block on failed agents. Will never return if
+ a failed agent is restarted with :clear-actions true."
[& agents]
(io! "await in transaction"
(when *agent*
diff --git a/src/clj/clojure/core_print.clj b/src/clj/clojure/core_print.clj
index d2b1612e..0ab1ee52 100644
--- a/src/clj/clojure/core_print.clj
+++ b/src/clj/clojure/core_print.clj
@@ -309,9 +309,13 @@
(.write w ")"))
(defmethod print-method clojure.lang.IDeref [o #^Writer w]
- (print-sequential (format "#<%s@%x: "
+ (print-sequential (format "#<%s@%x%s: "
(.getSimpleName (class o))
- (System/identityHashCode o))
+ (System/identityHashCode o)
+ (if (and (instance? clojure.lang.Agent o)
+ (agent-error o))
+ " FAILED"
+ ""))
pr-on, "", ">", (list (if (and (future? o) (not (future-done? o))) :pending @o)), w))
(def #^{:private true} print-initialized true)