Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

# 

# LSST Data Management System 

# Copyright 2008, 2009, 2010 LSST Corporation. 

# 

# This product includes software developed by the 

# LSST Project (http://www.lsst.org/). 

# 

# This program is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 3 of the License, or 

# (at your option) any later version. 

# 

# This program is distributed in the hope that it will be useful, 

# but WITHOUT ANY WARRANTY; without even the implied warranty of 

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

# GNU General Public License for more details. 

# 

# You should have received a copy of the LSST License Statement and 

# the GNU General Public License along with this program. If not, 

# see <http://www.lsstcorp.org/LegalNotices/>. 

# 

 

# 

import threading 

 

 

class SharedData: 

"""A lock-protected container for data that can be shared amongst threads. 

 

Parameters 

---------- 

needLockOnRead : `bool` 

If true (default), acquiring the lock will be needed when reading the data. This 

is recommended if the data items are anything but primitive types; otherwise, 

a compound data item (e.g. of type dict) could be updated without acquiring a lock. 

data : `dict` 

A dictionary of data to initialize the container with. This is done by calling 

initData(). Set this value to False when calling from a subclass constructor; this 

will allow any non-protected attributes to be set via the subclass's constructor. If 

None is given (default), it is assumed that all new attributes will be considered protected data. 

cond : `bool` 

Reuse this existing Condition instance to protect this container 

 

Notes 

----- 

This container holds data that is intended to be shared amongst multiple 

threads. In order to update or (optionally) examine the data, one must 

first aquire the lock associated with the container. 

 

This container also behaves like a threading.Container, via its 

wait(), notify(), and notifyAll() functions. Also like Condition, 

acquire() is reentrant. 

 

SharedData instances may be used with the with statement: 

 

sd = SharedData() 

with sd: 

sd.blah = 1 

 

The with statement will acquire the lock and ensure that it is released 

when its block is exited. 

""" 

 

def __init__(self, needLockOnRead=True, data=None, cond=None): 

self._d = {} 

if cond is None: 

cond = threading.Condition() 

self._cond = cond 

 

# behave like a Condition 

 

# acquire method 

self.acquire = cond.acquire 

 

# release method 

self.release = cond.release 

 

# notify method 

self.notify = cond.notify 

 

# notifyall method 

self.notifyAll = cond.notifyAll 

 

# wait method 

self.wait = cond.wait 

self._is_owned = cond._is_owned 

 

self._lockOnRead = needLockOnRead 

 

if isinstance(data, dict): 

self.initData(data) 

if data is None: 

self._d["__"] = True 

 

# overrides __enter__ 

def __enter__(self, *args, **kwds): 

return self._cond.__enter__(*args, **kwds) 

 

# overrides __exit__ 

def __exit__(self, *args, **kwds): 

return self._cond.__exit__(*args, **kwds) 

 

# overrides __getattribute__ 

def __getattribute__(self, name): 

if name == "_d" or len(self._d) == 0 or name not in self._d: 

return object.__getattribute__(self, name) 

 

if self._lockOnRead and not self._is_owned(): 

raise AttributeError("%s: lock required for read access" % name) 

return self._d[name] 

 

# overrides __setattr__ 

def __setattr__(self, name, value): 

if name == "_d" or len(self._d) == 0 or name in self.__dict__: 

object.__setattr__(self, name, value) 

return 

 

if not self._is_owned(): 

raise AttributeError("%s: lock required for write access" % name) 

 

self._d[name] = value 

 

def initData(self, data): 

"""Initialize the container with the data from a dictionary. 

 

Parameters 

---------- 

data : `dict` 

A dictionary of data to initialize the container with. Attributes will be added to 

this container with names matching the given the dictionary's key names and 

initialized to their corresponding values. The keys cannot match an existing 

function (or internal attribute) name. 

 

Raises 

------ 

ValueError if the dictionary has a key that conflicts with an existing function or 

internal attribute name. 

""" 

with self._cond: 

bad = [] 

realattrs = self.__dict__ 

for key in data: 

if key in realattrs: 

bad.append(key) 

if len(bad) > 0: 

raise ValueError("Names cause conflicts with functions or " + 

"internal data: " + str(bad)) 

 

for key in data: 

self._d[key] = data[key] 

 

if len(self._d) == 0: 

self._d["__"] = True 

 

# overrides dir() method 

def dir(self): 

return [k for k in self._d if k != "__"]