Building master (df303ceb650521dc7b1ba91e0eea383c387a5860) on Linux Mint 19.3 (x86_64, bionic codebase):
```
$ ./configure --with-sanitizers=thread && make clean && make
$ TSAN_OPTIONS="suppressions=test/sanitizer_suppressions/tsan" ./src/test/test_bitcoin --run_test=wallet_tests/CreateWalletFromFile
Running 1 test case...
WARNING: too long mutex cycle found
WARNING: too long mutex cycle found
WARNING: too long mutex cycle found
WARNING: too long mutex cycle found
WARNING: too long mutex cycle found
==================
WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock) (pid=31793)
  Cycle in lock order graph: M134059 (0x7b6400004cc8) => M133094 (0x56527c390c60) => M134059

  Mutex M133094 acquired here while holding mutex M134059 in main thread:
    #0 pthread_mutex_lock <null> (libtsan.so.0+0x3fadb)
    #1 __gthread_mutex_lock /usr/include/x86_64-linux-gnu/c++/7/bits/gthr-default.h:748 (test_bitcoin+0xd17c11)
    #2 __gthread_recursive_mutex_lock /usr/include/x86_64-linux-gnu/c++/7/bits/gthr-default.h:810 (test_bitcoin+0xd17c11)
    #3 std::recursive_mutex::lock() /usr/include/c++/7/mutex:107 (test_bitcoin+0xd17c11)
    #4 std::unique_lock<std::recursive_mutex>::lock() /usr/include/c++/7/bits/std_mutex.h:267 (test_bitcoin+0xd17c11)
    #5 UniqueLock<AnnotatedMixin<std::recursive_mutex>, std::unique_lock<std::recursive_mutex> >::Enter(char const*, char const*, int) sync.h:131 (test_bitcoin+0xd17c11)
    #6 UniqueLock<AnnotatedMixin<std::recursive_mutex>, std::unique_lock<std::recursive_mutex> >::UniqueLock(AnnotatedMixin<std::recursive_mutex>&, char const*, char const*, int, bool) sync.h:152 (test_bitcoin+0xd17c11)
    #7 CWallet::CreateWalletFromFile(interfaces::Chain&, WalletLocation const&, bilingual_str&, std::vector<bilingual_str, std::allocator<bilingual_str> >&, unsigned long) wallet/wallet.cpp:4004 (test_bitcoin+0xd397de)
    #8 TestLoadWallet wallet/test/wallet_tests.cpp:37 (test_bitcoin+0x703587)
    #9 wallet_tests::CreateWalletFromFile::test_method() wallet/test/wallet_tests.cpp:782 (test_bitcoin+0x72013e)
    #10 CreateWalletFromFile_invoker wallet/test/wallet_tests.cpp:700 (test_bitcoin+0x721452)
    #11 boost::detail::function::void_function_invoker0<void (*)(), void>::invoke(boost::detail::function::function_buffer&) /usr/include/boost/function/function_template.hpp:118 (test_bitcoin+0xe05fb)
    #12 boost::detail::function::function_obj_invoker0<boost::detail::forward, int>::invoke(boost::detail::function::function_buffer&) <null> (libboost_unit_test_framework.so.1.65.1+0x4b2cd)
    #13 __libc_start_main <null> (libc.so.6+0x21b96)

    Hint: use TSAN_OPTIONS=second_deadlock_stack=1 to get more informative warning message

  Mutex M134059 acquired here while holding mutex M133094 in main thread:
    #0 pthread_mutex_lock <null> (libtsan.so.0+0x3fadb)
    #1 __gthread_mutex_lock /usr/include/x86_64-linux-gnu/c++/7/bits/gthr-default.h:748 (test_bitcoin+0x71e053)
    #2 __gthread_recursive_mutex_lock /usr/include/x86_64-linux-gnu/c++/7/bits/gthr-default.h:810 (test_bitcoin+0x71e053)
    #3 std::recursive_mutex::lock() /usr/include/c++/7/mutex:107 (test_bitcoin+0x71e053)
    #4 AnnotatedMixin<std::recursive_mutex>::lock() sync.h:90 (test_bitcoin+0x71e053)
    #5 operator() wallet/test/wallet_tests.cpp:780 (test_bitcoin+0x71e053)
    #6 _M_invoke /usr/include/c++/7/bits/std_function.h:316 (test_bitcoin+0x71e677)
    #7 std::function<void (std::unique_ptr<interfaces::Wallet, std::default_delete<interfaces::Wallet> >)>::operator()(std::unique_ptr<interfaces::Wallet, std::default_delete<interfaces::Wallet> >) const /usr/include/c++/7/bits/std_function.h:706 (test_bitcoin+0xd39850)
    #8 CWallet::CreateWalletFromFile(interfaces::Chain&, WalletLocation const&, bilingual_str&, std::vector<bilingual_str, std::allocator<bilingual_str> >&, unsigned long) wallet/wallet.cpp:4006 (test_bitcoin+0xd39850)
    #9 TestLoadWallet wallet/test/wallet_tests.cpp:37 (test_bitcoin+0x703587)
    #10 wallet_tests::CreateWalletFromFile::test_method() wallet/test/wallet_tests.cpp:782 (test_bitcoin+0x72013e)
    #11 CreateWalletFromFile_invoker wallet/test/wallet_tests.cpp:700 (test_bitcoin+0x721452)
    #12 boost::detail::function::void_function_invoker0<void (*)(), void>::invoke(boost::detail::function::function_buffer&) /usr/include/boost/function/function_template.hpp:118 (test_bitcoin+0xe05fb)
    #13 boost::detail::function::function_obj_invoker0<boost::detail::forward, int>::invoke(boost::detail::function::function_buffer&) <null> (libboost_unit_test_framework.so.1.65.1+0x4b2cd)
    #14 __libc_start_main <null> (libc.so.6+0x21b96)

SUMMARY: ThreadSanitizer: lock-order-inversion (potential deadlock) (/usr/lib/x86_64-linux-gnu/libtsan.so.0+0x3fadb) in __interceptor_pthread_mutex_lock
==================
WARNING: too long mutex cycle found
WARNING: too long mutex cycle found
WARNING: too long mutex cycle found
WARNING: too long mutex cycle found
WARNING: too long mutex cycle found
WARNING: too long mutex cycle found
WARNING: too long mutex cycle found
WARNING: too long mutex cycle found
WARNING: too long mutex cycle found
WARNING: too long mutex cycle found

*** No errors detected
ThreadSanitizer: reported 1 warnings
```

---

The ThreadSanitizer does not report this issue on CI as the binaries are built with the `--disable-wallet` option: https://github.com/bitcoin/bitcoin/blob/df303ceb650521dc7b1ba91e0eea383c387a5860/ci/test/00_setup_env_native_tsan.sh#L14

UPDATE: This issue is caused by #16426 that has been merged on 2020-05-01.
return nullptr;
    }
    LOCK(cs_wallet);
    auto new_spk_man = std::unique_ptr<DescriptorScriptPubKeyMan>(new DescriptorScriptPubKeyMan(*this, desc));
    
    auto old_spk_man = GetDescriptorScriptPubKeyMan(desc);
    if (old_spk_man) {
        WalletLogPrintf("Update existing descriptor: %s\n", desc.descriptor->ToString());
        {
            LOCK(old_spk_man->cs_desc_man);
            new_spk_man->SetCache(old_spk_man->GetWalletDescriptor().cache);
        }
        
        auto old_spk_man_id = old_spk_man->GetID();
        for (bool internal : {false, true}) {
            for (OutputType t : OUTPUT_TYPES) {
                auto active_spk_man = GetScriptPubKeyMan(t, internal);
                if (active_spk_man && active_spk_man->GetID() == old_spk_man_id) {
                    if (internal) {
                        m_internal_spk_managers.erase(t);
                    } else {
                        m_external_spk_managers.erase(t);
                    }
                    break;
                }
            }
        }
        m_spk_managers.erase(old_spk_man_id);
    }