shebangs need to point at python2
[sfa.git] / sfa / util / callids.py
1 #!/usr/bin/env python2
2
3 import threading
4 import time
5
6 from sfa.util.sfalogging import 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 debug = False
15
16
17 class _call_ids_impl (dict):
18
19     _instance = None
20     # 5 minutes sounds amply enough
21     purge_timeout = 5 * 60
22     # when trying to get a lock
23     retries = 10
24     # in ms
25     wait_ms = 100
26
27     def __init__(self):
28         self._lock = threading.Lock()
29
30     # the only primitive
31     # return True if the callid is unknown, False otherwise
32     def already_handled(self, call_id):
33         # if not provided in the call...
34         if not call_id:
35             return False
36         has_lock = False
37         for attempt in range(_call_ids_impl.retries):
38             if debug:
39                 logger.debug("Waiting for lock (%d)" % attempt)
40             if self._lock.acquire(False):
41                 has_lock = True
42                 if debug:
43                     logger.debug("got lock (%d)" % attempt)
44                 break
45             time.sleep(float(_call_ids_impl.wait_ms) / 1000)
46         # in the unlikely event where we can't get the lock
47         if not has_lock:
48             logger.warning(
49                 "_call_ids_impl.should_handle_call_id: could not acquire lock")
50             return False
51         # we're good to go
52         if call_id in self:
53             self._purge()
54             self._lock.release()
55             return True
56         self[call_id] = time.time()
57         self._purge()
58         self._lock.release()
59         if debug:
60             logger.debug("released lock")
61         return False
62
63     def _purge(self):
64         now = time.time()
65         o_keys = []
66         for (k, v) in self.iteritems():
67             if (now - v) >= _call_ids_impl.purge_timeout:
68                 o_keys.append(k)
69         for k in o_keys:
70             if debug:
71                 logger.debug("Purging call_id %r (%s)" % (
72                     k, time.strftime("%H:%M:%S", time.localtime(self[k]))))
73             del self[k]
74         if debug:
75             logger.debug("AFTER PURGE")
76             for (k, v) in self.iteritems():
77                 logger.debug("%s -> %s" %
78                              (k, time.strftime("%H:%M:%S", time.localtime(v))))
79
80
81 def Callids():
82     if not _call_ids_impl._instance:
83         _call_ids_impl._instance = _call_ids_impl()
84     return _call_ids_impl._instance