From: Yang Zhang Date: Wed, 30 Jul 2008 02:35:08 +0000 (-0400) Subject: * added file locking around cache X-Git-Tag: sipb-xen-base/8.10~1 X-Git-Url: http://xvm.mit.edu/gitweb/invirt/packages/invirt-base.git/commitdiff_plain/ff3dc91d38ee542dbb79c94ac910b43c2985b58e?hp=b8c4bdf48d61f3c1eb6b835d51c91fd4cf4f370f * added file locking around cache * cleanup svn path=/trunk/packages/sipb-xen-base/; revision=781 --- diff --git a/files/usr/share/python-support/sipb-xen-base/invirt/common.py b/files/usr/share/python-support/sipb-xen-base/invirt/common.py index 250078e..ca90714 100644 --- a/files/usr/share/python-support/sipb-xen-base/invirt/common.py +++ b/files/usr/share/python-support/sipb-xen-base/invirt/common.py @@ -1,10 +1,13 @@ import unittest +from fcntl import flock, LOCK_EX, LOCK_UN +from os import remove class struct(object): 'A simple namespace object.' - def __init__(self, d = {}): + def __init__(self, d = {}, **kwargs): 'd is the dictionary to update my __dict__ with.' self.__dict__.update(d) + self.__dict__.update(kwargs) def dicts2struct(x): """ @@ -18,10 +21,41 @@ def dicts2struct(x): else: return x -def wrap(rsrc, func): - "Utility to that emulates with Python 2.5's `with closing(rsrc)`." - try: return func(rsrc) - finally: rsrc.close() +# +# Hacks to work around lack of Python 2.5's `with` statement. +# + +def with_closing(rsrc): + "Utility to emulate Python 2.5's `with closing(rsrc)` context manager." + def wrapper(func): + try: return func(rsrc) + finally: rsrc.close() + return wrapper + +def with_lock_file(path): + """ + Context manager for lock files. Example: + + @with_lock_file('/tmp/mylock') + def input(): + print 'locked' + return raw_input() + # decorator is executed immediately + print input # prints the input text + """ + def wrapper(func): + @with_closing(file(path, 'w')) + def g(f): + flock(f, LOCK_EX) + try: return func() + finally: flock(f, LOCK_UN) + remove(path) + return g + return wrapper + +# +# Tests. +# class common_tests(unittest.TestCase): def test_dicts2structs(self): diff --git a/files/usr/share/python-support/sipb-xen-base/invirt/config.py b/files/usr/share/python-support/sipb-xen-base/invirt/config.py index fad48fe..02409a4 100644 --- a/files/usr/share/python-support/sipb-xen-base/invirt/config.py +++ b/files/usr/share/python-support/sipb-xen-base/invirt/config.py @@ -1,10 +1,9 @@ import json, yaml from invirt.common import * -from os import error, makedirs -from os.path import dirname, getmtime +from os.path import getmtime default_src_path = '/etc/invirt/master.yaml' -default_cache_path = '/var/lib/invirt/invirt.json' +default_cache_path = '/var/lib/invirt/cache.json' try: default_loader = yaml.CSafeLoader except: default_loader = yaml.SafeLoader @@ -28,14 +27,19 @@ def load(src_path = default_src_path, if not do_refresh: # try reading from the cache first - try: cfg = wrap(file(cache_path), lambda f: json.read(f.read())) + try: cfg = with_closing(file(cache_path))(lambda f: json.read(f.read())) except: do_refresh = True if do_refresh: - # reload the source and regenerate the cache - cfg = wrap(file(src_path), lambda f: yaml.load(f, default_loader)) - try: wrap(file(cache_path, 'w'), lambda f: f.write(json.write(cfg))) - except: pass # silent failure + # Atomically reload the source and regenerate the cache. The read and + # write must be a single transaction, or a stale version may be + # written. + @with_lock_file('/var/lib/invirt/cache.lock') + def cfg(): + cfg = with_closing(file(src_path))(lambda f: yaml.load(f, default_loader)) + try: with_closing(file(cache_path, 'w'))(lambda f: f.write(json.write(cfg))) + except: pass # silent failure + return cfg return cfg dicts = load()