LSSTApplications  10.0+286,10.0+36,10.0+46,10.0-2-g4f67435,10.1+152,10.1+37,11.0,11.0+1,11.0-1-g47edd16,11.0-1-g60db491,11.0-1-g7418c06,11.0-2-g04d2804,11.0-2-g68503cd,11.0-2-g818369d,11.0-2-gb8b8ce7
LSSTDataManagementBasePackage
SharedData.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008, 2009, 2010 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 #
24 from __future__ import with_statement
25 import threading
26 
27 class SharedData(object):
28  """
29  a lock-protected container for data that can be shared amongst threads.
30 
31  This container holds data that is intended to be shared amongst multiple
32  threads. In order to update or (optionally) examine the data, one must
33  first aquire the lock associated with the container.
34 
35  This container also behaves like a threading.Container, via its
36  wait(), notify(), and notifyAll() functions. Also like Condition,
37  acquire() is reentrant.
38 
39  SharedData instances may be used with the with statement:
40 
41  sd = SharedData()
42  with sd:
43  sd.blah = 1
44 
45  The with statement will acquire the lock and ensure that it is released
46  when its block is exited.
47  """
48 
49  def __init__(self, needLockOnRead=True, data=None, cond=None):
50  """
51  create and initialize the shared data
52  @param needLockOnRead if true (default), acquiring the lock will
53  be needed when reading the data. This is recommended
54  if the data items are anything but primitive types;
55  otherwise, a compound data item (e.g. of type dict)
56  could be updated without acquiring a lock.
57  @param data a dictionary of data to initialize the container with.
58  This is done by calling initData(). Set this value to
59  False when calling from a subclass constructor; this
60  will allow any non-protected attributes to be set via
61  the subclass's constructor. If None is given (default),
62  it is assumed that all new attributes will be considered
63  protected data.
64  @param cond Reuse this existing Condition instance to protect this
65  container
66  """
67  self._d = {}
68  if cond is None:
69  cond = threading.Condition()
70  self._cond = cond
71 
72  # behave like a Condition
73  self.acquire = cond.acquire
74  self.release = cond.release
75  self.notify = cond.notify
76  self.notifyAll = cond.notifyAll
77  self.wait = cond.wait
78  self._is_owned = cond._is_owned
79 
80  self._lockOnRead = needLockOnRead
81 
82  if isinstance(data, dict):
83  self.initData(data)
84  if data is None:
85  self._d["__"] = True
86 
87  # behave like a Condition
88  def __enter__(self): return self._cond.__enter__();
89  def __exit__(self, *exc_info): return self._cond.__exit__(*exc_info);
90 
91  def __getattribute__(self, name):
92  if name == "_d" or len(self._d) == 0 or not self._d.has_key(name):
93  return object.__getattribute__(self, name)
94 
95  if self._lockOnRead and not self._is_owned():
96  raise AttributeError("%s: lock required for read access" % name)
97  return self._d[name]
98 
99 
100  def __setattr__(self, name, value):
101  if name == "_d" or len(self._d) == 0 or name in self.__dict__.keys():
102  object.__setattr__(self, name, value)
103  return
104 
105  if not self._is_owned():
106  raise AttributeError("%s: lock required for write access" % name)
107 
108  self._d[name] = value
109 
110 
111  def initData(self, data):
112  """
113  initialize the container with the data from a dictionary.
114  @param data a dictionary of data to initialize the container with.
115  Attributes will be added to this container with names
116  matching the given the dictionary's key names and
117  initialized to their corresponding values. The keys
118  cannot match an existing function (or internal attribute)
119  name.
120  @throws ValueError if the dictionary has a key that conflicts with
121  an existing function or internal attribute name.
122  """
123  with self._cond:
124  bad = []
125  realattrs = self.__dict__.keys()
126  for key in data.keys():
127  if key in realattrs:
128  bad.append(key)
129  if len(bad) > 0:
130  raise ValueError("Names cause conflicts with functions or " +
131  "internal data: " + str(bad))
132 
133  for key in data.keys():
134  self._d[key] = data[key]
135 
136  if len(self._d) == 0:
137  self._d["__"] = True
138 
139  def dir(self):
140  return filter(lambda k: k != "__", self._d.keys())
141