Skip to content

Commit 9a135da

Browse files
author
Brent Burley
committed
Merge branch 'review-brentb-main' into 'main'
Ptex improvements See merge request look/ptex!12
2 parents 054047d + 8989ff9 commit 9a135da

25 files changed

Lines changed: 1036 additions & 1449 deletions

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ enable_testing()
3333
# Setup platform-specific threading flags.
3434
find_package(Threads REQUIRED)
3535

36-
find_package(ZLIB REQUIRED)
36+
find_package(libdeflate REQUIRED)
3737

3838
if (NOT DEFINED PTEX_SHA)
3939
# Query git for current commit ID

src/ptex/CMakeLists.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ if(PTEX_BUILD_STATIC_LIBS)
2323
PRIVATE
2424
${CMAKE_CURRENT_SOURCE_DIR})
2525
target_link_libraries(Ptex_static
26-
PUBLIC Threads::Threads ZLIB::ZLIB)
26+
PUBLIC Threads::Threads
27+
PRIVATE libdeflate::libdeflate_static
28+
)
2729
install(TARGETS Ptex_static EXPORT Ptex DESTINATION ${CMAKE_INSTALL_LIBDIR})
2830
endif()
2931

@@ -39,7 +41,9 @@ if(PTEX_BUILD_SHARED_LIBS)
3941
${CMAKE_CURRENT_SOURCE_DIR})
4042
target_compile_definitions(Ptex_dynamic PRIVATE PTEX_EXPORTS)
4143
target_link_libraries(Ptex_dynamic
42-
PUBLIC Threads::Threads ZLIB::ZLIB)
44+
PUBLIC Threads::Threads
45+
PRIVATE libdeflate::libdeflate_shared
46+
)
4347
install(TARGETS Ptex_dynamic EXPORT Ptex DESTINATION ${CMAKE_INSTALL_LIBDIR})
4448
endif()
4549

src/ptex/PtexCache.cpp

Lines changed: 54 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ PTEX_NAMESPACE_BEGIN
8282

8383
void PtexCachedReader::release()
8484
{
85+
// Unref the texture, and log the texture as recently used if the ref count becomes 0. Note: If
86+
// the ref is negative after unref is called, it means the unref'd texture was not previously in
87+
// use which is an application error. In this case, the texture was presumably already logged as
88+
// recently used and we shouldn't log it again.
8589
if (0 == unref()) {
8690
_cache->logRecentlyUsed(this);
8791
}
@@ -123,7 +127,6 @@ PtexTexture* PtexReaderCache::get(const char* filename, Ptex::String& error)
123127
// lookup reader in map
124128
StringKey key(filename);
125129
PtexCachedReader* reader = _files.get(key);
126-
bool isNew = false;
127130

128131
if (reader) {
129132
if (!reader->ok()) return 0;
@@ -134,43 +137,36 @@ PtexTexture* PtexReaderCache::get(const char* filename, Ptex::String& error)
134137
reader->ref();
135138
} else {
136139
reader = new PtexCachedReader(_premultiply, _io, _err, this);
137-
isNew = true;
140+
size_t newMemUsed = 0;
141+
PtexCachedReader* newreader = reader;
142+
reader = _files.tryInsert(key, reader, newMemUsed);
143+
adjustMemUsed(newMemUsed);
144+
if (reader != newreader) {
145+
// another thread got here first
146+
reader->ref();
147+
delete newreader;
148+
}
138149
}
139150

140-
bool needOpen = reader->needToOpen();
141-
if (needOpen) {
151+
if (reader->needToOpen()) {
142152
std::string buffer;
143153
const char* pathToOpen = filename;
144154
// search for the file (unless we have an I/O handler)
145155
if (_io || findFile(pathToOpen, buffer, error)) {
146-
reader->open(pathToOpen, error);
156+
if (reader->open(pathToOpen, error)) {
157+
reader->logOpen();
158+
}
147159
} else {
148160
// flag reader as invalid so we don't try to open it again on next lookup
149161
reader->invalidate();
150162
}
151163
}
152164

153-
if (isNew) {
154-
size_t newMemUsed = 0;
155-
PtexCachedReader* newreader = reader;
156-
reader = _files.tryInsert(key, reader, newMemUsed);
157-
adjustMemUsed(newMemUsed);
158-
if (reader != newreader) {
159-
// another thread got here first
160-
reader->ref();
161-
delete newreader;
162-
}
163-
}
164-
165165
if (!reader->ok()) {
166166
reader->unref();
167167
return 0;
168168
}
169169

170-
if (needOpen) {
171-
reader->logOpen();
172-
}
173-
174170
return reader;
175171
}
176172

@@ -190,13 +186,14 @@ void PtexReaderCache::logRecentlyUsed(PtexCachedReader* reader)
190186
while (1) {
191187
MruList* mruList = _mruList;
192188
int slot = AtomicIncrement(&mruList->next)-1;
193-
if (slot < numMruFiles) {
189+
if (slot < maxMruFiles) {
194190
mruList->files[slot] = reader;
191+
processMru();
195192
return;
196193
}
197194
// no mru slot available, process mru list and try again
198195
do processMru();
199-
while (_mruList->next >= numMruFiles);
196+
while (_mruList->next >= maxMruFiles);
200197
}
201198
}
202199

@@ -205,7 +202,9 @@ void PtexReaderCache::processMru()
205202
// use a non-blocking lock so we can proceed as soon as space has been freed in the mru list
206203
// (which happens almost immediately in the processMru thread that has the lock)
207204
if (!_mruLock.trylock()) return;
208-
if (_mruList->next < numMruFiles) {
205+
206+
if (!_mruList->next) {
207+
// nothing to do
209208
_mruLock.unlock();
210209
return;
211210
}
@@ -217,7 +216,8 @@ void PtexReaderCache::processMru()
217216

218217
// extract relevant stats and add to open/active list
219218
size_t memUsedChange = 0, filesOpenChange = 0;
220-
for (int i = 0; i < numMruFiles; ++i) {
219+
int numFilesToProcess = std::min(int(mruList->next), maxMruFiles);
220+
for (int i = 0; i < numFilesToProcess; ++i) {
221221
PtexCachedReader* reader;
222222
do { reader = mruList->files[i]; } while (!reader); // loop on (unlikely) race condition
223223
mruList->files[i] = 0;
@@ -237,50 +237,55 @@ void PtexReaderCache::processMru()
237237
AtomicStore(&mruList->next, 0);
238238
adjustMemUsed(memUsedChange);
239239
adjustFilesOpen(filesOpenChange);
240+
_mruLock.unlock();
240241

241-
bool shouldPruneFiles = _filesOpen > _maxFiles;
242-
bool shouldPruneData = _maxMem && _memUsed > _maxMem;
243-
244-
if (shouldPruneFiles) {
245-
pruneFiles();
246-
}
247-
if (shouldPruneData) {
248-
pruneData();
242+
pruneFilesIfNeeded();
243+
if (_maxMem) {
244+
pruneDataIfNeeded();
249245
}
250-
_mruLock.unlock();
251246
}
252247

253248

254-
void PtexReaderCache::pruneFiles()
249+
void PtexReaderCache::pruneFilesIfNeeded()
255250
{
256-
size_t numToClose = _filesOpen - _maxFiles;
257-
if (numToClose > 0) {
258-
while (numToClose) {
259-
PtexCachedReader* reader = _openFiles.pop();
260-
if (!reader) { _filesOpen = 0; break; }
261-
if (reader->tryClose()) {
262-
--numToClose;
263-
--_filesOpen;
264-
}
251+
while (_filesOpen > _maxFiles) {
252+
// close one file not currently in use, and then check again
253+
_mruLock.lock();
254+
PtexCachedReader* reader = _openFiles.pop();
255+
_mruLock.unlock();
256+
257+
if (!reader) {
258+
// no inactive open file available to close
259+
break;
260+
}
261+
if (reader->tryClose()) {
262+
--_filesOpen;
265263
}
266264
}
267265
}
268266

269267

270-
void PtexReaderCache::pruneData()
268+
void PtexReaderCache::pruneDataIfNeeded()
271269
{
272270
size_t memUsedChangeTotal = 0;
273-
size_t memUsed = _memUsed;
274-
while (memUsed + memUsedChangeTotal > _maxMem) {
271+
while (_memUsed > _maxMem) {
272+
// prune one texture not currently in use, and then check again
273+
_mruLock.lock();
275274
PtexCachedReader* reader = _activeFiles.pop();
276-
if (!reader) break;
275+
_mruLock.unlock();
276+
if (!reader) {
277+
// no inactive texture available to prune
278+
break;
279+
}
277280
size_t memUsedChange;
278281
if (reader->tryPrune(memUsedChange)) {
279282
// Note: after clearing, memUsedChange is negative
280283
memUsedChangeTotal += memUsedChange;
281284
}
282285
}
283-
adjustMemUsed(memUsedChangeTotal);
286+
if (memUsedChangeTotal) {
287+
adjustMemUsed(memUsedChangeTotal);
288+
}
284289
}
285290

286291

src/ptex/PtexCache.h

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,28 @@ class PtexCachedReader : public PtexReader
136136
~PtexCachedReader() {}
137137

138138
void ref() {
139+
// Note: a negative ref count indicates the texture is locked, e.g. for pruning or purging.
140+
// This should happen rarely, and only for a brief instant, but when it does we must wait
141+
// for the ref count to become non-negative again. We do so by making CompareAndSwap fail
142+
// when the old value is negative.
139143
while (1) {
140-
int32_t oldCount = _refCount;
141-
if (oldCount >= 0 && AtomicCompareAndSwap(&_refCount, oldCount, oldCount+1))
144+
int32_t oldCount = std::max(0, int32_t(_refCount));
145+
int32_t newCount = oldCount+1;
146+
if (AtomicCompareAndSwap(&_refCount, oldCount, newCount))
142147
return;
143148
}
144149
}
145150

146151
int32_t unref() {
147-
return AtomicDecrement(&_refCount);
152+
int32_t newCount = AtomicDecrement(&_refCount);
153+
if (newCount < 0) {
154+
// A negative ref count here indicates an application error. The negative ref count also
155+
// means the texture is now inadvertently locked. Set an error state, unlock the
156+
// texture.
157+
setError("PtexTexture Error: unref() called with refCount <= 0");
158+
unlock();
159+
}
160+
return newCount;
148161
}
149162

150163
virtual void release();
@@ -273,8 +286,8 @@ class PtexReaderCache : public PtexCache
273286

274287
bool findFile(const char*& filename, std::string& buffer, Ptex::String& error);
275288
void processMru();
276-
void pruneFiles();
277-
void pruneData();
289+
void pruneFilesIfNeeded();
290+
void pruneDataIfNeeded();
278291
size_t _maxFiles;
279292
size_t _maxMem;
280293
PtexInputHandler* _io;
@@ -288,10 +301,10 @@ class PtexReaderCache : public PtexCache
288301
volatile size_t _filesOpen; CACHE_LINE_PAD(_filesOpen,size_t);
289302
Mutex _mruLock; CACHE_LINE_PAD(_mruLock,Mutex);
290303

291-
static const int numMruFiles = 50;
304+
static const int maxMruFiles = 50;
292305
struct MruList {
293306
volatile int next;
294-
PtexCachedReader* volatile files[numMruFiles];
307+
PtexCachedReader* volatile files[maxMruFiles];
295308
};
296309
MruList _mruLists[2];
297310
MruList* volatile _mruList;

0 commit comments

Comments
 (0)