You are not logged in Log in Join
You are here: Home » Members » matt » ZServerPublisher.py » View File

Log in
Name

Password

 

ZServerPublisher.py

File details
Size
3 K
File type
text/plain

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()