a framework for handling call ids
[sfa.git] / sfa / util / callids.py
1 #!/usr/bin/python
2
3 import threading
4 import time
5
6 from sfa.util.sfalogging import sfa_logger
7
8 """
9 Callids: a simple mechanism to remember the call ids served so fas
10 memory-only for now - thread-safe
11 implemented as a (singleton) hash 'callid'->timestamp
12 """
13
14 class _call_ids_impl (dict):
15
16     _instance = None
17     # 5 minutes sounds amply enough
18     purge_timeout=5*60
19     # when trying to get a lock
20     retries=8
21     # in ms
22     wait_ms=200
23
24     def __init__(self): 
25         self._lock=threading.Lock()
26
27     # the only primitive
28     # return True if the callid is unknown, False otherwise
29     def should_handle_call_id (self,call_id):
30         # if not provided in the call...
31         if not call_id: return True
32         has_lock=False
33         for attempt in range(_call_ids_impl.retries):
34             sfa_logger().debug("Waiting for lock (%d)"%attempt)
35             if self._lock.acquire(False): 
36                 has_lock=True
37                 sfa_logger().debug("got lock (%d)"%attempt)
38                 break
39             time.sleep(float(_call_ids_impl.wait_ms)/1000)
40         # in the unlikely event where we can't get the lock
41         if not has_lock:
42             sfa_logger().warning("_call_ids_impl.should_handle_call_id: could not acquire lock")
43             return True
44         # we're good to go
45         if self.has_key(call_id):
46             self._purge()
47             self._lock.release()
48             return False
49         self[call_id]=time.time()
50         self._purge()
51         self._lock.release()
52         sfa_logger().debug("released lock")
53         return True
54         
55     def _purge(self):
56         now=time.time()
57         o_keys=[]
58         for (k,v) in self.iteritems():
59             if (now-v) >= _call_ids_impl.purge_timeout: o_keys.append(k)
60         for k in o_keys: 
61             sfa_logger().debug("Purging call_id %r (%s)"%(k,time.strftime("%H:%M:%S",time.localtime(self[k]))))
62             del self[k]
63         sfa_logger().debug("AFTER PURGE")
64         for (k,v) in self.iteritems(): sfa_logger().debug("%s -> %s"%(k,time.strftime("%H:%M:%S",time.localtime(v))))
65         
66 def Callids ():
67     if not _call_ids_impl._instance:
68         _call_ids_impl._instance = _call_ids_impl()
69     return _call_ids_impl._instance