Commit 96f819bb authored by Jim Fulton's avatar Jim Fulton

Added a lock for calling cache methods to avoid a race condition

between calls from the storage and from the out-of-band
invalidation message handler.
parent ec9f5c8f
...@@ -144,16 +144,23 @@ file 0 and file 1. ...@@ -144,16 +144,23 @@ file 0 and file 1.
""" """
__version__ = "$Revision: 1.11 $"[11:-2] __version__ = "$Revision: 1.12 $"[11:-2]
import os, tempfile import os, tempfile
from struct import pack, unpack from struct import pack, unpack
from thread import allocate_lock
magic='ZEC0' magic='ZEC0'
class ClientCache: class ClientCache:
def __init__(self, storage='', size=20000000, client=None, var=None): def __init__(self, storage='', size=20000000, client=None, var=None):
# Allocate locks:
l=allocate_lock()
self._acquire=l.acquire
self._release=l.release
if client: if client:
# Create a persistent cache # Create a persistent cache
if var is None: var=os.path.join(INSTANCE_HOME,'var') if var is None: var=os.path.join(INSTANCE_HOME,'var')
...@@ -199,75 +206,117 @@ class ClientCache: ...@@ -199,75 +206,117 @@ class ClientCache:
self._current=current self._current=current
def open(self): def open(self):
self._index=index={} self._acquire()
self._get=index.get try:
serial={} self._index=index={}
f=self._f self._get=index.get
current=self._current serial={}
if f[not current] is not None: f=self._f
read_index(index, serial, f[not current], not current) current=self._current
self._pos=read_index(index, serial, f[current], current) if f[not current] is not None:
read_index(index, serial, f[not current], not current)
return serial.items() self._pos=read_index(index, serial, f[current], current)
return serial.items()
finally: self._release()
def invalidate(self, oid, version): def invalidate(self, oid, version):
p=self._get(oid, None) self._acquire()
if p is None: return None try:
f=self._f[p < 0] p=self._get(oid, None)
ap=abs(p) if p is None: return None
f.seek(ap) f=self._f[p < 0]
h=f.read(8) ap=abs(p)
if h != oid: return f.seek(ap)
f.seek(8,1) # Dang, we shouldn't have to do this. Bad Solaris & NT h=f.read(8)
if version: if h != oid: return
f.write('n') f.seek(8,1) # Dang, we shouldn't have to do this. Bad Solaris & NT
else: if version:
del self._index[oid] f.write('n')
f.write('i') else:
del self._index[oid]
f.write('i')
finally: self._release()
def load(self, oid, version): def load(self, oid, version):
p=self._get(oid, None) self._acquire()
if p is None: return None try:
f=self._f[p < 0] p=self._get(oid, None)
ap=abs(p) if p is None: return None
seek=f.seek f=self._f[p < 0]
read=f.read ap=abs(p)
seek(ap) seek=f.seek
h=read(27) read=f.read
if len(h)==27 and h[8] in 'nv' and h[:8]==oid: seek(ap)
tlen, vlen, dlen = unpack(">iHi", h[9:19]) h=read(27)
else: tlen=-1 if len(h)==27 and h[8] in 'nv' and h[:8]==oid:
if tlen <= 0 or vlen < 0 or dlen < 0 or vlen+dlen > tlen: tlen, vlen, dlen = unpack(">iHi", h[9:19])
del self._index[oid] else: tlen=-1
return None if tlen <= 0 or vlen < 0 or dlen < 0 or vlen+dlen > tlen:
if h[8]=='n':
if version: return None
if not dlen:
del self._index[oid] del self._index[oid]
return None return None
if not vlen or not version: if h[8]=='n':
if dlen: return read(dlen), h[19:] if version: return None
else: return None if not dlen:
del self._index[oid]
if dlen: seek(dlen, 1) return None
v=read(vlen)
if version != v: if not vlen or not version:
if dlen: if dlen: return read(dlen), h[19:]
seek(-dlen-vlen, 1) else: return None
return read(dlen), h[19:]
else: None if dlen: seek(dlen, 1)
v=read(vlen)
dlen=unpack(">i", read(4))[0] if version != v:
return read(dlen), read(8) if dlen:
seek(-dlen-vlen, 1)
return read(dlen), h[19:]
else: None
dlen=unpack(">i", read(4))[0]
return read(dlen), read(8)
finally: self._release()
def update(self, oid, serial, version, data): def update(self, oid, serial, version, data):
if version: self._acquire()
# We need to find and include non-version data try:
if version:
# We need to find and include non-version data
p=self._get(oid, None)
if p is None:
return self._store(oid, '', '', version, data, serial)
f=self._f[p < 0]
ap=abs(p)
seek=f.seek
read=f.read
seek(ap)
h=read(27)
if len(h)==27 and h[8] in 'nv' and h[:8]==oid:
tlen, vlen, dlen = unpack(">iHi", h[9:19])
else:
return self._store(oid, '', '', version, data, serial)
if tlen <= 0 or vlen < 0 or dlen <= 0 or vlen+dlen > tlen:
return self._store(oid, '', '', version, data, serial)
if dlen:
p=read(dlen)
s=h[19:]
else:
return self._store(oid, '', '', version, data, serial)
self._store(oid, p, s, version, data, serial)
else:
# Simple case, just store new data:
self._store(oid, data, serial, '', None, None)
finally: self._release()
def modifiedInVersion(self, oid):
self._acquire()
try:
p=self._get(oid, None) p=self._get(oid, None)
if p is None: if p is None: return None
return self.store(oid, '', '', version, data, serial)
f=self._f[p < 0] f=self._f[p < 0]
ap=abs(p) ap=abs(p)
seek=f.seek seek=f.seek
...@@ -276,69 +325,50 @@ class ClientCache: ...@@ -276,69 +325,50 @@ class ClientCache:
h=read(27) h=read(27)
if len(h)==27 and h[8] in 'nv' and h[:8]==oid: if len(h)==27 and h[8] in 'nv' and h[:8]==oid:
tlen, vlen, dlen = unpack(">iHi", h[9:19]) tlen, vlen, dlen = unpack(">iHi", h[9:19])
else: else: tlen=-1
return self.store(oid, '', '', version, data, serial) if tlen <= 0 or vlen < 0 or dlen < 0 or vlen+dlen > tlen:
del self._index[oid]
if tlen <= 0 or vlen < 0 or dlen <= 0 or vlen+dlen > tlen: return None
return self.store(oid, '', '', version, data, serial)
if dlen:
p=read(dlen)
s=h[19:]
else:
return self.store(oid, '', '', version, data, serial)
self.store(oid, p, s, version, data, serial)
else:
# Simple case, just store new data:
self.store(oid, data, serial, '', None, None)
def modifiedInVersion(self, oid): if h[8]=='n': return None
p=self._get(oid, None)
if p is None: return None
f=self._f[p < 0]
ap=abs(p)
seek=f.seek
read=f.read
seek(ap)
h=read(27)
if len(h)==27 and h[8] in 'nv' and h[:8]==oid:
tlen, vlen, dlen = unpack(">iHi", h[9:19])
else: tlen=-1
if tlen <= 0 or vlen < 0 or dlen < 0 or vlen+dlen > tlen:
del self._index[oid]
return None
if h[8]=='n': return None if not vlen: return ''
seek(dlen, 1)
if not vlen: return '' return read(vlen)
seek(dlen, 1) finally: self._release()
return read(vlen)
def checkSize(self, size): def checkSize(self, size):
# Make sure we aren't going to exceed the target size. self._acquire()
# If we are, then flip the cache. try:
if self._pos+size > self._limit: # Make sure we aren't going to exceed the target size.
current=not self._current # If we are, then flip the cache.
self._current=current if self._pos+size > self._limit:
if self._p[current] is not None: current=not self._current
# Persistent cache file: self._current=current
# Note that due to permission madness, waaa, if self._p[current] is not None:
# we need to remove the old file before # Persistent cache file:
# we open the new one. Waaaaaaaaaa. # Note that due to permission madness, waaa,
if self._f[current] is not None: # we need to remove the old file before
self._f[current].close() # we open the new one. Waaaaaaaaaa.
try: os.remove(self._p[current]) if self._f[current] is not None:
except: pass self._f[current].close()
self._f[current]=open(self._p[current],'w+b') try: os.remove(self._p[current])
else: except: pass
# Temporary cache file: self._f[current]=open(self._p[current],'w+b')
self._f[current] = tempfile.TemporaryFile(suffix='.zec') else:
self._f[current].write(magic) # Temporary cache file:
self._pos=pos=4 self._f[current] = tempfile.TemporaryFile(suffix='.zec')
self._f[current].write(magic)
self._pos=pos=4
finally: self._release()
def store(self, oid, p, s, version, pv, sv): def store(self, oid, p, s, version, pv, sv):
self._acquire()
try: self._store(oid, p, s, version, pv, sv)
finally: self._release()
def _store(self, oid, p, s, version, pv, sv):
if not s: if not s:
p='' p=''
s='\0\0\0\0\0\0\0\0' s='\0\0\0\0\0\0\0\0'
...@@ -348,7 +378,7 @@ class ClientCache: ...@@ -348,7 +378,7 @@ class ClientCache:
vlen=len(version) vlen=len(version)
else: else:
vlen=0 vlen=0
pos=self._pos pos=self._pos
current=self._current current=self._current
f=self._f[current] f=self._f[current]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment