lsst.utils  14.0-6-g4f52afe+1
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 from builtins import str
26 from builtins import object
27 
28 import threading
29 
30 
31 class SharedData(object):
32  """
33  a lock-protected container for data that can be shared amongst threads.
34 
35  This container holds data that is intended to be shared amongst multiple
36  threads. In order to update or (optionally) examine the data, one must
37  first aquire the lock associated with the container.
38 
39  This container also behaves like a threading.Container, via its
40  wait(), notify(), and notifyAll() functions. Also like Condition,
41  acquire() is reentrant.
42 
43  SharedData instances may be used with the with statement:
44 
45  sd = SharedData()
46  with sd:
47  sd.blah = 1
48 
49  The with statement will acquire the lock and ensure that it is released
50  when its block is exited.
51  """
52 
53  def __init__(self, needLockOnRead=True, data=None, cond=None):
54  """
55  create and initialize the shared data
56  @param needLockOnRead if true (default), acquiring the lock will
57  be needed when reading the data. This is recommended
58  if the data items are anything but primitive types;
59  otherwise, a compound data item (e.g. of type dict)
60  could be updated without acquiring a lock.
61  @param data a dictionary of data to initialize the container with.
62  This is done by calling initData(). Set this value to
63  False when calling from a subclass constructor; this
64  will allow any non-protected attributes to be set via
65  the subclass's constructor. If None is given (default),
66  it is assumed that all new attributes will be considered
67  protected data.
68  @param cond Reuse this existing Condition instance to protect this
69  container
70  """
71  self._d = {}
72  if cond is None:
73  cond = threading.Condition()
74  self._cond = cond
75 
76  # behave like a Condition
77  self.acquire = cond.acquire
78  self.release = cond.release
79  self.notify = cond.notify
80  self.notifyAll = cond.notifyAll
81  self.wait = cond.wait
82  self._is_owned = cond._is_owned
83 
84  self._lockOnRead = needLockOnRead
85 
86  if isinstance(data, dict):
87  self.initData(data)
88  if data is None:
89  self._d["__"] = True
90 
91  # behave like a Condition
92  def __enter__(self):
93  return self._cond.__enter__()
94 
95  def __exit__(self, *exc_info):
96  return self._cond.__exit__(*exc_info)
97 
98  def __getattribute__(self, name):
99  if name == "_d" or not self._d or name not in self._d:
100  return object.__getattribute__(self, name)
101 
102  if self._lockOnRead and not self._is_owned():
103  raise AttributeError("%s: lock required for read access" % name)
104  return self._d[name]
105 
106  def __setattr__(self, name, value):
107  if name == "_d" or not self._d or name in self.__dict__:
108  object.__setattr__(self, name, value)
109  return
110 
111  if not self._is_owned():
112  raise AttributeError("%s: lock required for write access" % name)
113 
114  self._d[name] = value
115 
116  def initData(self, data):
117  """
118  initialize the container with the data from a dictionary.
119  @param data a dictionary of data to initialize the container with.
120  Attributes will be added to this container with names
121  matching the given the dictionary's key names and
122  initialized to their corresponding values. The keys
123  cannot match an existing function (or internal attribute)
124  name.
125  @throws ValueError if the dictionary has a key that conflicts with
126  an existing function or internal attribute name.
127  """
128  with self._cond:
129  bad = set(data.keys()).intersection(set(self.__dict__.keys()))
130  if len(bad) > 0:
131  raise ValueError("Names cause conflicts with functions or " +
132  "internal data: " + str(bad))
133 
134  for key in data:
135  self._d[key] = data[key]
136 
137  if not self._d:
138  self._d["__"] = True
139 
140  def dir(self):
141  return [k for k in self._d if k != "__"]
def __init__(self, needLockOnRead=True, data=None, cond=None)
Definition: SharedData.py:53