Best Way to Ensure a Call to (shutdown-agents)?

580 views
Skip to first unread message

Randall R Schulz

unread,
Dec 16, 2008, 1:58:13 AM12/16/08
to clo...@googlegroups.com
Hi,

I've just started using agents and as I've done that, I've noticed that
once you use an agent, exiting the REPL leads to a hang (with zero CPU
usage). As Rich pointed out to me, this is avoided by calling
(shutdown-agents).

So my question is this: What's a simple way to ensure that
(shutdown-agents) is called when necessary without having to remember
to manually enter that call at the REPL before exiting?


Randall Schulz

MikeM

unread,
Dec 16, 2008, 2:02:37 PM12/16/08
to Clojure
You can try this:

(let [t (new Thread (fn[] (shutdown-agents)))]
(.. java.lang.Runtime (getRuntime) (addShutdownHook t)))

It works for me, but seems to take a long time to complete the
shutdown.

Your question made me wonder if the agent thread pools should use
daemon threads - then this wouldn't be an issue.

Randall R Schulz

unread,
Dec 16, 2008, 3:36:21 PM12/16/08
to clo...@googlegroups.com
On Tuesday 16 December 2008 06:02, MikeM wrote:
> You can try this:
>
> (let [t (new Thread (fn[] (shutdown-agents)))]
> (.. java.lang.Runtime (getRuntime) (addShutdownHook t)))
>
> It works for me, but seems to take a long time to complete the
> shutdown.

I tried adding this to the module that starts the agents in question
(the question of the proper place for such code being left aside for
now):

(def *shutdown-hook* (atom nil))

(when-not @*shutdown-hook*
(let [shutdown-thread (new Thread shutdown-agents)]
(swap! *shutdown-hook* (fn [old] shutdown-thread))
(when (identical? @*shutdown-hook* shutdown-thread)
(.. Runtime (getRuntime) (addShutdownHook shutdown-thread))))
)

I think this is doing what I think it's doing (reliably registering only
one agent shutdown handler Thread), but exiting the REPL via CTRL-D
still hangs. I can't say it hangs literally forever, obviously, but for
longer than I'm willing to wait, certainly. So then it occurred to me
to try calling (System/exit 0), and that exits immediately. So then I
took out the code to set up the shutdown handler and found the
(System/exit 0) _still_ effects an immediate exit from the REPL.

Lastly, if I just execute Mike's code directly:

(let [shutdown-thread (new Thread shutdown-agents)]
(.. Runtime (getRuntime) (addShutdownHook shutdown-thread)))

the hang upon CTRL-D / EOF at the REPL still happens.


So I am still confused about the right way to deal with agents in order
to get reliable and prompt exit from the REPL and why an EOF from the
terminal hangs (only if agents were started and regardless of whether a
shutdown hook is in place) but (System/exit 0) does not hang (again,
regardless of whether agents were started and whether a shutdown hook
is in place).

What am I missing?


> Your question made me wonder if the agent thread pools should use
> daemon threads - then this wouldn't be an issue.

Yes. That seems plausible, but I don't know enough about the possible
subtleties to say for sure.


Randall Schulz

Randall R Schulz

unread,
Dec 16, 2008, 3:59:31 PM12/16/08
to clo...@googlegroups.com
On Tuesday 16 December 2008 07:36, Randall R Schulz wrote:
> ...

>
> So I am still confused about the right way to deal with agents in
> order to get reliable and prompt exit from the REPL and why an EOF
> from the terminal hangs (only if agents were started and regardless
> of whether a shutdown hook is in place) but (System/exit 0) does not
> hang (again, regardless of whether agents were started and whether a
> shutdown hook is in place).
>
> What am I missing?

One last piece of evidence on this. If I directly call (shutdown-agents)
before exiting the REPL using CTRL-D, then Clojure exits promptly.


Randall Schulz

Randall R Schulz

unread,
Dec 17, 2008, 6:57:43 PM12/17/08
to clo...@googlegroups.com


This sequence of evaluations ends in the REPL hanging:

1:1 user=> (.. Runtime (getRuntime) (addShutdownHook (new Thread shutdown-agents)))
nil
1:2 user=> (def agent-99 (agent "Secret agent (99)"))
#'user/agent-99
1:3 user=> (send-off agent-99 (fn [a] (println (str "In agent: " a))))
In agent: Secret agent (99)
#<Agent clojure.lang.Agent@5b0668>
1:4 user=> (await agent-99)
nil
1:5 user=>

CTRL-D

[ Hangs here. CTRL-C gets back to shell prompt. ]


Randall Schulz

Rich Hickey

unread,
Dec 17, 2008, 7:05:14 PM12/17/08
to Clojure
This is not a problem when using clojure.lang.Repl, but is when using
clojure.main.

Rich

Randall R Schulz

unread,
Dec 17, 2008, 7:10:15 PM12/17/08
to clo...@googlegroups.com
On Wednesday 17 December 2008 10:57, Randall R Schulz wrote:
> On Monday 15 December 2008 17:58, Randall R Schulz wrote:
> > ...

>
> This sequence of evaluations ends in the REPL hanging:
>
> 1:1 user=> (.. Runtime (getRuntime) (addShutdownHook (new Thread
> shutdown-agents))) nil
> 1:2 user=> (def agent-99 (agent "Secret agent (99)"))
> #'user/agent-99
> 1:3 user=> (send-off agent-99 (fn [a] (println (str "In agent: "
> a)))) In agent: Secret agent (99)
> #<Agent clojure.lang.Agent@5b0668>
> 1:4 user=> (await agent-99)
> nil
> 1:5 user=>
>
> CTRL-D
>
> [ Hangs here. CTRL-C gets back to shell prompt. ]

Since Chouser asked on #clojure which REPL (I was using the Contrib
REPL), I tried this with the stock REPL and the hang does not occur. In
fact, with the stock REPL and _without_ registering the shutdown hook,
the hang also does _not_ occur.

So it would seem this is all about the Contrib REPL, not Agents or
shutdown hooks or interactions between them.


Randall Schulz

Chouser

unread,
Dec 17, 2008, 7:31:57 PM12/17/08
to clo...@googlegroups.com
On Wed, Dec 17, 2008 at 2:10 PM, Randall R Schulz <rsc...@sonic.net> wrote:
>
> Since Chouser asked on #clojure which REPL (I was using the Contrib
> REPL), I tried this with the stock REPL and the hang does not occur.

Specifically, this line causes the JVM to shut down, even if there are
agent threads in their pools:

http://code.google.com/p/clojure/source/browse/trunk/src/jvm/clojure/lang/Repl.java?r=1160#119

I don't see a similar line in clojure.main or the recent unification
patch.

--Chouser

Stephen C. Gilardi

unread,
Dec 17, 2008, 7:35:06 PM12/17/08
to clo...@googlegroups.com
Right. I've confirmed that it works if we put (System/exit 0) in the finally clause of clojure.main/-main.

I'll get that in an updated patch.

Should we consider all exits from clojure.main/-main to be "normal", i.e., is the 0 code there appropriate? We could set it to 1 for exceptions caught by clojure.main/-main itself. A developer always has the option to call (System/exit x) manually if desired. Thoughts?

--Steve
Reply all
Reply to author
Forward
0 new messages