Comment 3 for bug 927179

Revision history for this message
Werner Thie (werner-thieprojects) wrote : Re: [Bug 927179] Re: _athenaDetachClient detaches only every other client

On 05.02.2012 18:16, Jean-Paul Calderone wrote:
> Do you happen to know the divmod.org bug number?

Found the original mail, no ticket was filed.

Werner

---- original sent to <email address hidden>

Attaching/Detaching LiveElements is a lot of fun but while ensuring that
everything got detached I stumbled over the problem that out of 36
fragments only 18 (the even numbered ones) received a detach call. This
pattern smelled a lot like there is an iteration over a list happening
while somewhere in the code items are removed from the list. A quick
debugging session confirmed that this is in fact so. Consider the
original code on the server in nevow/athena for:

def _athenaDetachServer(self):
   """
   Locally remove this from its parent.

   @raise OrphanedFragment: if not attached to a parent.
   """
   if self.fragmentParent is None:
     raise OrphanedFragment(self)
   for ch in self.liveFragmentChildren:
     ch._athenaDetachServer()
   self.fragmentParent.liveFragmentChildren.remove(self)
   self.fragmentParent = None
   self.page = None
   self.detached()
expose(_athenaDetachServer)

which detaches every other liveFragmentChildren, whereas the code below
detaches all liveFragmentChildren, working with a copy of the list while
iterating:

def _athenaDetachServer(self):
   """
   Locally remove this from its parent.

   @raise OrphanedFragment: if not attached to a parent.
   """
   if self.fragmentParent is None:
     raise OrphanedFragment(self)
   #need a copy the children's list for undisturbed cleanup
   for ch in list(self.liveFragmentChildren):
     ch._athenaDetachServer()
   self.fragmentParent.liveFragmentChildren.remove(self)
   self.fragmentParent = None
   self.page = None
   self.detached()
expose(_athenaDetachServer)

The same holds true for the client code in nevow/js/__init__.js, where

function _athenaDetachClient(self) {
   for (var i = 0; i < self.childWidgets.length; ++i) {
     self.childWidgets[i]._athenaDetachClient();
   }
   if (self.widgetParent !== null) {
     self.widgetParent.removeChildWidget(self);
   }
   delete Nevow.Athena.Widget._athenaWidgets[self.objectID];
   self.detached();
},

becomes

function _athenaDetachClient(self) {
   var childWidgets = [];
   //need a copy the children's list for undisturbed cleanup
   for (var i = 0; i < self.childWidgets.length; ++i) {
     childWidgets[i] = self.childWidgets[i];
   }
   for (var i = 0; i < childWidgets.length; ++i) {
     childWidgets[i]._athenaDetachClient();
   }
   if (self.widgetParent !== null) {
     self.widgetParent.removeChildWidget(self);
   }
   delete Nevow.Athena.Widget._athenaWidgets[self.objectID];
         self.detached();
},