forked from NatronGitHub/Natron
-
Notifications
You must be signed in to change notification settings - Fork 0
/
TLSHolderImpl.h
289 lines (241 loc) · 9.89 KB
/
TLSHolderImpl.h
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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
/* ***** BEGIN LICENSE BLOCK *****
* This file is part of Natron <https://natrongithub.github.io/>,
* (C) 2018-2021 The Natron developers
* (C) 2013-2018 INRIA and Alexandre Gauthier-Foichat
*
* Natron 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 2 of the License, or
* (at your option) any later version.
*
* Natron 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 GNU General Public License
* along with Natron. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
* ***** END LICENSE BLOCK ***** */
#ifndef Engine_TLSHolderImpl_h
#define Engine_TLSHolderImpl_h
// ***** BEGIN PYTHON BLOCK *****
// from <https://docs.python.org/3/c-api/intro.html#include-files>:
// "Since Python may define some pre-processor definitions which affect the standard headers on some systems, you must include Python.h before any standard headers are included."
#include <Python.h>
// ***** END PYTHON BLOCK *****
#include "Global/Macros.h"
#include "TLSHolder.h"
#include "Engine/AppManager.h"
#include "Engine/EffectInstance.h"
NATRON_NAMESPACE_ENTER
//Template specialization for EffectInstance::EffectTLSData:
//We do this for the following reasons:
//We may be here in 2 cases: either in a thread from the multi-thread suite or from a thread that just got spawned
//from the host-frame threading (executing tiledRenderingFunctor).
//A multi-thread suite thread is not allowed by OpenFX to call clipGetImage, which does not require us to apply TLS
//on OfxClipInstance and also RenderArgs in EffectInstance. But a multi-thread suite thread may call the abort() function
//which needs the ParallelRenderArgs set on the EffectInstance.
//Similarly a host-frame threading thread is spawned at a time where the spawner thread only has the ParallelRenderArgs
//set on the TLS, so just copy this instead of the whole TLS.
template <>
EffectInstance::EffectTLSDataPtr
TLSHolder<EffectInstance::EffectTLSData>::copyAndReturnNewTLS(const QThread* fromThread,
const QThread* toThread) const
{
QWriteLocker k(&perThreadDataMutex);
ThreadDataMap::iterator found = perThreadData.find(fromThread);
if ( found == perThreadData.end() ) {
///No TLS for fromThread
return EffectInstance::EffectTLSDataPtr();
}
ThreadData data;
//Copy constructor
data.value = boost::make_shared<EffectInstance::EffectTLSData>( *(found->second.value) );
perThreadData[toThread] = data;
return data.value;
}
template <>
void
TLSHolder<EffectInstance::EffectTLSData>::copyTLS(const QThread* fromThread,
const QThread* toThread) const
{
EffectInstance::EffectTLSDataPtr tlsDataPtr = copyAndReturnNewTLS(fromThread, toThread);
Q_UNUSED(tlsDataPtr);
}
template <typename T>
void
TLSHolder<T>::copyTLS(const QThread* fromThread,
const QThread* toThread) const
{
Q_UNUSED(fromThread);
Q_UNUSED(toThread);
}
template <typename T>
boost::shared_ptr<T>
TLSHolder<T>::copyAndReturnNewTLS(const QThread* fromThread,
const QThread* toThread) const
{
Q_UNUSED(fromThread);
Q_UNUSED(toThread);
return boost::shared_ptr<T>();
}
template <typename T>
bool
TLSHolder<T>::canCleanupPerThreadData(const QThread* curThread) const
{
QReadLocker k(&perThreadDataMutex);
if ( perThreadData.empty() ) {
return true; // cleanup would return true
}
typename ThreadDataMap::iterator found = perThreadData.find(curThread);
if ( found != perThreadData.end() ) {
return true; // cleanup would do something
}
return false;
}
template <typename T>
bool
TLSHolder<T>::cleanupPerThreadData(const QThread* curThread) const
{
QWriteLocker k(&perThreadDataMutex);
typename ThreadDataMap::iterator found = perThreadData.find(curThread);
if ( found != perThreadData.end() ) {
perThreadData.erase(found);
}
return perThreadData.empty();
}
template <typename T>
boost::shared_ptr<T>
TLSHolder<T>::getTLSData() const
{
QThread* curThread = QThread::currentThread();
//This thread might be registered by a spawner thread, copy the TLS and attempt to find the TLS for this holder.
boost::shared_ptr<T> ret = appPTR->getAppTLS()->copyTLSFromSpawnerThread<T>(this, curThread);
if (ret) {
return ret;
}
//Attempt to find an object in the map. It will be there if we already called getOrCreateTLSData() for this thread
{
QReadLocker k(&perThreadDataMutex);
const ThreadDataMap& perThreadDataCRef = perThreadData; // take a const ref, since it's a read lock
typename ThreadDataMap::const_iterator found = perThreadDataCRef.find(curThread);
if ( found != perThreadDataCRef.end() ) {
ret = found->second.value;
}
}
return ret;
}
template <typename T>
boost::shared_ptr<T>
TLSHolder<T>::getOrCreateTLSData() const
{
QThread* curThread = QThread::currentThread();
//This thread might be registered by a spawner thread, copy the TLS and attempt to find the TLS for this holder.
boost::shared_ptr<T> ret = appPTR->getAppTLS()->copyTLSFromSpawnerThread<T>(this, curThread);
if (ret) {
return ret;
}
//Attempt to find an object in the map. It will be there if we already called getOrCreateTLSData() for this thread
//Note that if present, this call is extremely fast as we do not block other threads
{
QReadLocker k(&perThreadDataMutex);
const ThreadDataMap& perThreadDataCRef = perThreadData; // take a const ref, since it's a read lock
typename ThreadDataMap::const_iterator found = perThreadDataCRef.find(curThread);
if ( found != perThreadDataCRef.end() ) {
assert(found->second.value);
return found->second.value;
}
}
//getOrCreateTLSData() has never been called on the thread, lookup the TLS
ThreadData data;
TLSHolderBaseConstPtr thisShared = shared_from_this();
appPTR->getAppTLS()->registerTLSHolder(thisShared);
data.value = boost::make_shared<T>();
{
QWriteLocker k(&perThreadDataMutex);
perThreadData.insert( std::make_pair(curThread, data) );
}
assert(data.value);
return data.value;
}
template <typename T>
boost::shared_ptr<T>
AppTLS::copyTLSFromSpawnerThread(const TLSHolderBase* holder,
const QThread* curThread)
{
//3 cases where this function returns NULL:
// 1) No spawner thread registered
// 2) T is not a ParallelRenderArgs (see comments above copyAndReturnNewTLS explicit template instantiation
// 3) The spawner thread did not have TLS but was marked in the spawn map...
// Either way: return a new object
// first pass with a read lock to exit early without taking the write lock
{
QReadLocker k(&_spawnsMutex);
const ThreadSpawnMap& spawnsCRef = _spawns; // take a const ref, since it's a read lock
ThreadSpawnMap::const_iterator foundSpawned = spawnsCRef.find(curThread);
if ( foundSpawned == spawnsCRef.end() ) {
//This is not a spawned thread and it did not have TLS already
return boost::shared_ptr<T>();
}
}
const QThread* foundThread = 0;
// Find first under a read lock to enable better concurrency
{
QReadLocker k(&_spawnsMutex);
ThreadSpawnMap::iterator foundSpawned = _spawns.find(curThread);
if ( foundSpawned == _spawns.end() ) {
//This is not a spawned thread and it did not have TLS already
return boost::shared_ptr<T>();
}
foundThread = foundSpawned->second;
}
if (foundThread) {
QWriteLocker k(&_spawnsMutex);
ThreadSpawnMap::iterator foundSpawned = _spawns.find(curThread);
if ( foundSpawned == _spawns.end() ) {
//This is not a spawned thread and it did not have TLS already
return boost::shared_ptr<T>();
}
foundThread = foundSpawned->second;
//Erase the thread from the spawn map
_spawns.erase(foundSpawned);
}
{
QWriteLocker k(&_objectMutex);
boost::shared_ptr<T> retval = copyTLSFromSpawnerThreadInternal<T>(holder, curThread, foundThread);
return retval;
}
} // AppTLS::copyTLSFromSpawnerThread
template <typename T>
boost::shared_ptr<T>
AppTLS::copyTLSFromSpawnerThreadInternal(const TLSHolderBase* holder,
const QThread* curThread,
const QThread* spawnerThread)
{
//Private - should be locked
assert( !_objectMutex.tryLockForWrite() );
boost::shared_ptr<T> tls;
//This is a spawned thread and the first time we need the TLS for this thread, copy the whole TLS on all objects
for (TLSObjects::iterator it = _object->objects.begin();
it != _object->objects.end(); ++it) {
TLSHolderBaseConstPtr p = (*it).lock();
if (p) {
const TLSHolder<T>* foundHolder = 0;
if (p.get() == holder) {
//This is the object from which it has been required to create the TLS data, copy
//the TLS data from the spawner thread and mark it to 'tls'.
foundHolder = dynamic_cast<const TLSHolder<T>*>( p.get() );
if (foundHolder) {
tls = foundHolder->copyAndReturnNewTLS(spawnerThread, curThread);
}
}
if (!foundHolder) {
//Copy anyway
p->copyTLS(spawnerThread, curThread);
}
}
}
return tls;
}
NATRON_NAMESPACE_EXIT
#endif // Engine_TLSHolderImpl_h