Web · Wiki · Activities · Blog · Lists · Chat · Meeting · Bugs · Git · Translate · Archive · People · Donate

Commit eea772d7e5fed34fd06b34d3604d6b404f57daa3

fsemulation: guard all data store accesses with lock

Despite having internal locking, python-dbus / libdbus apparently
isn't thread-safe. All current data store implementations are
single-threaded anyway, so just guard all data store accesses (which
happen via D-Bus) with a (single) lock to prevent multiple D-Bus calls
being invoked in parallel.
  
1414# along with this program. If not, see <http://www.gnu.org/licenses/>.
1515import collections
1616import errno
17import functools
1718import logging
1819import os
1920import stat
21import threading
2022import time
2123
2224import dbus
4040"""Metadata properties used for determining the file name of an entry"""
4141
4242
43def synchronised(func):
44 @functools.wraps(func)
45 def wrapper(self, *args, **kwargs):
46 with self._lock:
47 return func(self, *args, **kwargs)
48 return wrapper
49
50
4351class _LRU(collections.MutableMapping):
4452 """Simple, but reasonably fast Least Recently Used (LRU) cache"""
4553
9393class DataStore(object):
9494 def __init__(self):
9595 self.supports_versions = False
96 self._lock = threading.RLock()
9697 self._data_store_version = 0
9798 bus = dbus.SessionBus()
9899 try:
124124 logging.info('0.84+ data store without version support found')
125125 self._data_store_version = 84
126126
127 @synchronised
127128 def list_object_ids(self, query=None):
128129 """Retrieve the object_ids of all (matching) data store entries
129130
151151 for entry in self._data_store.find(query, ['uid'],
152152 byte_arrays=True, timeout=DBUS_TIMEOUT_MAX)[0]]
153153
154 @synchronised
154155 def list_metadata(self, query=None):
155156 """Retrieve object_id and selected metadata of matching entries
156157
186186 for entry in self._data_store.find(query, properties,
187187 timeout=DBUS_TIMEOUT_MAX, byte_arrays=True)[0]]
188188
189 @synchronised
189190 def list_versions(self, tree_id):
190191 """Retrieve all version_ids of the given data store entry"""
191192 options = {'all_versions': True, 'order_by': ['-timestamp']}
194194 for entry in self._data_store.find({'tree_id': tree_id},
195195 options, timeout=DBUS_TIMEOUT_MAX, byte_arrays=True)[0]]
196196
197 @synchronised
197198 def list_tree_ids(self, query=None):
198199 """Retrieve the tree_ids of all (matching) data store entries"""
199200 return [unicode(entry[0]) for entry in self.list_object_ids(query)]
200201
202 @synchronised
201203 def list_property_values(self, name, query=None):
202204 """Return all unique values of the given property"""
203205 assert isinstance(name, unicode)
220220
221221 return dict.fromkeys([entry.get(name) for entry in entries]).keys()
222222
223 @synchronised
223224 def check_object_id(self, object_id):
224225 """Return True if the given object_id identifies a data store entry"""
225226 try:
232232
233233 return True
234234
235 @synchronised
235236 def check_tree_id(self, tree_id):
236237 """Return True if the given tree_id identifies a data store entry"""
237238 assert isinstance(tree_id, unicode)
241241 byte_arrays=True)[0]
242242 return bool(results)
243243
244 @synchronised
244245 def check_property_contains(self, name, word):
245246 """Return True if there is at least one entry containing word in the
246247 given property
263263
264264 return bool(results)
265265
266 @synchronised
266267 def get_properties(self, object_id, names=None):
267268 """Read given properties for data store entry identified by object_id
268269
299299
300300 return self._convert_metadata(metadata)
301301
302 @synchronised
302303 def list_properties(self, object_id):
303304 """List the names of all properties for this entry
304305
307307 """
308308 return self.get_properties(object_id).keys()
309309
310 @synchronised
310311 def create_property(self, object_id, name, value):
311312 """Set the given property, raising an error if it already exists"""
312313 assert isinstance(name, unicode)
318318 metadata[name] = value
319319 self._change_metadata(object_id, metadata)
320320
321 @synchronised
321322 def replace_property(self, object_id, name, value):
322323 """Modify the given, already existing property"""
323324 assert isinstance(name, unicode)
331331 metadata[name] = value
332332 self._change_metadata(object_id, metadata)
333333
334 @synchronised
334335 def set_properties(self, object_id, properties):
335336 """Write the given (sub)set of properties
336337
346346 metadata.update(properties)
347347 self._change_metadata(object_id, metadata)
348348
349 @synchronised
349350 def remove_properties(self, object_id, names):
350351 """Remove the given (sub)set of properties
351352
363363
364364 self._change_metadata(object_id, metadata)
365365
366 @synchronised
366367 def remove_entry(self, object_id):
367368 """Remove a single (version of a) data store entry"""
368369 if self._data_store.dbus_interface == DS_DBUS_INTERFACE2:
378378 assert isinstance(object_id, unicode)
379379 self._data_store.delete(object_id, timeout=DBUS_TIMEOUT_MAX)
380380
381 @synchronised
381382 def create_new(self, properties):
382383 """Create a new data store entry
383384
396396 else:
397397 return self._data_store.create(properties, '', False)
398398
399 @synchronised
399400 def get_data(self, object_id):
400401 """Return path to data for data store entry identified by object_id."""
401402 if self._data_store.dbus_interface == DS_DBUS_INTERFACE2:
410410 assert isinstance(object_id, unicode)
411411 return self._data_store.get_filename(object_id, byte_arrays=True)
412412
413 @synchronised
413414 def get_size(self, object_id):
414415 # FIXME: make use of filesize property if available
415416 path = self.get_data(object_id)
421421 os.remove(path)
422422 return size
423423
424 @synchronised
424425 def write_data(self, object_id, path):
425426 """Update data for data store entry identified by object_id.
426427