I have found what appears to be a bug in LockingTransaction, albeit one 
that probably wouldn't occur often.  But, I suppose that's a given for a 
previously undiscovered problem in oft-used code that hasn't changed for 
some while. :)

I'm using the Clojure 1.4 library strictly from Java code and I have a 
fairly simple transaction which dispatches an action to an agent (send, not 
send-off).  When called from a JUnit test, such that we jump right in to 
things, skipping some of the initialization we normally do in our app, I 
get a ConcurrentModificationException from inside Locktransaction.run() 
while it is iterating through the "actions" list, dispatching the actions 
after committing the transaction.

Based on some debugging, here's what seems to be happening:

1. My transaction is run, dispatching an action to an agent.
2. The transaction completes and is successfully committed.
3. LockingTransaction does post-commit cleanup, freeing locks and putting a 
stop() to the transaction, which nulls the transaction's Info object 
reference.
4. Notifications are sent and we start iterating the list of actions to be 
dispatched.
5. The run() method calls Agent.dispatchAction().  Because the thread's 
transaction is no longer "running" (due to the Info object being null) and 
no action is being processed on the thread (so its "nested" vector is 
null), the action is enqueue()d with the agent.
6. As part of the enqueue process, the action is consed onto the agent's 
ActionQueue.  Here's where the unique circumstances come into play.
   a. At this point, we haven't really interacted with the Clojure runtime, 
specifically the RT class, so its initiation machinery kicks in.
   b. Down in the depths, it executes a transaction to add a library to its 
list of loaded libraries.
   c. The still-existing-but-not-running thread-local transaction, with its 
existing action list intact, fires up, runs and commits.
   d. The post-commit stuff runs, including a nested attempt to dispatch 
that same action, again, which apparently succeeds.
   e. The action list is cleared before exiting the run() method.
7. Upon returning way back up the stack to our 
not-quite-finished-post-processing transaction, we continue iterating the 
now-cleared action list, which promptly throws the 
ConcurrentModificationException.

A quick perusal of the LockingTransaction code shows that the only 
interaction with the action list is adding an item to it in the enqueue() 
method, iterating it in the post-processing section of run() and clearing 
it in the "finally" clause of that section, so it's easy to see how a 
transaction started by any of the action-dispatching machinery would cause 
problems.  Any such activity in the actions themselves would not be an 
issue, since they'd occur on other threads, but the dispatch stuff all runs 
on the same thread.  The few moving parts that occur in this code seem 
fairly safe, as long as the runtime is already initialized, but if that 
occurs during this phase, I think all bets are off.

Does this analysis seem sound?  If so, is there a more formal process for 
filing this as a bug?  I can probably create a nice, compact example to 
reproduce it.

Thanks!

-Brandon :)

-- 
-- 
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
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to