core (memory): Sessions fixes:

- avoid memory leak with forgotten _purgeSession in case of conflict: an exception is now raised instead
    - _purgeSession now cancel the timer if it was not done before
    - KeyError in profileGet is now explicitly raised throught a failure.Failure
    - use iterator to iterate session in ProfileSessions
This commit is contained in:
Goffi 2015-08-15 22:13:27 +02:00
commit 0a22619026

View file

@ -27,7 +27,8 @@ import copy
from collections import namedtuple
from ConfigParser import SafeConfigParser, NoOptionError, NoSectionError
from uuid import uuid4
from twisted.internet import defer, reactor
from twisted.python import failure
from twisted.internet import defer, reactor, error
from twisted.words.protocols.jabber import jid
from sat.core import exceptions
from sat.core.constants import Const as C
@ -40,6 +41,7 @@ from sat.tools import config as tools_config
PresenceTuple = namedtuple("PresenceTuple", ('show', 'priority', 'statuses'))
MSG_NO_SESSION = "Session id doesn't exist or is finished"
class Sessions(object):
"""Sessions are data associated to key used for a temporary moment, with optional profile checking."""
@ -55,7 +57,8 @@ class Sessions(object):
self.resettable_timeout = resettable_timeout
def newSession(self, session_data=None, session_id=None, profile=None):
""" Create a new session
"""Create a new session
@param session_data: mutable data to use, default to a dict
@param session_id (str): force the session_id to the given string
@param profile: if set, the session is owned by the profile,
@ -65,8 +68,7 @@ class Sessions(object):
if session_id is None:
session_id = str(uuid4())
elif session_id in self._sessions:
self._sessions[session_id][0].cancel()
log.warning(u"Session [{id}] is going to be re-initialised".format(id=session_id))
raise exceptions.ConflictError(u"Session id {} is already used".format(session_id))
timer = reactor.callLater(self.timeout, self._purgeSession, session_id)
if session_data is None:
session_data = {}
@ -74,8 +76,18 @@ class Sessions(object):
return session_id, session_data
def _purgeSession(self, session_id):
try:
timer, session_data, profile = self._sessions[session_id]
except ValueError:
timer, session_data = self._sessions[session_id]
profile = None
try:
timer.cancel()
except error.AlreadyCalled:
# if the session is time-outed, the timer has been called
pass
del self._sessions[session_id]
log.debug(u"Session [%s] purged" % session_id)
log.debug(u"Session {} purged{}".format(session_id, u' (profile {})'.format(profile) if profile is not None else u''))
def __len__(self):
return len(self._sessions)
@ -88,6 +100,8 @@ class Sessions(object):
timer, session_data, profile_set = self._sessions[session_id]
except ValueError:
raise exceptions.InternalError("You need to use __getitem__ when profile is not set")
except KeyError:
raise failure.Failure(KeyError(MSG_NO_SESSION))
if profile_set != profile:
raise exceptions.InternalError("current profile differ from set profile !")
if self.resettable_timeout:
@ -99,6 +113,8 @@ class Sessions(object):
timer, session_data = self._sessions[session_id]
except ValueError:
raise exceptions.InternalError("You need to use profileGet instead of __getitem__ when profile is set")
except KeyError:
raise failure.Failure(KeyError(MSG_NO_SESSION))
if self.resettable_timeout:
timer.reset(self.timeout)
return session_data
@ -108,12 +124,9 @@ class Sessions(object):
def __delitem__(self, session_id):
""" Cancel the timer, then actually delete the session data """
try:
timer = self._sessions[session_id][0]
timer.cancel()
self._purgeSession(session_id)
except KeyError:
log.debug(u"Session [%s] doesn't exists, timeout expired?" % session_id)
timer = self._sessions[session_id][0]
timer.cancel()
self._purgeSession(session_id)
def keys(self):
return self._sessions.keys()
@ -134,7 +147,7 @@ class ProfileSessions(Sessions):
@return: a list containing the sessions ids
"""
ret = []
for session_id in self._sessions:
for session_id in self._sessions.iterkeys():
try:
timer, session_data, profile_set = self._sessions[session_id]
except ValueError: