diff options
author | Helmut Grohne <helmut@subdivi.de> | 2012-11-01 11:35:49 +0100 |
---|---|---|
committer | Helmut Grohne <helmut@subdivi.de> | 2012-11-01 11:38:22 +0100 |
commit | 576bd830cf77dae71b2cd10f0241667fe48930a6 (patch) | |
tree | fbd06a7740f540060db957a92dc3757067443002 /wsgitools/scgi/forkpool.py | |
parent | dd8148e11636d6c3792e8ceb03478048ae8eb571 (diff) | |
download | wsgitools-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.py | 32 |
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): """ |