File contents
##############################################################################
#
# 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()