Skip to content

Commit

Permalink
tf: allow TF_MAKE_STATIC_DATA to provide const access
Browse files Browse the repository at this point in the history
TF_MAKE_STATIC_DATA can now be used to define a TfStaticData instance
that only allows const access to the underlying data.  The data itself
is non-const so that initialization code can be used in a
straightforward manner.  This is particularly useful for copy-on-write
types where seemingly innocuous access (e.g. range-for iteration) can
trigger detachment.  Consider the following example,

    TF_MAKE_STATIC_DATA(VtIntArray, _array)
    {
       *_array= { /* ... */ };
    }

    void
    Func()
    {
        WorkParallelForN(_array->size(), [](size_t f, size_t l) {
            for (; f != l; ++f) {
                int x = (*_array)[f]; // invokes non-const operator[]
                // ...
            }
        }
    }

This code attempts to read _array in parallel but, because VtIntArray
is copy-on-write and TfStaticData::operator* returns a non-const
reference, multiple threads may race to detach if the array has been
shared previously.  TF_MAKE_STATIC_DATA(const VtIntArray, _array)
solves this issue.

(Internal change: 2237275)
  • Loading branch information
jloy authored and pixar-oss committed Jun 13, 2022
1 parent a338bb4 commit e6b3d02
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 3 deletions.
19 changes: 16 additions & 3 deletions pxr/base/tf/staticData.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "pxr/base/tf/preprocessorUtilsLite.h"

#include <atomic>
#include <type_traits>

PXR_NAMESPACE_OPEN_SCOPE

Expand Down Expand Up @@ -174,6 +175,18 @@ class TfStaticData {
/// If the type uses commas to separate template arguments you need to enclose
/// the type in parentheses as shown in the last example.
///
/// If the data does not need to be mutated after initialization, you may
/// specify a const type. The underlying data is non-const but the
/// TfStaticData accessors only provide const access.
///
/// \code
/// TF_MAKE_STATIC_DATA(const vector<string>, constNames) {
/// constNames->push_back("hello");
/// constNames->push_back("static const");
/// constNames->push_back("world");
/// }
/// \endcode
///
/// Note that this macro may only be used at namespace scope (not function
/// scope).
///
Expand All @@ -185,11 +198,11 @@ class TfStaticData {
/// \hideinitializer
#define TF_MAKE_STATIC_DATA(Type, Name) \
static void TF_PP_CAT(Name,_Tf_StaticDataFactoryImpl)( \
TF_PP_EAT_PARENS(Type) *); \
std::remove_const_t<TF_PP_EAT_PARENS(Type)> *); \
namespace { \
struct TF_PP_CAT(Name,_Tf_StaticDataFactory) { \
static TF_PP_EAT_PARENS(Type) *New() { \
auto *p = new TF_PP_EAT_PARENS(Type); \
auto *p = new std::remove_const_t<TF_PP_EAT_PARENS(Type)>; \
TF_PP_CAT(Name,_Tf_StaticDataFactoryImpl)(p); \
return p; \
} \
Expand All @@ -198,7 +211,7 @@ class TfStaticData {
static TfStaticData< \
TF_PP_EAT_PARENS(Type), TF_PP_CAT(Name,_Tf_StaticDataFactory)> Name; \
static void TF_PP_CAT(Name,_Tf_StaticDataFactoryImpl)( \
TF_PP_EAT_PARENS(Type) *Name)
std::remove_const_t<TF_PP_EAT_PARENS(Type)> *Name)

PXR_NAMESPACE_CLOSE_SCOPE

Expand Down
12 changes: 12 additions & 0 deletions pxr/base/tf/testenv/staticData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ TF_MAKE_STATIC_DATA((map<int, int>), _initMap) {
}
#endif

TF_MAKE_STATIC_DATA(const std::vector<int>, _constVector)
{
*_constVector = { 1, 2, 3 };
}

class Count {
public:
Count() { ++count; }
Expand Down Expand Up @@ -116,6 +121,13 @@ Test_TfStaticData()
TF_AXIOM((*_initMap)[2] == 22);
#endif

// test accessing const static data
TF_AXIOM(!_constVector.IsInitialized());
TF_AXIOM(_constVector->size() == 3);
TF_AXIOM((*_constVector)[0] == 1);
TF_AXIOM((*_constVector)[1] == 2);
TF_AXIOM((*_constVector)[2] == 3);

return true;
}

Expand Down

0 comments on commit e6b3d02

Please sign in to comment.