/usr/local/lib64/python3.6/site-packages/torch/include/c10/util
NameSizeModeActions
accumulate.h42050644editdlrm
AlignOf.h48350644editdlrm
Array.h113540644editdlrm
ArrayRef.h90580644editdlrm
Backtrace.h3640644editdlrm
BFloat16-inl.h92220644editdlrm
BFloat16-math.h52170644editdlrm
BFloat16.h23720644editdlrm
Bitset.h34140644editdlrm
C++17.h133290644editdlrm
complex.h175420644editdlrm
complex_math.h109020644editdlrm
complex_utils.h9580644editdlrm
ConstexprCrc.h66330644editdlrm
copysign.h8660644editdlrm
DeadlockDetection.h19200644editdlrm
Deprecated.h35790644editdlrm
either.h64230644editdlrm
env.h8350644editdlrm
Exception.h247230644editdlrm
ExclusivelyOwned.h47590644editdlrm
Flags.h100560644editdlrm
flat_hash_map.h616550644editdlrm
FunctionRef.h23010644editdlrm
Half-inl.h86740644editdlrm
Half.h189650644editdlrm
hash.h50840644editdlrm
IdWrapper.h23480644editdlrm
intrusive_ptr.h355630644editdlrm
in_place.h3500644editdlrm
irange.h26790644editdlrm
LeftRight.h60160644editdlrm
llvmMathExtras.h291680644editdlrm
Logging.h112560644editdlrm
logging_is_google_glog.h20310644editdlrm
logging_is_not_google_glog.h82710644editdlrm
MathConstants.h8580644editdlrm
math_compat.h72960644editdlrm
MaybeOwned.h66890644editdlrm
Metaprogramming.h152880644editdlrm
numa.h6960644editdlrm
Optional.h355920644editdlrm
order_preserving_flat_hash_map.h654820644editdlrm
overloaded.h7090644editdlrm
python_stub.h560644editdlrm
qint8.h4720644editdlrm
qint32.h3190644editdlrm
quint4x2.h3660644editdlrm
quint8.h3200644editdlrm
Registry.h122420644editdlrm
reverse_iterator.h87960644editdlrm
ScopeExit.h13450644editdlrm
signal_handler.h31540644editdlrm
SmallBuffer.h12430644editdlrm
SmallVector.h344560644editdlrm
sparse_bitset.h265110644editdlrm
StringUtil.h45380644editdlrm
string_utils.h39890644editdlrm
string_view.h201890644editdlrm
tempfile.h60290644editdlrm
ThreadLocal.h38830644editdlrm
ThreadLocalDebugInfo.h26030644editdlrm
thread_name.h1480644editdlrm
Type.h6070644editdlrm
TypeCast.h69720644editdlrm
typeid.h187930644editdlrm
TypeIndex.h52510644editdlrm
TypeList.h169010644editdlrm
TypeTraits.h53680644editdlrm
Unicode.h2950644editdlrm
UniqueVoidPtr.h41170644editdlrm
Unroll.h6670644editdlrm
variant.h951820644editdlrm
win32-headers.h8580644editdlrm
Edit: /usr/local/lib64/python3.6/site-packages/torch/include/c10/util/LeftRight.h (6016B)
#include #include #include #include #include #include namespace c10 { namespace detail { struct IncrementRAII final { public: explicit IncrementRAII(std::atomic* counter) : _counter(counter) { _counter->fetch_add(1); } ~IncrementRAII() { _counter->fetch_sub(1); } private: std::atomic* _counter; C10_DISABLE_COPY_AND_ASSIGN(IncrementRAII); }; } // namespace detail // LeftRight wait-free readers synchronization primitive // https://hal.archives-ouvertes.fr/hal-01207881/document // // LeftRight is quite easy to use (it can make an arbitrary // data structure permit wait-free reads), but it has some // particular performance characteristics you should be aware // of if you're deciding to use it: // // - Reads still incur an atomic write (this is how LeftRight // keeps track of how long it needs to keep around the old // data structure) // // - Writes get executed twice, to keep both the left and right // versions up to date. So if your write is expensive or // nondeterministic, this is also an inappropriate structure // // LeftRight is used fairly rarely in PyTorch's codebase. If you // are still not sure if you need it or not, consult your local // C++ expert. // template class LeftRight final { public: template explicit LeftRight(const Args&... args) : _counters{{{0}, {0}}}, _foregroundCounterIndex(0), _foregroundDataIndex(0), _data{{T{args...}, T{args...}}}, _writeMutex() {} // Copying and moving would not be threadsafe. // Needs more thought and careful design to make that work. LeftRight(const LeftRight&) = delete; LeftRight(LeftRight&&) noexcept = delete; LeftRight& operator=(const LeftRight&) = delete; LeftRight& operator=(LeftRight&&) noexcept = delete; ~LeftRight() { // wait until any potentially running writers are finished { std::unique_lock lock(_writeMutex); } // wait until any potentially running readers are finished while (_counters[0].load() != 0 || _counters[1].load() != 0) { std::this_thread::yield(); } } template auto read(F&& readFunc) const -> typename std::result_of::type { detail::IncrementRAII _increment_counter( &_counters[_foregroundCounterIndex.load()]); return readFunc(_data[_foregroundDataIndex.load()]); } // Throwing an exception in writeFunc is ok but causes the state to be either // the old or the new state, depending on if the first or the second call to // writeFunc threw. template auto write(F&& writeFunc) -> typename std::result_of::type { std::unique_lock lock(_writeMutex); return _write(writeFunc); } private: template auto _write(const F& writeFunc) -> typename std::result_of::type { /* * Assume, A is in background and B in foreground. In simplified terms, we * want to do the following: * 1. Write to A (old background) * 2. Switch A/B * 3. Write to B (new background) * * More detailed algorithm (explanations on why this is important are below * in code): * 1. Write to A * 2. Switch A/B data pointers * 3. Wait until A counter is zero * 4. Switch A/B counters * 5. Wait until B counter is zero * 6. Write to B */ auto localDataIndex = _foregroundDataIndex.load(); // 1. Write to A _callWriteFuncOnBackgroundInstance(writeFunc, localDataIndex); // 2. Switch A/B data pointers localDataIndex = localDataIndex ^ 1; _foregroundDataIndex = localDataIndex; /* * 3. Wait until A counter is zero * * In the previous write run, A was foreground and B was background. * There was a time after switching _foregroundDataIndex (B to foreground) * and before switching _foregroundCounterIndex, in which new readers could * have read B but incremented A's counter. * * In this current run, we just switched _foregroundDataIndex (A back to * foreground), but before writing to the new background B, we have to make * sure A's counter was zero briefly, so all these old readers are gone. */ auto localCounterIndex = _foregroundCounterIndex.load(); _waitForBackgroundCounterToBeZero(localCounterIndex); /* * 4. Switch A/B counters * * Now that we know all readers on B are really gone, we can switch the * counters and have new readers increment A's counter again, which is the * correct counter since they're reading A. */ localCounterIndex = localCounterIndex ^ 1; _foregroundCounterIndex = localCounterIndex; /* * 5. Wait until B counter is zero * * This waits for all the readers on B that came in while both data and * counter for B was in foreground, i.e. normal readers that happened * outside of that brief gap between switching data and counter. */ _waitForBackgroundCounterToBeZero(localCounterIndex); // 6. Write to B return _callWriteFuncOnBackgroundInstance(writeFunc, localDataIndex); } template auto _callWriteFuncOnBackgroundInstance( const F& writeFunc, uint8_t localDataIndex) -> typename std::result_of::type { try { return writeFunc(_data[localDataIndex ^ 1]); } catch (...) { // recover invariant by copying from the foreground instance _data[localDataIndex ^ 1] = _data[localDataIndex]; // rethrow throw; } } void _waitForBackgroundCounterToBeZero(uint8_t counterIndex) { while (_counters[counterIndex ^ 1].load() != 0) { std::this_thread::yield(); } } mutable std::array, 2> _counters; std::atomic _foregroundCounterIndex; std::atomic _foregroundDataIndex; std::array _data; std::mutex _writeMutex; }; } // namespace c10