On Wed, Sep 11, 2013 at 2:47 AM, Brandon Ibach <brandon.l.ib...@gmail.com>wrote:
> 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. > > How are you calling send without the rest of clojure having been initialized? > 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. > > -- -- 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.