############################################################################## # # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## from ZPublisher import publish_module import ThreadLock gc_lock = ThreadLock.allocate_lock() GC_TRANSACTION_LIMIT = 100 # A transaction limit, to force gc GC_TRANSACTION_THRESHOLD = 10 # A lower bound on how often to gc active_threads = [0,0] # Thread count, and skipped gc call count class ZServerPublisher: def __init__(self, accept): try: import cprof cprof_enabled = 1 except: cprof_enabled = 0 import os import sys import gc import time gc.disable() if cprof_enabled: # cprof requires a specially patched python, see # http://www.zope.org/Members/matt os.environ['CRASHCMD'] = "gdb -batch -x gdb.cmd %s %s" % ( sys.executable, "%d") cprof.activate() while 1: try: name, request, response=accept() retries = 0 # Technically, active_threads[1] is accessed non-threadsafe, # but the only harm here is an extra quiesce while (GC_TRANSACTION_LIMIT and active_threads[0] and active_threads[1] > GC_TRANSACTION_LIMIT and retries < 3): # Go to sleep for a bit, trying to let remaining # threads exit -- we'll only sleep for 1 second total #sys.stderr.write("sleeping for GC\n") time.sleep(0.3) retries = retries + 1 gc_lock.acquire() active_threads[0] = active_threads[0] + 1 gc_lock.release() publish_module( name, request=request, response=response) finally: response._finish() request=response=None gc_lock.acquire() a = active_threads[0] - 1 if a == 0: if (GC_TRANSACTION_THRESHOLD and active_threads[1] < GC_TRANSACTION_THRESHOLD): #sys.stderr.write("Deferring gc.collect\n") active_threads[1] = active_threads[1] + 1 else: #sys.stderr.write("Invoking gc.collect()\n") # NOTE -- if collect() always fires (and is a problem), # the other way to do it is a gc.enable(), then make # a tuple, then gc.disable() again. Tuple creation # is hooked into gc internally, so gc internal # counters can be used to control the actual collection gc.collect() active_threads[1] = 0 else: #sys.stderr.write("Skipping gc.collect(), %d threads active\n"% a) active_threads[1] = active_threads[1] + 1 active_threads[0] = a gc_lock.release()