summaryrefslogtreecommitdiff
path: root/wsgitools/scgi/forkpool.py
diff options
context:
space:
mode:
authorHelmut Grohne <helmut@subdivi.de>2012-11-01 11:35:49 +0100
committerHelmut Grohne <helmut@subdivi.de>2012-11-01 11:38:22 +0100
commit576bd830cf77dae71b2cd10f0241667fe48930a6 (patch)
treefbd06a7740f540060db957a92dc3757067443002 /wsgitools/scgi/forkpool.py
parentdd8148e11636d6c3792e8ceb03478048ae8eb571 (diff)
downloadwsgitools-576bd830cf77dae71b2cd10f0241667fe48930a6.tar.gz
scgi.forkpool: implement RLIMIT_CPU
The limit is only set on workers does not apply to the master. Upon reaching the soft limit the worker terminates after finished the current request.
Diffstat (limited to 'wsgitools/scgi/forkpool.py')
-rw-r--r--wsgitools/scgi/forkpool.py32
1 files changed, 31 insertions, 1 deletions
diff --git a/wsgitools/scgi/forkpool.py b/wsgitools/scgi/forkpool.py
index 52240f2..f27cd25 100644
--- a/wsgitools/scgi/forkpool.py
+++ b/wsgitools/scgi/forkpool.py
@@ -5,6 +5,10 @@ It works with multiple processes that are periodically cleaned up to prevent
memory leaks having an impact to the system.
"""
+try:
+ import resource
+except ImportError:
+ resource = None
import socket
import os
import select
@@ -178,7 +182,7 @@ class SCGIServer:
def __init__(self, wsgiapp, port, interface="localhost", error=sys.stderr,
minworkers=2, maxworkers=32, maxrequests=1000, config={},
- reusesocket=None):
+ reusesocket=None, cpulimit=None):
"""
@param wsgiapp: is the WSGI application to be run.
@type port: int
@@ -203,6 +207,12 @@ class SCGIServer:
Instead use given socket as listen socket. The passed socket
must be set up for accepting tcp connections (i.e. C{AF_INET},
C{SOCK_STREAM} with bind and listen called).
+ @type cpulimit: (int, int)
+ @param cpulimit: a pair of soft and hard cpu time limit in seconds.
+ This limit is installed for each worker using RLIMIT_CPU if
+ resource limits are available to this platform. After reaching
+ the soft limit workers will continue to process the current
+ request and then cleanly terminate.
"""
assert hasattr(error, "write")
self.wsgiapp = wsgiapp
@@ -213,6 +223,10 @@ class SCGIServer:
self.config = config.copy()
self.config["wsgi.errors"] = error
self.reusesocket = reusesocket
+ # cpulimit changes meaning:
+ # master: None or a tuple denoting the limit to be configured.
+ # worker: boolean denoting whether the limit is reached.
+ self.cpulimit = cpulimit
self.server = None # becomes a socket
# maps filedescriptors to WorkerStates
self.workers = {}
@@ -302,6 +316,15 @@ class SCGIServer:
else:
self.running = False
+ def sigxcpuhandler(self, sig=None, stackframe=None):
+ """
+ Signal hanlder function for the SIGXCUP signal. It is sent to a
+ worker when the soft RLIMIT_CPU is crossed.
+ @param sig: ignored for usage with signal.signal
+ @param stackframe: ignored for usage with signal.signal
+ """
+ self.cpulimit = True
+
def spawnworker(self):
"""
internal! spawns a single worker
@@ -317,6 +340,11 @@ class SCGIServer:
worker.sock.close()
del self.workers
+ if self.cpulimit and resource:
+ signal.signal(signal.SIGXCPU, self.sigxcpuhandler)
+ resource.setrlimit(resource.RLIMIT_CPU, self.cpulimit)
+ self.cpulimit = False
+
try:
self.work(worksock)
except socket.error:
@@ -343,6 +371,8 @@ class SCGIServer:
worksock.sendall('1') # tell server we're working
self.process(con)
worksock.sendall('0') # tell server we've finished
+ if self.cpulimit:
+ break
def process(self, con):
"""