Skip to content

Commit a6239ea

Browse files
authored
fix: Error retrieval safeguards for recycled objects (#4239)
The recent switch from boost thread_specific_ptr left a bug: if an ImageInput (say) is deleted without the caller clearing its error state, and then later another ImageInput is allocated and happens to get the same address as the first one, it can "inherit" the unretrieved error state left behind by the other. Fix by using a unique ID (initialized by an incrementing atomic counter) to index the object in the per-thread error store. I feel like we still don't have a foolproof replacement for the really nice lifetime management that thread_specific_ptr was doing for us. One could argue that unretrieved errors in an object are a "bug" in the user's program, but unretrieved bugs currently just accumulate in the map, even after the ImageInput is destroyed, until the thread terminates. --------- Signed-off-by: Larry Gritz <[email protected]>
1 parent 1c81e49 commit a6239ea

File tree

5 files changed

+46
-31
lines changed

5 files changed

+46
-31
lines changed

src/libOpenImageIO/imageinput.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,20 @@ using namespace pvt;
2727

2828

2929
// store an error message per thread, for a specific ImageInput
30-
static thread_local tsl::robin_map<const ImageInput*, std::string>
31-
input_error_messages;
30+
static thread_local tsl::robin_map<uint64_t, std::string> input_error_messages;
31+
static std::atomic_int64_t input_next_id(0);
32+
3233

3334
class ImageInput::Impl {
3435
public:
36+
Impl()
37+
: m_id(++input_next_id)
38+
{
39+
}
40+
3541
// So we can lock this ImageInput for the thread-safe methods.
3642
std::recursive_mutex m_mutex;
43+
uint64_t m_id;
3744
int m_threads = 0;
3845

3946
// The IOProxy object we will use for all I/O operations.
@@ -86,7 +93,7 @@ ImageInput::~ImageInput()
8693
// Erase any leftover errors from this thread
8794
// TODO: can we clear other threads' errors?
8895
// TODO: potentially unsafe due to the static destruction order fiasco
89-
// input_error_messages.erase(this);
96+
// input_error_messages.erase(m_impl->m_id);
9097
}
9198

9299

@@ -1096,7 +1103,7 @@ ImageInput::append_error(string_view message) const
10961103
{
10971104
if (message.size() && message.back() == '\n')
10981105
message.remove_suffix(1);
1099-
std::string& err_str = input_error_messages[this];
1106+
std::string& err_str = input_error_messages[m_impl->m_id];
11001107
OIIO_DASSERT(
11011108
err_str.size() < 1024 * 1024 * 16
11021109
&& "Accumulated error messages > 16MB. Try checking return codes!");
@@ -1112,7 +1119,7 @@ ImageInput::append_error(string_view message) const
11121119
bool
11131120
ImageInput::has_error() const
11141121
{
1115-
auto iter = input_error_messages.find(this);
1122+
auto iter = input_error_messages.find(m_impl->m_id);
11161123
if (iter == input_error_messages.end())
11171124
return false;
11181125
return iter.value().size() > 0;
@@ -1124,7 +1131,7 @@ std::string
11241131
ImageInput::geterror(bool clear) const
11251132
{
11261133
std::string e;
1127-
auto iter = input_error_messages.find(this);
1134+
auto iter = input_error_messages.find(m_impl->m_id);
11281135
if (iter != input_error_messages.end()) {
11291136
e = iter.value();
11301137
if (clear)

src/libOpenImageIO/imageoutput.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,22 @@ using namespace pvt;
3030

3131

3232
// store an error message per thread, for a specific ImageInput
33-
static thread_local tsl::robin_map<const ImageOutput*, std::string>
34-
output_error_messages;
33+
static thread_local tsl::robin_map<uint64_t, std::string> output_error_messages;
34+
static std::atomic_int64_t output_next_id(0);
3535

3636

3737
class ImageOutput::Impl {
3838
public:
39+
Impl()
40+
: m_id(++output_next_id)
41+
{
42+
}
43+
3944
// Unneeded?
4045
// // So we can lock this ImageOutput for the thread-safe methods.
4146
// std::recursive_mutex m_mutex;
4247

48+
uint64_t m_id;
4349
int m_threads = 0;
4450

4551
// The IOProxy object we will use for all I/O operations.
@@ -269,7 +275,7 @@ ImageOutput::append_error(string_view message) const
269275
{
270276
if (message.size() && message.back() == '\n')
271277
message.remove_suffix(1);
272-
std::string& err_str = output_error_messages[this];
278+
std::string& err_str = output_error_messages[m_impl->m_id];
273279
OIIO_DASSERT(
274280
err_str.size() < 1024 * 1024 * 16
275281
&& "Accumulated error messages > 16MB. Try checking return codes!");
@@ -698,7 +704,7 @@ ImageOutput::copy_tile_to_image_buffer(int x, int y, int z, TypeDesc format,
698704
bool
699705
ImageOutput::has_error() const
700706
{
701-
auto iter = output_error_messages.find(this);
707+
auto iter = output_error_messages.find(m_impl->m_id);
702708
if (iter == output_error_messages.end())
703709
return false;
704710
return iter.value().size() > 0;
@@ -710,7 +716,7 @@ std::string
710716
ImageOutput::geterror(bool clear) const
711717
{
712718
std::string e;
713-
auto iter = output_error_messages.find(this);
719+
auto iter = output_error_messages.find(m_impl->m_id);
714720
if (iter != output_error_messages.end()) {
715721
e = iter.value();
716722
if (clear)

src/libtexture/imagecache.cpp

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ spin_mutex ImageCacheImpl::m_perthread_info_mutex;
4949

5050
namespace { // anonymous
5151

52+
static thread_local tsl::robin_map<uint64_t, std::string> imcache_error_messages;
5253
static std::shared_ptr<ImageCacheImpl> shared_image_cache;
5354
static spin_mutex shared_image_cache_mutex;
5455

@@ -65,11 +66,8 @@ static ustring s_averagecolor("averagecolor"), s_averagealpha("averagealpha");
6566
static ustring s_constantcolor("constantcolor");
6667
static ustring s_constantalpha("constantalpha");
6768

68-
static thread_local tsl::robin_map<const ImageCacheImpl*, std::string>
69-
imcache_error_messages;
70-
7169
// constantly increasing, so we can avoid issues if an ImageCache pointer is re-used after being freed
72-
static std::atomic_int64_t imagecache_id_atomic = 0;
70+
static std::atomic_int64_t imagecache_next_id = 0;
7371
static thread_local tsl::robin_map<uint64_t, ImageCachePerThreadInfo*>
7472
imagecache_per_thread_infos;
7573

@@ -1657,7 +1655,7 @@ ImageCacheTile::data(int x, int y, int z, int c) const
16571655

16581656
ImageCacheImpl::ImageCacheImpl()
16591657
{
1660-
imagecache_id = imagecache_id_atomic.fetch_add(1);
1658+
imagecache_id = imagecache_next_id.fetch_add(1);
16611659
init();
16621660
}
16631661

@@ -1725,7 +1723,7 @@ ImageCacheImpl::~ImageCacheImpl()
17251723
// Erase any leftover errors from this thread
17261724
// TODO: can we clear other threads' errors?
17271725
// TODO: potentially unsafe due to the static destruction order fiasco
1728-
// imcache_error_messages.erase(this);
1726+
// imcache_error_messages.erase(imagecache_id);
17291727
}
17301728

17311729

@@ -2735,7 +2733,7 @@ ImageCacheImpl::get_image_info(ImageCacheFile* file,
27352733
// for the file to be nonexistent or broken!
27362734
if ((*(int*)data = (file && !file->broken())) == 0) {
27372735
// eat any error generated by find_file
2738-
imcache_error_messages.erase(this);
2736+
imcache_error_messages.erase(imagecache_id);
27392737
(void)OIIO::geterror(true); // Suppress global errors
27402738
}
27412739
return true;
@@ -2763,7 +2761,7 @@ ImageCacheImpl::get_image_info(ImageCacheFile* file,
27632761
if (file->broken()) {
27642762
if (file->errors_should_issue()) {
27652763
// eat any earlier messages
2766-
imcache_error_messages.erase(this);
2764+
imcache_error_messages.erase(imagecache_id);
27672765
error("Invalid image file \"{}\": {}", file->filename(),
27682766
file->broken_error_message());
27692767
}
@@ -3878,7 +3876,7 @@ ImageCacheImpl::purge_perthread_microcaches()
38783876
bool
38793877
ImageCacheImpl::has_error() const
38803878
{
3881-
auto iter = imcache_error_messages.find(this);
3879+
auto iter = imcache_error_messages.find(imagecache_id);
38823880
if (iter == imcache_error_messages.end())
38833881
return false;
38843882
return iter.value().size() > 0;
@@ -3890,7 +3888,7 @@ std::string
38903888
ImageCacheImpl::geterror(bool clear) const
38913889
{
38923890
std::string e;
3893-
auto iter = imcache_error_messages.find(this);
3891+
auto iter = imcache_error_messages.find(imagecache_id);
38943892
if (iter != imcache_error_messages.end()) {
38953893
e = iter.value();
38963894
if (clear)
@@ -3906,7 +3904,7 @@ ImageCacheImpl::append_error(string_view message) const
39063904
{
39073905
if (message.size() && message.back() == '\n')
39083906
message.remove_suffix(1);
3909-
std::string& err_str = imcache_error_messages[this];
3907+
std::string& err_str = imcache_error_messages[imagecache_id];
39103908
OIIO_DASSERT(
39113909
err_str.size() < 1024 * 1024 * 16
39123910
&& "Accumulated error messages > 16MB. Try checking return codes!");

src/libtexture/texture_pvt.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -551,9 +551,10 @@ class TextureSystemImpl final : public TextureSystem {
551551
float dsdy, float dtdy, float sblur, float tblur);
552552

553553
ImageCacheImpl* m_imagecache = nullptr;
554-
bool m_imagecache_owner = false; ///< True if we own the ImageCache
555-
Imath::M44f m_Mw2c; ///< world-to-"common" matrix
556-
Imath::M44f m_Mc2w; ///< common-to-world matrix
554+
uint64_t m_id; // A unique ID for this TextureSystem
555+
Imath::M44f m_Mw2c; ///< world-to-"common" matrix
556+
Imath::M44f m_Mc2w; ///< common-to-world matrix
557+
bool m_imagecache_owner = false; ///< True if we own the ImageCache
557558
bool m_gray_to_rgb; ///< automatically copy gray to rgb channels?
558559
bool m_flip_t; ///< Flip direction of t coord?
559560
int m_max_tile_channels; ///< narrow tile ID channel range when

src/libtexture/texturesys.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ static spin_mutex shared_texturesys_mutex;
5353
static bool do_unit_test_texture = false;
5454
static float unit_test_texture_blur = 0.0f;
5555

56+
static thread_local tsl::robin_map<int64_t, std::string> txsys_error_messages;
57+
static std::atomic_int64_t txsys_next_id(0);
58+
5659
static vfloat4 u8scale(1.0f / 255.0f);
5760
static vfloat4 u16scale(1.0f / 65535.0f);
5861

@@ -323,6 +326,7 @@ texture_type_name(TexFormat f)
323326

324327

325328
TextureSystemImpl::TextureSystemImpl(ImageCache* imagecache)
329+
: m_id(++txsys_next_id)
326330
{
327331
m_imagecache = (ImageCacheImpl*)imagecache;
328332
init();
@@ -358,7 +362,7 @@ TextureSystemImpl::~TextureSystemImpl()
358362
// Erase any leftover errors from this thread
359363
// TODO: can we clear other threads' errors?
360364
// TODO: potentially unsafe due to the static destruction order fiasco
361-
// txsys_error_messages.erase(this);
365+
// txsys_error_messages.erase(m_id);
362366
}
363367

364368

@@ -900,13 +904,12 @@ TextureSystemImpl::get_texels(TextureHandle* texture_handle_,
900904
return ok;
901905
}
902906

903-
static thread_local tsl::robin_map<const TextureSystemImpl*, std::string>
904-
txsys_error_messages;
907+
905908

906909
bool
907910
TextureSystemImpl::has_error() const
908911
{
909-
auto iter = txsys_error_messages.find(this);
912+
auto iter = txsys_error_messages.find(m_id);
910913
if (iter == txsys_error_messages.end())
911914
return false;
912915
return iter.value().size() > 0;
@@ -918,7 +921,7 @@ std::string
918921
TextureSystemImpl::geterror(bool clear) const
919922
{
920923
std::string e;
921-
auto iter = txsys_error_messages.find(this);
924+
auto iter = txsys_error_messages.find(m_id);
922925
if (iter != txsys_error_messages.end()) {
923926
e = iter.value();
924927
if (clear)
@@ -934,7 +937,7 @@ TextureSystemImpl::append_error(string_view message) const
934937
{
935938
if (message.size() && message.back() == '\n')
936939
message.remove_suffix(1);
937-
std::string& err_str = txsys_error_messages[this];
940+
std::string& err_str = txsys_error_messages[m_id];
938941
OIIO_DASSERT(
939942
err_str.size() < 1024 * 1024 * 16
940943
&& "Accumulated error messages > 16MB. Try checking return codes!");

0 commit comments

Comments
 (0)