diff --git a/zephyr/lib/context_managers.py b/zephyr/lib/context_managers.py new file mode 100644 index 0000000000..01bb0152fd --- /dev/null +++ b/zephyr/lib/context_managers.py @@ -0,0 +1,36 @@ +""" +Context managers, i.e. things you can use with the 'with' statement. +""" + +import fcntl +from os import path +from contextlib import contextmanager + +@contextmanager +def flock(lockfile, shared=False): + """Lock a file object using flock(2) for the duration of a 'with' statement. + + If shared is True, use a LOCK_SH lock, otherwise LOCK_EX.""" + + fcntl.flock(lockfile, fcntl.LOCK_SH if shared else fcntl.LOCK_EX) + try: + yield + finally: + fcntl.flock(lockfile, fcntl.LOCK_UN) + +@contextmanager +def lockfile(filename, shared=False): + """Lock a file using flock(2) for the duration of a 'with' statement. + + If shared is True, use a LOCK_SH lock, otherwise LOCK_EX. + + The file is given by name and will be created if it does not exist.""" + + if not path.exists(filename): + with open(filename, 'w') as lock: + lock.write('0') + + # TODO: Can we just open the file for writing, and skip the above check? + with open(filename, 'r') as lock: + with flock(lock, shared=shared): + yield diff --git a/zephyr/models.py b/zephyr/models.py index 6d33e93ac2..ea45ab90e6 100644 --- a/zephyr/models.py +++ b/zephyr/models.py @@ -6,13 +6,13 @@ import base64 import calendar from zephyr.lib.cache import cache_with_key from zephyr.lib.initial_password import initial_password, initial_api_key -import fcntl import os import simplejson from django.db import transaction, IntegrityError from zephyr.lib import bugdown from zephyr.lib.bulk_create import batch_bulk_create from zephyr.lib.avatar import gravatar_hash +from zephyr.lib.context_managers import lockfile import requests from django.contrib.auth.models import UserManager from django.utils import timezone @@ -473,15 +473,9 @@ def get_user_profile_by_id(uid): # Store an event in the log for re-importing messages def log_event(event): assert("timestamp" in event) - if not os.path.exists(settings.MESSAGE_LOG + '.lock'): - with open(settings.MESSAGE_LOG + '.lock', 'w') as lock: - lock.write('0') - - with open(settings.MESSAGE_LOG + '.lock', 'r') as lock: - fcntl.flock(lock, fcntl.LOCK_EX) + with lockfile(settings.MESSAGE_LOG + '.lock'): with open(settings.MESSAGE_LOG, 'a') as log: log.write(simplejson.dumps(event) + '\n') - fcntl.flock(lock, fcntl.LOCK_UN) def log_message(message): if not message.sending_client.name.startswith("test:"):