From 7549ac1049f40bd0dac4eca9941b30159fad5f9d Mon Sep 17 00:00:00 2001 From: Kevin Mihelich Date: Sat, 13 Jul 2013 20:32:37 +0000 Subject: [PATCH] community/openimageio: update arm patch --- community/openimageio/PKGBUILD | 2 +- community/openimageio/arm.patch | 712 -------------------------------- 2 files changed, 1 insertion(+), 713 deletions(-) diff --git a/community/openimageio/PKGBUILD b/community/openimageio/PKGBUILD index 76d75bef3..da07354d3 100644 --- a/community/openimageio/PKGBUILD +++ b/community/openimageio/PKGBUILD @@ -20,7 +20,7 @@ optdepends=('qt4: iv image viewer' source=(https://github.com/OpenImageIO/oiio/tarball/Release-$pkgver arm.patch) md5sums=('8738dd9b24fc4f0cb3eebb094347104a' - '97557842752c812c7c84dd23cc7cd6ff') + 'd3a81f05892a747e922aed541059aade') build() { cd "$srcdir"/$_pkgname* diff --git a/community/openimageio/arm.patch b/community/openimageio/arm.patch index 2a445f43f..54e7573b7 100644 --- a/community/openimageio/arm.patch +++ b/community/openimageio/arm.patch @@ -27,715 +27,3 @@ diff -urN a/src/include/thread.h b/src/include/thread.h #endif /* ! USE_TBB_ATOMIC */ -diff -urN a/src/include/thread.h.orig b/src/include/thread.h.orig ---- a/src/include/thread.h.orig 1969-12-31 17:00:00.000000000 -0700 -+++ b/src/include/thread.h.orig 2013-07-08 13:47:25.000000000 -0600 -@@ -0,0 +1,708 @@ -+/* -+ Copyright 2008 Larry Gritz and the other authors and contributors. -+ All Rights Reserved. -+ -+ Redistribution and use in source and binary forms, with or without -+ modification, are permitted provided that the following conditions are -+ met: -+ * Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ * Neither the name of the software's owners nor the names of its -+ contributors may be used to endorse or promote products derived from -+ this software without specific prior written permission. -+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ -+ (This is the Modified BSD License) -+*/ -+ -+ -+///////////////////////////////////////////////////////////////////////// -+/// @file thread.h -+/// -+/// @brief Wrappers and utilities for multithreading. -+///////////////////////////////////////////////////////////////////////// -+ -+ -+#ifndef OPENIMAGEIO_THREAD_H -+#define OPENIMAGEIO_THREAD_H -+ -+#include "version.h" -+#include "sysutil.h" -+ -+ -+// defining NOMINMAX to prevent problems with std::min/std::max -+// and std::numeric_limits::min()/std::numeric_limits::max() -+// when boost include windows.h -+#ifdef _MSC_VER -+# define WIN32_LEAN_AND_MEAN -+# define VC_EXTRALEAN -+# ifndef NOMINMAX -+# define NOMINMAX -+# endif -+#endif -+ -+#include -+#if defined(__GNUC__) && (BOOST_VERSION == 104500) -+// gcc reports errors inside some of the boost headers with boost 1.45 -+// See: https://svn.boost.org/trac/boost/ticket/4818 -+#pragma GCC diagnostic ignored "-Wunused-variable" -+#endif -+ -+#include -+#include -+#include -+ -+#if defined(__GNUC__) && (BOOST_VERSION == 104500) -+// can't restore via push/pop in all versions of gcc (warning push/pop implemented for 4.6+ only) -+#pragma GCC diagnostic error "-Wunused-variable" -+#endif -+ -+#ifndef USE_TBB -+# define USE_TBB 0 -+#endif -+ -+// Include files we need for atomic counters. -+// Some day, we hope this is all replaced by use of std::atomic<>. -+#if USE_TBB -+# include -+# include -+# define USE_TBB_ATOMIC 1 -+# define USE_TBB_SPINLOCK 1 -+#else -+# define USE_TBB_ATOMIC 0 -+# define USE_TBB_SPINLOCK 0 -+#endif -+ -+ -+#if defined(_MSC_VER) && !USE_TBB -+# include -+# include -+# pragma intrinsic (_InterlockedExchangeAdd) -+# pragma intrinsic (_InterlockedCompareExchange) -+# pragma intrinsic (_InterlockedCompareExchange64) -+# pragma intrinsic (_ReadWriteBarrier) -+# if defined(_WIN64) -+# pragma intrinsic(_InterlockedExchangeAdd64) -+# endif -+// InterlockedExchangeAdd64 is not available for XP -+# if defined(_WIN32_WINNT) && _WIN32_WINNT <= 0x0501 -+inline long long -+InterlockedExchangeAdd64 (volatile long long *Addend, long long Value) -+{ -+ long long Old; -+ do { -+ Old = *Addend; -+ } while (_InterlockedCompareExchange64(Addend, Old + Value, Old) != Old); -+ return Old; -+} -+# endif -+#endif -+ -+#if defined(__GNUC__) && (defined(_GLIBCXX_ATOMIC_BUILTINS) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 401)) -+#if !defined(__FreeBSD__) || defined(__x86_64__) -+#define USE_GCC_ATOMICS -+#endif -+#endif -+ -+OIIO_NAMESPACE_ENTER -+{ -+ -+/// Null mutex that can be substituted for a real one to test how much -+/// overhead is associated with a particular mutex. -+class null_mutex { -+public: -+ null_mutex () { } -+ ~null_mutex () { } -+ void lock () { } -+ void unlock () { } -+ void lock_shared () { } -+ void unlock_shared () { } -+}; -+ -+/// Null lock that can be substituted for a real one to test how much -+/// overhead is associated with a particular lock. -+template -+class null_lock { -+public: -+ null_lock (T &m) { } -+}; -+ -+ -+// Null thread-specific ptr that just wraps a single ordinary pointer -+// -+template -+class null_thread_specific_ptr { -+public: -+ typedef void (*destructor_t)(T *); -+ null_thread_specific_ptr (destructor_t dest=NULL) -+ : m_ptr(NULL), m_dest(dest) { } -+ ~null_thread_specific_ptr () { reset (NULL); } -+ T * get () { return m_ptr; } -+ void reset (T *newptr=NULL) { -+ if (m_ptr) { -+ if (m_dest) -+ (*m_dest) (m_ptr); -+ else -+ delete m_ptr; -+ } -+ m_ptr = newptr; -+ } -+private: -+ T *m_ptr; -+ destructor_t m_dest; -+}; -+ -+ -+#ifdef NOTHREADS -+ -+// Definitions that we use for debugging to turn off all mutexes, locks, -+// and atomics in order to test the performance hit of our thread safety. -+ -+// Null thread-specific ptr that just wraps a single ordinary pointer -+// -+template -+class thread_specific_ptr { -+public: -+ typedef void (*destructor_t)(T *); -+ thread_specific_ptr (destructor_t dest=NULL) -+ : m_ptr(NULL), m_dest(dest) { } -+ ~thread_specific_ptr () { reset (NULL); } -+ T * get () { return m_ptr; } -+ void reset (T *newptr=NULL) { -+ if (m_ptr) { -+ if (m_dest) -+ (*m_dest) (m_ptr); -+ else -+ delete m_ptr; -+ } -+ m_ptr = newptr; -+ } -+private: -+ T *m_ptr; -+ destructor_t m_dest; -+}; -+ -+ -+typedef null_mutex mutex; -+typedef null_mutex recursive_mutex; -+typedef null_lock lock_guard; -+typedef null_lock recursive_lock_guard; -+ -+ -+#else -+ -+// Fairly modern Boost has all the mutex and lock types we need. -+ -+typedef boost::mutex mutex; -+typedef boost::recursive_mutex recursive_mutex; -+typedef boost::lock_guard< boost::mutex > lock_guard; -+typedef boost::lock_guard< boost::recursive_mutex > recursive_lock_guard; -+using boost::thread_specific_ptr; -+ -+#endif -+ -+ -+ -+/// Atomic version of: r = *at, *at += x, return r -+/// For each of several architectures. -+inline int -+atomic_exchange_and_add (volatile int *at, int x) -+{ -+#ifdef USE_GCC_ATOMICS -+ return __sync_fetch_and_add ((int *)at, x); -+#elif USE_TBB -+ atomic *a = (atomic *)at; -+ return a->fetch_and_add (x); -+#elif defined(_MSC_VER) -+ // Windows -+ return _InterlockedExchangeAdd ((volatile LONG *)at, x); -+#else -+# error No atomics on this platform. -+#endif -+} -+ -+ -+ -+inline long long -+atomic_exchange_and_add (volatile long long *at, long long x) -+{ -+#ifdef USE_GCC_ATOMICS -+ return __sync_fetch_and_add (at, x); -+#elif USE_TBB -+ atomic *a = (atomic *)at; -+ return a->fetch_and_add (x); -+#elif defined(_MSC_VER) -+ // Windows -+# if defined(_WIN64) -+ return _InterlockedExchangeAdd64 ((volatile LONGLONG *)at, x); -+# else -+ return InterlockedExchangeAdd64 ((volatile LONGLONG *)at, x); -+# endif -+#else -+# error No atomics on this platform. -+#endif -+} -+ -+ -+ -+/// Atomic version of: -+/// if (*at == compareval) { -+/// *at = newval; return true; -+/// } else { -+/// return false; -+/// -+inline bool -+atomic_compare_and_exchange (volatile int *at, int compareval, int newval) -+{ -+#ifdef USE_GCC_ATOMICS -+ return __sync_bool_compare_and_swap (at, compareval, newval); -+#elif USE_TBB -+ atomic *a = (atomic *)at; -+ return a->compare_and_swap (newval, compareval) == newval; -+#elif defined(_MSC_VER) -+ return (_InterlockedCompareExchange ((volatile LONG *)at, newval, compareval) == compareval); -+#else -+# error No atomics on this platform. -+#endif -+} -+ -+ -+ -+inline bool -+atomic_compare_and_exchange (volatile long long *at, long long compareval, long long newval) -+{ -+#ifdef USE_GCC_ATOMICS -+ return __sync_bool_compare_and_swap (at, compareval, newval); -+#elif USE_TBB -+ atomic *a = (atomic *)at; -+ return a->compare_and_swap (newval, compareval) == newval; -+#elif defined(_MSC_VER) -+ return (_InterlockedCompareExchange64 ((volatile LONGLONG *)at, newval, compareval) == compareval); -+#else -+# error No atomics on this platform. -+#endif -+} -+ -+ -+ -+/// Yield the processor for the rest of the timeslice. -+/// -+inline void -+yield () -+{ -+#if defined(__GNUC__) -+ sched_yield (); -+#elif defined(_MSC_VER) -+ SwitchToThread (); -+#else -+# error No yield on this platform. -+#endif -+} -+ -+ -+ -+// Slight pause -+inline void -+pause (int delay) -+{ -+#if defined(__GNUC__) -+ for (int i = 0; i < delay; ++i) { -+#if defined __arm__ || defined __s390__ -+ __asm__ __volatile__("NOP;"); -+#else -+ __asm__ __volatile__("pause;"); -+#endif -+ } -+#elif USE_TBB -+ __TBB_Pause(delay); -+#elif defined(_MSC_VER) -+ for (int i = 0; i < delay; ++i) { -+#if defined (_WIN64) -+ YieldProcessor(); -+#else -+ _asm pause -+#endif /* _WIN64 */ -+ } -+#else -+ // No pause on this platform, just punt -+ for (int i = 0; i < delay; ++i) ; -+#endif -+} -+ -+ -+ -+// Helper class to deliver ever longer pauses until we yield our timeslice. -+class atomic_backoff { -+public: -+ atomic_backoff () : m_count(1) { } -+ -+ void operator() () { -+ if (m_count <= 16) { -+ pause (m_count); -+ m_count *= 2; -+ } else { -+ yield(); -+ } -+ } -+ -+private: -+ int m_count; -+}; -+ -+ -+ -+#if USE_TBB_ATOMIC -+using tbb::atomic; -+#else -+// If we're not using TBB's atomic, we need to define our own atomic<>. -+ -+ -+/// Atomic integer. Increment, decrement, add, and subtract in a -+/// totally thread-safe manner. -+template -+class atomic { -+public: -+ /// Construct with initial value. -+ /// -+ atomic (T val=0) : m_val(val) { } -+ -+ ~atomic () { } -+ -+ /// Retrieve value -+ /// -+ T operator() () const { return atomic_exchange_and_add (&m_val, 0); } -+ -+ /// Retrieve value -+ /// -+ operator T() const { return atomic_exchange_and_add (&m_val, 0); } -+ -+ /// Fast retrieval of value, no interchange, don't care about memory -+ /// fences. -+ T fast_value () const { return m_val; } -+ -+ /// Assign new value. -+ /// -+ T operator= (T x) { -+ //incorrect? return (m_val = x); -+ while (1) { -+ T result = m_val; -+ if (atomic_compare_and_exchange (&m_val, result, x)) -+ break; -+ } -+ return x; -+ } -+ -+ /// Pre-increment: ++foo -+ /// -+ T operator++ () { return atomic_exchange_and_add (&m_val, 1) + 1; } -+ -+ /// Post-increment: foo++ -+ /// -+ T operator++ (int) { return atomic_exchange_and_add (&m_val, 1); } -+ -+ /// Pre-decrement: --foo -+ /// -+ T operator-- () { return atomic_exchange_and_add (&m_val, -1) - 1; } -+ -+ /// Post-decrement: foo-- -+ /// -+ T operator-- (int) { return atomic_exchange_and_add (&m_val, -1); } -+ -+ /// Add to the value, return the new result -+ /// -+ T operator+= (T x) { return atomic_exchange_and_add (&m_val, x) + x; } -+ -+ /// Subtract from the value, return the new result -+ /// -+ T operator-= (T x) { return atomic_exchange_and_add (&m_val, -x) - x; } -+ -+ bool bool_compare_and_swap (T compareval, T newval) { -+ return atomic_compare_and_exchange (&m_val, compareval, newval); -+ } -+ -+ T operator= (const atomic &x) { -+ T r = x(); -+ *this = r; -+ return r; -+ } -+ -+private: -+#ifdef __arm__ -+ OIIO_ALIGN(8) -+#endif -+ volatile mutable T m_val; -+ -+ // Disallow copy construction by making private and unimplemented. -+ atomic (atomic const &); -+}; -+ -+ -+#endif /* ! USE_TBB_ATOMIC */ -+ -+ -+#ifdef NOTHREADS -+ -+typedef int atomic_int; -+typedef long long atomic_ll; -+ -+#else -+ -+typedef atomic atomic_int; -+typedef atomic atomic_ll; -+ -+#endif -+ -+ -+ -+#ifdef NOTHREADS -+ -+typedef null_mutex spin_mutex; -+typedef null_lock spin_lock; -+ -+#elif USE_TBB_SPINLOCK -+ -+// Use TBB's spin locks -+typedef tbb::spin_mutex spin_mutex; -+typedef tbb::spin_mutex::scoped_lock spin_lock; -+ -+ -+#else -+ -+// Define our own spin locks. Do we trust them? -+ -+ -+/// A spin_mutex is semantically equivalent to a regular mutex, except -+/// for the following: -+/// - A spin_mutex is just 4 bytes, whereas a regular mutex is quite -+/// large (44 bytes for pthread). -+/// - A spin_mutex is extremely fast to lock and unlock, whereas a regular -+/// mutex is surprisingly expensive just to acquire a lock. -+/// - A spin_mutex takes CPU while it waits, so this can be very -+/// wasteful compared to a regular mutex that blocks (gives up its -+/// CPU slices until it acquires the lock). -+/// -+/// The bottom line is that mutex is the usual choice, but in cases where -+/// you need to acquire locks very frequently, but only need to hold the -+/// lock for a very short period of time, you may save runtime by using -+/// a spin_mutex, even though it's non-blocking. -+/// -+/// N.B. A spin_mutex is only the size of an int. To avoid "false -+/// sharing", be careful not to put two spin_mutex objects on the same -+/// cache line (within 128 bytes of each other), or the two mutexes may -+/// effectively (and wastefully) lock against each other. -+/// -+class spin_mutex { -+public: -+ /// Default constructor -- initialize to unlocked. -+ /// -+ spin_mutex (void) { m_locked = 0; } -+ -+ ~spin_mutex (void) { } -+ -+ /// Copy constructor -- initialize to unlocked. -+ /// -+ spin_mutex (const spin_mutex &) { m_locked = 0; } -+ -+ /// Assignment does not do anything, since lockedness should not -+ /// transfer. -+ const spin_mutex& operator= (const spin_mutex&) { return *this; } -+ -+ /// Acquire the lock, spin until we have it. -+ /// -+ void lock () { -+ // To avoid spinning too tightly, we use the atomic_backoff to -+ // provide increasingly longer pauses, and if the lock is under -+ // lots of contention, eventually yield the timeslice. -+ atomic_backoff backoff; -+ -+ // Try to get ownership of the lock. Though experimentation, we -+ // found that OIIO_UNLIKELY makes this just a bit faster on -+ // gcc x86/x86_64 systems. -+ while (! OIIO_UNLIKELY(try_lock())) { -+ do { -+ backoff(); -+ } while (m_locked); -+ -+ // The full try_lock() involves a compare_and_swap, which -+ // writes memory, and that will lock the bus. But a normal -+ // read of m_locked will let us spin until the value -+ // changes, without locking the bus. So it's faster to -+ // check in this manner until the mutex appears to be free. -+ } -+ } -+ -+ /// Release the lock that we hold. -+ /// -+ void unlock () { -+#if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) -+ // Fastest way to do it is with a store with "release" semantics -+ __asm__ __volatile__("": : :"memory"); -+ m_locked = 0; -+ // N.B. GCC gives us an intrinsic that is even better, an atomic -+ // assignment of 0 with "release" barrier semantics: -+ // __sync_lock_release (&m_locked); -+ // But empirically we found it not as performant as the above. -+#elif defined(_MSC_VER) -+ _ReadWriteBarrier(); -+ m_locked = 0; -+#else -+ // Otherwise, just assign zero to the atomic (but that's a full -+ // memory barrier). -+ *(atomic_int *)&m_locked = 0; -+#endif -+ } -+ -+ /// Try to acquire the lock. Return true if we have it, false if -+ /// somebody else is holding the lock. -+ bool try_lock () { -+#if USE_TBB_ATOMIC -+ // TBB's compare_and_swap returns the original value -+ return (*(atomic_int *)&m_locked).compare_and_swap (0, 1) == 0; -+#elif defined(__GNUC__) -+ // GCC gives us an intrinsic that is even better -- an atomic -+ // exchange with "acquire" barrier semantics. -+ return __sync_lock_test_and_set (&m_locked, 1) == 0; -+#else -+ // Our compare_and_swap returns true if it swapped -+ return atomic_compare_and_exchange (&m_locked, 0, 1); -+#endif -+ } -+ -+ /// Helper class: scoped lock for a spin_mutex -- grabs the lock upon -+ /// construction, releases the lock when it exits scope. -+ class lock_guard { -+ public: -+ lock_guard (spin_mutex &fm) : m_fm(fm) { m_fm.lock(); } -+ ~lock_guard () { m_fm.unlock(); } -+ private: -+ lock_guard(); // Do not implement (even though TBB does) -+ lock_guard(const lock_guard& other); // Do not implement -+ lock_guard& operator = (const lock_guard& other); // Do not implement -+ spin_mutex & m_fm; -+ }; -+ -+private: -+ volatile int m_locked; ///< Atomic counter is zero if nobody holds the lock -+}; -+ -+ -+typedef spin_mutex::lock_guard spin_lock; -+ -+#endif -+ -+ -+ -+/// Spinning reader/writer mutex. This is just like spin_mutex, except -+/// that there are separate locking mechanisms for "writers" (exclusive -+/// holders of the lock, presumably because they are modifying whatever -+/// the lock is protecting) and "readers" (non-exclusive, non-modifying -+/// tasks that may access the protectee simultaneously). -+class spin_rw_mutex { -+public: -+ /// Default constructor -- initialize to unlocked. -+ /// -+ spin_rw_mutex (void) { m_readers = 0; } -+ -+ ~spin_rw_mutex (void) { } -+ -+ /// Copy constructor -- initialize to unlocked. -+ /// -+ spin_rw_mutex (const spin_rw_mutex &) { m_readers = 0; } -+ -+ /// Assignment does not do anything, since lockedness should not -+ /// transfer. -+ const spin_rw_mutex& operator= (const spin_rw_mutex&) { return *this; } -+ -+ /// Acquire the reader lock. -+ /// -+ void read_lock () { -+ // Spin until there are no writers active -+ m_locked.lock(); -+ // Register ourself as a reader -+ ++m_readers; -+ // Release the lock, to let other readers work -+ m_locked.unlock(); -+ } -+ -+ /// Release the reader lock. -+ /// -+ void read_unlock () { -+ --m_readers; // it's atomic, no need to lock to release -+ } -+ -+ /// Acquire the writer lock. -+ /// -+ void write_lock () { -+ // Make sure no new readers (or writers) can start -+ m_locked.lock(); -+ // Spin until the last reader is done, at which point we will be -+ // the sole owners and nobody else (reader or writer) can acquire -+ // the resource until we release it. -+ while (*(volatile int *)&m_readers > 0) -+ ; -+ } -+ -+ /// Release the writer lock. -+ /// -+ void write_unlock () { -+ // Let other readers or writers get the lock -+ m_locked.unlock (); -+ } -+ -+ /// Helper class: scoped read lock for a spin_rw_mutex -- grabs the -+ /// read lock upon construction, releases the lock when it exits scope. -+ class read_lock_guard { -+ public: -+ read_lock_guard (spin_rw_mutex &fm) : m_fm(fm) { m_fm.read_lock(); } -+ ~read_lock_guard () { m_fm.read_unlock(); } -+ private: -+ read_lock_guard(); // Do not implement -+ read_lock_guard(const read_lock_guard& other); // Do not implement -+ read_lock_guard& operator = (const read_lock_guard& other); // Do not implement -+ spin_rw_mutex & m_fm; -+ }; -+ -+ /// Helper class: scoped write lock for a spin_rw_mutex -- grabs the -+ /// read lock upon construction, releases the lock when it exits scope. -+ class write_lock_guard { -+ public: -+ write_lock_guard (spin_rw_mutex &fm) : m_fm(fm) { m_fm.write_lock(); } -+ ~write_lock_guard () { m_fm.write_unlock(); } -+ private: -+ write_lock_guard(); // Do not implement -+ write_lock_guard(const write_lock_guard& other); // Do not implement -+ write_lock_guard& operator = (const write_lock_guard& other); // Do not implement -+ spin_rw_mutex & m_fm; -+ }; -+ -+private: -+ OIIO_CACHE_ALIGN -+ spin_mutex m_locked; // write lock -+ char pad1_[OIIO_CACHE_LINE_SIZE-sizeof(spin_mutex)]; -+ OIIO_CACHE_ALIGN -+ atomic_int m_readers; // number of readers -+ char pad2_[OIIO_CACHE_LINE_SIZE-sizeof(atomic_int)]; -+}; -+ -+ -+typedef spin_rw_mutex::read_lock_guard spin_rw_read_lock; -+typedef spin_rw_mutex::write_lock_guard spin_rw_write_lock; -+ -+ -+} -+OIIO_NAMESPACE_EXIT -+ -+#endif // OPENIMAGEIO_THREAD_H