ldapweb/webapp/lru.py

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)