71 lines
1.9 KiB
Python
71 lines
1.9 KiB
Python
import functools
|
|
import logging
|
|
import sys
|
|
import threading
|
|
import time
|
|
|
|
log = logging.getLogger('ldap-web.lru')
|
|
|
|
def locked(f):
|
|
@functools.wraps(f)
|
|
def func(self, *a, **kw):
|
|
self.lock.acquire()
|
|
try:
|
|
return f(self, *a, **kw)
|
|
finally:
|
|
self.lock.release()
|
|
return func
|
|
|
|
class LRUPool(threading.Thread):
|
|
"""A key-value pool to store objects with a timeout.
|
|
|
|
Consumers of objects can register callbacks which will be called on
|
|
object expiry. Expiry is least-recently-used - any access reset the
|
|
time counter."""
|
|
def __init__(self, timeout=60.0, **kw):
|
|
threading.Thread.__init__(self, **kw)
|
|
self.setDaemon(True)
|
|
self.pool = {}
|
|
self.timeout = timeout
|
|
self.lock = threading.Lock()
|
|
self.callbacks = {}
|
|
def run(self):
|
|
log.info("Pool {} starting.", self)
|
|
while True:
|
|
time.sleep(self.timeout / 2)
|
|
self.lock.acquire()
|
|
now = time.time()
|
|
drop = set()
|
|
for k, [c, atime] in self.pool.items():
|
|
if now - atime > self.timeout:
|
|
log.info("Pool {} dropping {}.", self, k)
|
|
drop.add(k)
|
|
|
|
for k in list(drop):
|
|
self._drop(k)
|
|
self.lock.release()
|
|
def register_callback(self, action, cb):
|
|
self.callbacks.setdefault(action, []).append(cb)
|
|
@locked
|
|
def __getitem__(self, key):
|
|
item = self.pool.get(key)
|
|
if not item:
|
|
return
|
|
item[1] = time.time()
|
|
return item[0]
|
|
def _insert(self, key, item):
|
|
self.pool[key] = [item, time.time()]
|
|
return item
|
|
@locked
|
|
def insert(self, key, item):
|
|
return self._insert(key, item)
|
|
def _drop(self, key):
|
|
for f in self.callbacks.get('drop',[]):
|
|
f(key)
|
|
return self.pool.pop(key, None)
|
|
@locked
|
|
def drop(self, key):
|
|
return self._drop(key)
|
|
|
|
|