Describe the bug
With two threads, each running a copper::select loop and messaging each other, tsan gives lock-order-inversion reports.
To Reproduce
test-copper.cpp:
#include <thread>
#include "copper.h"
void b_run(copper::buffered_channel<bool>* a_to_b,
copper::buffered_channel<bool>* b_to_a) {
while (copper::channel_op_status::success == copper::select(
(*a_to_b) >> [&](bool v) {
std::cout << "ok b" << std::endl;
(void)b_to_a->push(true);
}));
}
int main(int argc, char* argv[]) {
copper::buffered_channel<bool> a_to_b;
copper::buffered_channel<bool> b_to_a;
new std::thread(&b_run, &a_to_b, &b_to_a);
(void)a_to_b.push(true);
while (copper::channel_op_status::success == copper::select(
b_to_a >> [&](bool v) {
std::cout << "ok a" << std::endl;
(void)a_to_b.push(true);
}));
return 0;
}
gnu-make Makefile:
CLANGFLAGS=-Wall -pedantic -Wno-gnu-zero-variadic-macro-arguments
CXXFLAGS=-std=c++20 -g -pedantic -O2
OBJ_EXT=o
EXE_SUFFIX=
LINKER=clang++ -o [email protected]
SAN=-fsanitize=thread
%.${OBJ_EXT} : %.cpp
${CXX} ${SAN} ${CLANGFLAGS} ${CXXFLAGS} -c -o [email protected] $<
all: test-copper
test-copper: test-copper.${OBJ_EXT}
${LINKER} ${SAN} $^ -lpthread
clean:
rm -f *~ *.${OBJ_EXT} test-copper
example report:
WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock) (pid=2594197)
Cycle in lock order graph: M12 (0x7fff99869c60) => M14 (0x7fff99869d88) => M12
Mutex M14 acquired here while holding mutex M12 in thread T1:
#0 pthread_mutex_lock <null> (test-copper+0x444ab6)
#1 __gthread_mutex_lock(pthread_mutex_t*) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10/bits/gthr-default.h:749:12 (test-copper+0x4bb59d)
#2 __gthread_recursive_mutex_lock(pthread_mutex_t*) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10/bits/gthr-default.h:811:10 (test-copper+0x4bb59d)
#3 std::recursive_mutex::lock() /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/mutex:106:17 (test-copper+0x4bb59d)
#4 std::unique_lock<std::recursive_mutex>::lock() /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/unique_lock.h:138:17 (test-copper+0x4bb59d)
#5 std::unique_lock<std::recursive_mutex>::unique_lock(std::recursive_mutex&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/unique_lock.h:68:2 (test-copper+0x4bb59d)
#6 copper::channel_op_status copper::channel<true, bool, std::queue, std::deque>::push_func_wt<(copper::wait_type)0, bool copper::channel<true, bool, std::queue, std::deque>::push_wt<(copper::wait_type)0, bool, bool, 0>(bool&&)::'lambda'()>(bool&&, bool&&...) /home/seth/src/test-copper/./copper.h:532:21 (test-copper+0x4bb59d)
#7 bool copper::channel<true, bool, std::queue, std::deque>::push_wt<(copper::wait_type)0, bool, bool, 0>(bool&&) /home/seth/src/test-copper/./copper.h:510:19 (test-copper+0x4ba90d)
#8 bool copper::channel<true, bool, std::queue, std::deque>::push<bool>(bool&&) /home/seth/src/test-copper/./copper.h:538:22 (test-copper+0x4ba90d)
#9 b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0::operator()(bool) const /home/seth/src/test-copper/test-copper.cpp:12:34 (test-copper+0x4ba90d)
#10 void std::__invoke_impl<void, b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0&, bool>(std::__invoke_other, b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/invoke.h:60:14 (test-copper+0x4ba90d)
#11 std::__invoke_result<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0&, bool>::type std::__invoke<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0&, bool>(b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/invoke.h:95:14 (test-copper+0x4ba90d)
#12 std::invoke_result<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0&, bool>::type std::invoke<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0&, bool>(b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/functional:88:14 (test-copper+0x4ba90d)
#13 copper::_detail::popper<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0, true, bool, std::queue, std::deque>::operator()(bool&&) /home/seth/src/test-copper/./copper.h:893:36 (test-copper+0x4ba90d)
#14 copper::channel_op_status copper::channel<true, bool, std::queue, std::deque>::_pop_func_with_lock_general<(copper::wait_type)0, copper::_detail::popper_base<true, bool, std::queue, std::deque>&, std::unique_lock<std::recursive_mutex> >(copper::_detail::popper_base<true, bool, std::queue, std::deque>&, std::unique_lock<std::recursive_mutex>&) /home/seth/src/test-copper/./copper.h:687:21 (test-copper+0x4bf350)
#15 copper::channel_op_status copper::channel<true, bool, std::queue, std::deque>::pop_func_wt<(copper::wait_type)0, copper::_detail::popper_base<true, bool, std::queue, std::deque>&>(copper::_detail::popper_base<true, bool, std::queue, std::deque>&) /home/seth/src/test-copper/./copper.h:455:22 (test-copper+0x4bf057)
#16 copper::channel_op_status copper::_detail::popper_base<true, bool, std::queue, std::deque>::select<(copper::wait_type)0>() /home/seth/src/test-copper/./copper.h:865:41 (test-copper+0x4ba61b)
#17 copper::channel_op_status copper::_detail::select_manager<copper::_detail::popper<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0, true, bool, std::queue, std::deque> >::select<(copper::wait_type)0>() /home/seth/src/test-copper/./copper.h:1510:36 (test-copper+0x4ba61b)
#18 copper::channel_op_status copper::_detail::opselect<(copper::wait_type)0, copper::_detail::popper<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0, true, bool, std::queue, std::deque>&>(std::tuple<copper::_detail::popper<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0, true, bool, std::queue, std::deque>&>&&) /home/seth/src/test-copper/./copper.h:1335:29 (test-copper+0x4ba61b)
#19 std::enable_if<((copper::_detail::popper<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0, true, bool, std::queue, std::deque>::is_op_base) && ...), copper::channel_op_status>::type copper::select<copper::_detail::popper<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0, true, bool, std::queue, std::deque> >(copper::_detail::popper<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0, true, bool, std::queue, std::deque>&&) /home/seth/src/test-copper/./copper.h:1444:12 (test-copper+0x4ba61b)
#20 b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*) /home/seth/src/test-copper/test-copper.cpp:9:50 (test-copper+0x4ba61b)
#21 void std::__invoke_impl<void, void (*)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*>(std::__invoke_other, void (*&&)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*&&, copper::channel<true, bool, std::queue, std::deque>*&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/invoke.h:60:14 (test-copper+0x4c21b7)
#22 std::__invoke_result<void (*)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*>::type std::__invoke<void (*)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*>(void (*&&)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*&&, copper::channel<true, bool, std::queue, std::deque>*&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/invoke.h:95:14 (test-copper+0x4c21b7)
#23 void std::thread::_Invoker<std::tuple<void (*)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*> >::_M_invoke<0ul, 1ul, 2ul>(std::_Index_tuple<0ul, 1ul, 2ul>) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/thread:264:13 (test-copper+0x4c21b7)
#24 std::thread::_Invoker<std::tuple<void (*)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*> >::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/thread:271:11 (test-copper+0x4c21b7)
#25 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*> > >::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/thread:215:13 (test-copper+0x4c21b7)
#26 <null> <null> (libstdc++.so.6+0xda693)
Mutex M12 previously acquired by the same thread here:
#0 pthread_mutex_lock <null> (test-copper+0x444ab6)
#1 __gthread_mutex_lock(pthread_mutex_t*) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10/bits/gthr-default.h:749:12 (test-copper+0x4bf03e)
#2 __gthread_recursive_mutex_lock(pthread_mutex_t*) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10/bits/gthr-default.h:811:10 (test-copper+0x4bf03e)
#3 std::recursive_mutex::lock() /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/mutex:106:17 (test-copper+0x4bf03e)
#4 std::unique_lock<std::recursive_mutex>::lock() /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/unique_lock.h:138:17 (test-copper+0x4bf03e)
#5 std::unique_lock<std::recursive_mutex>::unique_lock(std::recursive_mutex&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/unique_lock.h:68:2 (test-copper+0x4bf03e)
#6 copper::channel_op_status copper::channel<true, bool, std::queue, std::deque>::pop_func_wt<(copper::wait_type)0, copper::_detail::popper_base<true, bool, std::queue, std::deque>&>(copper::_detail::popper_base<true, bool, std::queue, std::deque>&) /home/seth/src/test-copper/./copper.h:454:21 (test-copper+0x4bf03e)
#7 copper::channel_op_status copper::_detail::popper_base<true, bool, std::queue, std::deque>::select<(copper::wait_type)0>() /home/seth/src/test-copper/./copper.h:865:41 (test-copper+0x4ba61b)
#8 copper::channel_op_status copper::_detail::select_manager<copper::_detail::popper<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0, true, bool, std::queue, std::deque> >::select<(copper::wait_type)0>() /home/seth/src/test-copper/./copper.h:1510:36 (test-copper+0x4ba61b)
#9 copper::channel_op_status copper::_detail::opselect<(copper::wait_type)0, copper::_detail::popper<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0, true, bool, std::queue, std::deque>&>(std::tuple<copper::_detail::popper<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0, true, bool, std::queue, std::deque>&>&&) /home/seth/src/test-copper/./copper.h:1335:29 (test-copper+0x4ba61b)
#10 std::enable_if<((copper::_detail::popper<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0, true, bool, std::queue, std::deque>::is_op_base) && ...), copper::channel_op_status>::type copper::select<copper::_detail::popper<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0, true, bool, std::queue, std::deque> >(copper::_detail::popper<b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*)::$_0, true, bool, std::queue, std::deque>&&) /home/seth/src/test-copper/./copper.h:1444:12 (test-copper+0x4ba61b)
#11 b_run(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*) /home/seth/src/test-copper/test-copper.cpp:9:50 (test-copper+0x4ba61b)
#12 void std::__invoke_impl<void, void (*)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*>(std::__invoke_other, void (*&&)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*&&, copper::channel<true, bool, std::queue, std::deque>*&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/invoke.h:60:14 (test-copper+0x4c21b7)
#13 std::__invoke_result<void (*)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*>::type std::__invoke<void (*)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*>(void (*&&)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*&&, copper::channel<true, bool, std::queue, std::deque>*&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/invoke.h:95:14 (test-copper+0x4c21b7)
#14 void std::thread::_Invoker<std::tuple<void (*)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*> >::_M_invoke<0ul, 1ul, 2ul>(std::_Index_tuple<0ul, 1ul, 2ul>) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/thread:264:13 (test-copper+0x4c21b7)
#15 std::thread::_Invoker<std::tuple<void (*)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*> >::operator()() /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/thread:271:11 (test-copper+0x4c21b7)
#16 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*), copper::channel<true, bool, std::queue, std::deque>*, copper::channel<true, bool, std::queue, std::deque>*> > >::_M_run() /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/thread:215:13 (test-copper+0x4c21b7)
#17 <null> <null> (libstdc++.so.6+0xda693)
Mutex M12 acquired here while holding mutex M14 in main thread:
#0 pthread_mutex_lock <null> (test-copper+0x444ab6)
#1 __gthread_mutex_lock(pthread_mutex_t*) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10/bits/gthr-default.h:749:12 (test-copper+0x4bb59d)
#2 __gthread_recursive_mutex_lock(pthread_mutex_t*) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10/bits/gthr-default.h:811:10 (test-copper+0x4bb59d)
#3 std::recursive_mutex::lock() /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/mutex:106:17 (test-copper+0x4bb59d)
#4 std::unique_lock<std::recursive_mutex>::lock() /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/unique_lock.h:138:17 (test-copper+0x4bb59d)
#5 std::unique_lock<std::recursive_mutex>::unique_lock(std::recursive_mutex&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/unique_lock.h:68:2 (test-copper+0x4bb59d)
#6 copper::channel_op_status copper::channel<true, bool, std::queue, std::deque>::push_func_wt<(copper::wait_type)0, bool copper::channel<true, bool, std::queue, std::deque>::push_wt<(copper::wait_type)0, bool, bool, 0>(bool&&)::'lambda'()>(bool&&, bool&&...) /home/seth/src/test-copper/./copper.h:532:21 (test-copper+0x4bb59d)
#7 bool copper::channel<true, bool, std::queue, std::deque>::push_wt<(copper::wait_type)0, bool, bool, 0>(bool&&) /home/seth/src/test-copper/./copper.h:510:19 (test-copper+0x4baa12)
#8 bool copper::channel<true, bool, std::queue, std::deque>::push<bool>(bool&&) /home/seth/src/test-copper/./copper.h:538:22 (test-copper+0x4baa12)
#9 main::$_1::operator()(bool) const /home/seth/src/test-copper/test-copper.cpp:29:33 (test-copper+0x4baa12)
#10 void std::__invoke_impl<void, main::$_1&, bool>(std::__invoke_other, main::$_1&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/invoke.h:60:14 (test-copper+0x4baa12)
#11 std::__invoke_result<main::$_1&, bool>::type std::__invoke<main::$_1&, bool>(main::$_1&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/invoke.h:95:14 (test-copper+0x4baa12)
#12 std::invoke_result<main::$_1&, bool>::type std::invoke<main::$_1&, bool>(main::$_1&, bool&&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/functional:88:14 (test-copper+0x4baa12)
#13 copper::_detail::popper<main::$_1, true, bool, std::queue, std::deque>::operator()(bool&&) /home/seth/src/test-copper/./copper.h:893:36 (test-copper+0x4baa12)
#14 copper::channel_op_status copper::channel<true, bool, std::queue, std::deque>::_pop_func_with_lock_general<(copper::wait_type)0, copper::_detail::popper_base<true, bool, std::queue, std::deque>&, std::unique_lock<std::recursive_mutex> >(copper::_detail::popper_base<true, bool, std::queue, std::deque>&, std::unique_lock<std::recursive_mutex>&) /home/seth/src/test-copper/./copper.h:687:21 (test-copper+0x4bf350)
#15 copper::channel_op_status copper::channel<true, bool, std::queue, std::deque>::pop_func_wt<(copper::wait_type)0, copper::_detail::popper_base<true, bool, std::queue, std::deque>&>(copper::_detail::popper_base<true, bool, std::queue, std::deque>&) /home/seth/src/test-copper/./copper.h:455:22 (test-copper+0x4bf057)
#16 copper::channel_op_status copper::_detail::popper_base<true, bool, std::queue, std::deque>::select<(copper::wait_type)0>() /home/seth/src/test-copper/./copper.h:865:41 (test-copper+0x4ba77b)
#17 copper::channel_op_status copper::_detail::select_manager<copper::_detail::popper<main::$_1, true, bool, std::queue, std::deque> >::select<(copper::wait_type)0>() /home/seth/src/test-copper/./copper.h:1510:36 (test-copper+0x4ba77b)
#18 copper::channel_op_status copper::_detail::opselect<(copper::wait_type)0, copper::_detail::popper<main::$_1, true, bool, std::queue, std::deque>&>(std::tuple<copper::_detail::popper<main::$_1, true, bool, std::queue, std::deque>&>&&) /home/seth/src/test-copper/./copper.h:1335:29 (test-copper+0x4ba77b)
#19 std::enable_if<((copper::_detail::popper<main::$_1, true, bool, std::queue, std::deque>::is_op_base) && ...), copper::channel_op_status>::type copper::select<copper::_detail::popper<main::$_1, true, bool, std::queue, std::deque> >(copper::_detail::popper<main::$_1, true, bool, std::queue, std::deque>&&) /home/seth/src/test-copper/./copper.h:1444:12 (test-copper+0x4ba77b)
#20 main /home/seth/src/test-copper/test-copper.cpp:26:50 (test-copper+0x4ba77b)
Mutex M14 previously acquired by the same thread here:
#0 pthread_mutex_lock <null> (test-copper+0x444ab6)
#1 __gthread_mutex_lock(pthread_mutex_t*) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10/bits/gthr-default.h:749:12 (test-copper+0x4bf03e)
#2 __gthread_recursive_mutex_lock(pthread_mutex_t*) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10/bits/gthr-default.h:811:10 (test-copper+0x4bf03e)
#3 std::recursive_mutex::lock() /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/mutex:106:17 (test-copper+0x4bf03e)
#4 std::unique_lock<std::recursive_mutex>::lock() /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/unique_lock.h:138:17 (test-copper+0x4bf03e)
#5 std::unique_lock<std::recursive_mutex>::unique_lock(std::recursive_mutex&) /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/unique_lock.h:68:2 (test-copper+0x4bf03e)
#6 copper::channel_op_status copper::channel<true, bool, std::queue, std::deque>::pop_func_wt<(copper::wait_type)0, copper::_detail::popper_base<true, bool, std::queue, std::deque>&>(copper::_detail::popper_base<true, bool, std::queue, std::deque>&) /home/seth/src/test-copper/./copper.h:454:21 (test-copper+0x4bf03e)
#7 copper::channel_op_status copper::_detail::popper_base<true, bool, std::queue, std::deque>::select<(copper::wait_type)0>() /home/seth/src/test-copper/./copper.h:865:41 (test-copper+0x4ba77b)
#8 copper::channel_op_status copper::_detail::select_manager<copper::_detail::popper<main::$_1, true, bool, std::queue, std::deque> >::select<(copper::wait_type)0>() /home/seth/src/test-copper/./copper.h:1510:36 (test-copper+0x4ba77b)
#9 copper::channel_op_status copper::_detail::opselect<(copper::wait_type)0, copper::_detail::popper<main::$_1, true, bool, std::queue, std::deque>&>(std::tuple<copper::_detail::popper<main::$_1, true, bool, std::queue, std::deque>&>&&) /home/seth/src/test-copper/./copper.h:1335:29 (test-copper+0x4ba77b)
#10 std::enable_if<((copper::_detail::popper<main::$_1, true, bool, std::queue, std::deque>::is_op_base) && ...), copper::channel_op_status>::type copper::select<copper::_detail::popper<main::$_1, true, bool, std::queue, std::deque> >(copper::_detail::popper<main::$_1, true, bool, std::queue, std::deque>&&) /home/seth/src/test-copper/./copper.h:1444:12 (test-copper+0x4ba77b)
#11 main /home/seth/src/test-copper/test-copper.cpp:26:50 (test-copper+0x4ba77b)
Thread T1 (tid=2594199, running) created by main thread at:
#0 pthread_create <null> (test-copper+0x426d0b)
#1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) <null> (libstdc++.so.6+0xda969)
#2 __libc_start_main csu/../csu/libc-start.c:332:16 (libc.so.6+0x28564)
SUMMARY: ThreadSanitizer: lock-order-inversion (potential deadlock) (/home/seth/src/test-copper/test-copper+0x444ab6) in pthread_mutex_lock
==================
Platform
$ uname -a
Linux chirp 5.11.0-22-generic #23-Ubuntu SMP Thu Jun 17 00:34:23 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
$ clang --version
Ubuntu clang version 12.0.0-1ubuntu1
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
bug