webish.py: fix for #237: when listing large directories, insert a turn break once every 100 children, to work around non-optimized tail recursion Deferreds

This commit is contained in:
Brian Warner 2008-02-12 21:28:52 -07:00
parent e400c5a8fb
commit 124b2160b9

View File

@ -18,6 +18,7 @@ from allmydata import get_package_versions_string
from zope.interface import implements, Interface from zope.interface import implements, Interface
import urllib import urllib
from formless import webform from formless import webform
from foolscap.eventual import fireEventually
from nevow.util import resource_filename from nevow.util import resource_filename
@ -209,6 +210,25 @@ class Directory(rend.Page):
def data_children(self, ctx, data): def data_children(self, ctx, data):
d = self._dirnode.list() d = self._dirnode.list()
d.addCallback(lambda dict: sorted(dict.items())) d.addCallback(lambda dict: sorted(dict.items()))
def _stall_some(items):
# Deferreds don't optimize out tail recursion, and the way
# Nevow's flattener handles Deferreds doesn't take this into
# account. As a result, large lists of Deferreds that fire in the
# same turn (i.e. the output of defer.succeed) will cause a stack
# overflow. To work around this, we insert a turn break after
# every 100 items, using foolscap's fireEventually(). This gives
# the stack a chance to be popped. It would also work to put
# every item in its own turn, but that'd be a lot more
# inefficient. This addresses ticket #237, for which I was never
# able to create a failing unit test.
output = []
for i,item in enumerate(items):
if i % 100 == 0:
output.append(fireEventually(item))
else:
output.append(item)
return output
d.addCallback(_stall_some)
return d return d
def render_row(self, ctx, data): def render_row(self, ctx, data):