Django

Code

Changeset 8688

Show
Ignore:
Timestamp:
08/28/08 20:44:11 (3 months ago)
Author:
mtredinnick
Message:

Fixed #8616 -- Fixed a race condition in the file-based session backend.
Thanks to warren@wandrsmith.net for the patch.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/AUTHORS

    r8672 r8688  
    364364    sloonz <simon.lipp@insa-lyon.fr> 
    365365    SmileyChris <smileychris@gmail.com> 
     366    Warren Smith <warren@wandrsmith.net> 
    366367    smurf@smurf.noris.de 
    367368    Vsevolod Solovyov 
  • django/trunk/django/contrib/sessions/backends/file.py

    r8451 r8688  
    66from django.contrib.sessions.backends.base import SessionBase, CreateError 
    77from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured 
     8from django.core.files import locks 
    89 
     10IO_LOCK_SUFFIX = "_iolock" 
    911 
    1012class SessionStore(SessionBase): 
     
    4345        return os.path.join(self.storage_path, self.file_prefix + session_key) 
    4446 
     47    def _key_to_io_lock_file(self, session_key=None): 
     48        """ 
     49        Get the I/O lock file associated with this session key. 
     50        """ 
     51        return self._key_to_file(session_key) + IO_LOCK_SUFFIX 
     52 
    4553    def load(self): 
    4654        session_data = {} 
    4755        try: 
    48             session_file = open(self._key_to_file(), "rb") 
     56            # Open and acquire a shared lock on the I/O lock file before 
     57            # attempting to read the session file.  This makes us wait to read 
     58            # the session file until another thread or process is finished 
     59            # writing it. 
     60            lock_path = self._key_to_io_lock_file() 
     61            io_lock_file = open(lock_path, "rb") 
     62            locks.lock(io_lock_file, locks.LOCK_SH) 
    4963            try: 
     64                session_file = open(self._key_to_file(), "rb") 
    5065                try: 
    51                     session_data = self.decode(session_file.read()) 
    52                 except (EOFError, SuspiciousOperation): 
    53                     self.create() 
     66                    try: 
     67                        session_data = self.decode(session_file.read()) 
     68                    except (EOFError, SuspiciousOperation): 
     69                        self.create() 
     70                finally: 
     71                    session_file.close() 
    5472            finally: 
    55                 session_file.close() 
     73                locks.unlock(io_lock_file) 
     74                io_lock_file.close() 
     75                os.unlink(lock_path) 
    5676        except IOError: 
    5777            pass 
     
    7797        session_data = self._get_session(no_load=must_create) 
    7898        try: 
    79             fd = os.open(self._key_to_file(self.session_key), flags) 
     99            # Open and acquire an exclusive lock on the I/O lock file before 
     100            # attempting to write the session file.  This makes other threads 
     101            # or processes wait to read or write the session file until we are 
     102            # finished writing it. 
     103            lock_path = self._key_to_io_lock_file() 
     104            io_lock_file = open(lock_path, "wb") 
     105            locks.lock(io_lock_file, locks.LOCK_EX) 
    80106            try: 
    81                 os.write(fd, self.encode(session_data)) 
     107                fd = os.open(self._key_to_file(self.session_key), flags) 
     108                try: 
     109                    os.write(fd, self.encode(session_data)) 
     110                finally: 
     111                    os.close(fd) 
    82112            finally: 
    83                 os.close(fd) 
     113                locks.unlock(io_lock_file) 
     114                io_lock_file.close() 
     115                os.unlink(lock_path) 
    84116        except OSError, e: 
    85117            if must_create and e.errno == errno.EEXIST: