#define CDSLIB_COMPILER_FEATURE_TSAN_H
// Thread Sanitizer annotations.
-// From https://groups.google.com/d/msg/thread-sanitizer/SsrHB7FTnTk/mNTGNLQj-9cJ
+// From http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/tsan/annotate_happens_before.cc?view=markup
//@cond
CDS_TSAN_ANNOTATE_IGNORE_READS_END
# define CDS_TSAN_ANNOTATE_NEW_MEMORY( addr, sz ) AnnotateNewMemory( (char *) __FILE__, __LINE__, reinterpret_cast<void *>(addr), sz )
+# define CDS_TSAN_ANNOTATE_MUTEX_ACQUIRED( addr ) AnnotateRWLockAcquired( __FILE__, __LINE__, reinterpret_cast<void *>(addr), 1 )
+# define CDS_TSAN_ANNOTATE_MUTEX_RELEASED( addr ) AnnotateRWLockReleased( __FILE__, __LINE__, reinterpret_cast<void *>(addr), 1 )
+
// provided by TSan
extern "C" {
void AnnotateHappensBefore(const char *f, int l, void *addr);
void AnnotateNewMemory(char *f, int l, void * mem, size_t size);
+ void AnnotateRWLockAcquired( const char *f, int l, void *m, long is_w );
+ void AnnotateRWLockReleased( const char *f, int l, void *m, long is_w );
}
#else // CDS_THREAD_SANITIZER_ENABLED
# define CDS_TSAN_ANNOTATE_NEW_MEMORY( addr, sz )
+# define CDS_TSAN_ANNOTATE_MUTEX_ACQUIRED( addr )
+# define CDS_TSAN_ANNOTATE_MUTEX_RELEASED( addr )
+
#endif
//@endcond
*/
bool dequeue( value_type& dest )
{
- return dequeue_with( [&dest]( value_type& src ) { dest = std::move( src );});
+ return dequeue_with( [&dest]( value_type& src ) {
+ // TSan finds a race between this read of \p src and node_type constructor
+ // I think, it is wrong
+ CDS_TSAN_ANNOTATE_IGNORE_READS_BEGIN;
+ dest = std::move( src );
+ CDS_TSAN_ANNOTATE_IGNORE_READS_END;
+ });
}
/// Dequeues a value using a functor
*/
bool dequeue( value_type& dest )
{
- return dequeue_with( [&dest]( value_type& src ) { dest = std::move( src ); });
+ return dequeue_with( [&dest]( value_type& src ) {
+ // TSan finds a race between this read of \p src and node_type constructor
+ // I think, it is wrong
+ CDS_TSAN_ANNOTATE_IGNORE_READS_BEGIN;
+ dest = std::move( src );
+ CDS_TSAN_ANNOTATE_IGNORE_READS_END;
+ });
}
/// Dequeues a value using a functor
*/
bool dequeue( value_type& dest )
{
- return dequeue_with( [&dest]( value_type& src ) { dest = std::move( src );});
+ return dequeue_with( [&dest]( value_type& src ) {
+ // TSan finds a race between this read of \p src and node_type constructor
+ // I think, it is wrong
+ CDS_TSAN_ANNOTATE_IGNORE_READS_BEGIN;
+ dest = std::move( src );
+ CDS_TSAN_ANNOTATE_IGNORE_READS_END;
+ });
}
/// Dequeues a value using a functor
{
backoff_strategy backoff;
while ( nTryCount-- ) {
- if ( try_lock())
+ if ( try_lock() ) {
+ CDS_TSAN_ANNOTATE_MUTEX_ACQUIRED( &m_spin );
return true;
+ }
backoff();
}
return false;
CDS_DEBUG_ONLY( m_dbgOwnerId = OS::c_NullThreadId; )
m_spin.store( false, atomics::memory_order_release );
+ CDS_TSAN_ANNOTATE_MUTEX_RELEASED( &m_spin );
}
};
backoff_strategy bkoff;
while ( nTryCount-- ) {
- if ( try_acquire())
+ if ( try_acquire() ) {
+ CDS_TSAN_ANNOTATE_MUTEX_ACQUIRED( &m_spin );
return true;
+ }
bkoff();
}
return false;
else {
free();
m_spin.store( 0, atomics::memory_order_release );
+ CDS_TSAN_ANNOTATE_MUTEX_RELEASED( &m_spin );
}
return true;
}
thread_record * pOldHead = m_pHead.load( atomics::memory_order_acquire );
do {
pRec->m_list.m_pNext = pOldHead;
+ CDS_TSAN_ANNOTATE_HAPPENS_BEFORE( &( pRec->m_list.m_pNext ));
} while ( !m_pHead.compare_exchange_weak( pOldHead, pRec, atomics::memory_order_release, atomics::memory_order_relaxed ));
return pRec;
hplist_node * pOldHead = m_pListHead.load( atomics::memory_order_acquire );
do {
hprec->m_pNextNode = pOldHead;
+ CDS_TSAN_ANNOTATE_HAPPENS_BEFORE( &( hprec->m_pNextNode ));
} while ( !m_pListHead.compare_exchange_weak( pOldHead, hprec, atomics::memory_order_release, atomics::memory_order_relaxed ));
return hprec;
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/unit)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/stress)
+
+file(GLOB SANITIZER_OPTION_FILES ${PROJECT_SOURCE_DIR}/tools/tsan-suppression)
+file(COPY ${SANITIZER_OPTION_FILES} DESTINATION ${EXECUTABLE_OUTPUT_PATH})
item_type* p;
while ( (p = static_cast<item_type*>( m_FreeList.get())) != nullptr ) {
+ CDS_TSAN_ANNOTATE_IGNORE_RW_BEGIN;
p->counter++;
+ CDS_TSAN_ANNOTATE_IGNORE_RW_END;
arr[n] = p;
++m_nSuccess;
++n;
# verosity=n Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).
# history_size=[0..7], default 2
-# DHP
-#race:cds::gc::details::retired_ptr::free
+# false: LazyList potential deadlock
+deadlock:cds/intrusive/impl/lazy_list.h
-# uRCU false positive
-#race:cds::urcu::gc*::batch_retire*
+# false: BronsonAVLTree potential deadlock
+deadlock:cds/container/impl/bronson_avltree_map_rcu.h
-# EllenBinTree false positive
-#race:ellen_bintree_pool::internal_node_allocator*::allocate
+#TODO: temporary suppressed. Must be researched later
+race:cds/container/impl/bronson_avltree_map_rcu.h
-# TODO: TSan false positive or library issues?
-#race:cds::container::OptimisticQueue*::alloc_node
+#TODO: MSPriorityQueue - temporary suppressed. Must be researched later
+# Seems, TSan don't see spinlock blocking. How to learn TSan to see non-traditional locking algo?..
+race:cds::intrusive::MSPriorityQueue
+
+#TODO: gc::DHP must be reimplemented ASAP
+race:cds::gc::dhp::GarbageCollector::scan
+
+#TODO: temporary suppressed. Must be researched later
+race:cds::memory::michael::Heap
\ No newline at end of file