forked from PixarAnimationStudios/OpenUSD
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathidentity.cpp
214 lines (183 loc) · 6.69 KB
/
identity.cpp
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
//
// Copyright 2016 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "pxr/pxr.h"
#include "pxr/usd/sdf/identity.h"
PXR_NAMESPACE_OPEN_SCOPE
//
// Sdf_Identity
//
Sdf_Identity::Sdf_Identity(Sdf_IdentityRegistry *registry,
const SdfPath &path) :
_refCount(0), _registry(registry), _path(path)
{
}
Sdf_Identity::~Sdf_Identity()
{
if (_registry) {
_registry->_Remove(_path, this);
}
}
const SdfLayerHandle &
Sdf_Identity::GetLayer() const
{
if (ARCH_LIKELY(_registry)) {
return _registry->GetLayer();
}
static const SdfLayerHandle empty;
return empty;
}
void Sdf_Identity::_Forget()
{
_path = SdfPath();
_registry = NULL;
}
//
// Sdf_IdentityRegistry
//
Sdf_IdentityRegistry::Sdf_IdentityRegistry(const SdfLayerHandle &layer) :
_layer(layer)
{
}
Sdf_IdentityRegistry::~Sdf_IdentityRegistry()
{
tbb::spin_mutex::scoped_lock lock(_idsMutex);
TF_FOR_ALL(i, _ids) {
(*i).second->_Forget();
}
}
Sdf_IdentityRefPtr
Sdf_IdentityRegistry::Identify(const SdfPath &path)
{
auto TryAcquire = [](Sdf_Identity *rawId) {
// Acquire an additional reference to this identity. We need to do
// this before proceeding to protect ourselves from race conditions,
// since other threads could drop the ref-count of this identity at
// any time, potentially beginning its destruction.
if (rawId->_refCount++ > 0) {
// The node is still in active use and we can share it.
// Since we just acquired a reference here, we know the
// node cannot expire before we return it.
return true;
} else {
// The identity has expired but not yet been removed from
// the registry map, due to the registry destructor racing
// this function for the _idsMutex.
//
// We cannot re-use the identity because we cannot stop the
// destructor from completing (and its memory being freed)
// so we must allocate a new identity. Discard the reference
// we just acquired.
--rawId->_refCount;
return false;
}
};
Sdf_IdentityRefPtr oldLastId;
tbb::spin_mutex::scoped_lock lock(_idsMutex);
if (Sdf_Identity *lastIdPtr = _lastId.get()) {
if (lastIdPtr->GetPath() == path) {
if (TryAcquire(lastIdPtr)) {
return Sdf_IdentityRefPtr(lastIdPtr, /* add_ref = */ false);
}
}
}
// Copy _lastId so we don't attempt to drop it until we've released the
// lock.
oldLastId = _lastId;
_IdMap::iterator i = _ids.find(path);
if (i != _ids.end()) {
Sdf_Identity *rawId = i->second;
if (TryAcquire(rawId)) {
Sdf_IdentityRefPtr ret(rawId, /* add_ref = */ false);
_lastId = ret;
return ret;
}
}
TfAutoMallocTag2 tag("Sdf", "Sdf_IdentityRegistry::Identify");
Sdf_Identity *id = new Sdf_Identity(this, path);
// Note, this potentially overwrites an existing identity for this
// path. Per the code above, this only happens when the existing
// identity is in the process of being destroyed.
_ids[path] = id;
_lastId = Sdf_IdentityRefPtr(id);
return _lastId;
}
void
Sdf_IdentityRegistry::_Remove(const SdfPath &path, Sdf_Identity *id)
{
tbb::spin_mutex::scoped_lock lock(_idsMutex);
_IdMap::iterator i = _ids.find(path);
if (i == _ids.end()) {
// It is possible for this path entry to have already been
// removed. Consider the case where Identify() is called for
// a path whose prior identity is expiring, but has not yet
// been removed from the table (due to races bewteen threads).
// Identify() will allocate a new identity at the same path.
// If that new identity is then dropped, it can remove the
// path from the table, all before the original identity
// can be removed.
//
// An alternate design that might be cleaner would be to
// do something like Sdf_PathNodes, where we allow multiple
// expired entries to exist for a given path in addition
// to at most one live one.
return;
}
if (i->second == id) {
// Only erase this entry if it still maps to this identity.
// As described above, it is possible that Identify() has
// replaced this with a new identity.
_ids.erase(i);
}
}
void
Sdf_IdentityRegistry::MoveIdentity(const SdfPath &oldPath,
const SdfPath &newPath)
{
// We hold the mutex, but note that per our Sdf thread-safety rules,
// no other thread is allowed to be reading or writing this layer
// at the same time that the layer is being mutated.
tbb::spin_mutex::scoped_lock lock(_idsMutex);
// Make sure an identity actually exists at the old path, otherwise
// there's nothing to do.
if (_ids.count(oldPath) == 0) {
return;
}
// Insert an entry in the identity map for the new path. If an identity
// already exists there, make sure we stomp it first.
std::pair<_IdMap::iterator, bool> newIdStatus =
_ids.insert(std::make_pair(newPath, (Sdf_Identity*)NULL));
if (!newIdStatus.second) {
if (TF_VERIFY(newIdStatus.first->second)) {
newIdStatus.first->second->_Forget();
}
}
// Copy the identity from the entry at the old path to the new path and
// update it to point at the new path.
_IdMap::iterator oldIdIt = _ids.find(oldPath);
newIdStatus.first->second = oldIdIt->second;
newIdStatus.first->second->_path = newPath;
// Erase the old identity map entry.
_ids.erase(oldIdIt);
}
PXR_NAMESPACE_CLOSE_SCOPE