summaryrefslogtreecommitdiff
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
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.
-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):
"""