|  | //===-- sanitizer_thread_arg_retval.cpp -------------------------*- C++ -*-===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // This file is shared between sanitizer tools. | 
|  | // | 
|  | // Tracks thread arguments and return value for leak checking. | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "sanitizer_thread_arg_retval.h" | 
|  |  | 
|  | #include "sanitizer_placement_new.h" | 
|  |  | 
|  | namespace __sanitizer { | 
|  |  | 
|  | void ThreadArgRetval::CreateLocked(uptr thread, bool detached, | 
|  | const Args& args) { | 
|  | CheckLocked(); | 
|  | Data& t = data_[thread]; | 
|  | t = {}; | 
|  | t.gen = gen_++; | 
|  | static_assert(sizeof(gen_) == sizeof(u32) && kInvalidGen == UINT32_MAX); | 
|  | if (gen_ == kInvalidGen) | 
|  | gen_ = 0; | 
|  | t.detached = detached; | 
|  | t.args = args; | 
|  | } | 
|  |  | 
|  | ThreadArgRetval::Args ThreadArgRetval::GetArgs(uptr thread) const { | 
|  | __sanitizer::Lock lock(&mtx_); | 
|  | auto t = data_.find(thread); | 
|  | CHECK(t); | 
|  | if (t->second.done) | 
|  | return {}; | 
|  | return t->second.args; | 
|  | } | 
|  |  | 
|  | void ThreadArgRetval::Finish(uptr thread, void* retval) { | 
|  | __sanitizer::Lock lock(&mtx_); | 
|  | auto t = data_.find(thread); | 
|  | if (!t) | 
|  | return; | 
|  | if (t->second.detached) { | 
|  | // Retval of detached thread connot be retrieved. | 
|  | data_.erase(t); | 
|  | return; | 
|  | } | 
|  | t->second.done = true; | 
|  | t->second.args.arg_retval = retval; | 
|  | } | 
|  |  | 
|  | u32 ThreadArgRetval::BeforeJoin(uptr thread) const { | 
|  | __sanitizer::Lock lock(&mtx_); | 
|  | auto t = data_.find(thread); | 
|  | if (t && !t->second.detached) { | 
|  | return t->second.gen; | 
|  | } | 
|  | if (!common_flags()->detect_invalid_join) | 
|  | return kInvalidGen; | 
|  | const char* reason = "unknown"; | 
|  | if (!t) { | 
|  | reason = "already joined"; | 
|  | } else if (t->second.detached) { | 
|  | reason = "detached"; | 
|  | } | 
|  | Report("ERROR: %s: Joining %s thread, aborting.\n", SanitizerToolName, | 
|  | reason); | 
|  | Die(); | 
|  | } | 
|  |  | 
|  | void ThreadArgRetval::AfterJoin(uptr thread, u32 gen) { | 
|  | __sanitizer::Lock lock(&mtx_); | 
|  | auto t = data_.find(thread); | 
|  | if (!t || gen != t->second.gen) { | 
|  | // Thread was reused and erased by any other event, or we had an invalid | 
|  | // join. | 
|  | return; | 
|  | } | 
|  | CHECK(!t->second.detached); | 
|  | data_.erase(t); | 
|  | } | 
|  |  | 
|  | void ThreadArgRetval::DetachLocked(uptr thread) { | 
|  | CheckLocked(); | 
|  | auto t = data_.find(thread); | 
|  | CHECK(t); | 
|  | CHECK(!t->second.detached); | 
|  | if (t->second.done) { | 
|  | // We can't retrive retval after detached thread finished. | 
|  | data_.erase(t); | 
|  | return; | 
|  | } | 
|  | t->second.detached = true; | 
|  | } | 
|  |  | 
|  | void ThreadArgRetval::GetAllPtrsLocked(InternalMmapVector<uptr>* ptrs) { | 
|  | CheckLocked(); | 
|  | CHECK(ptrs); | 
|  | data_.forEach([&](DenseMap<uptr, Data>::value_type& kv) -> bool { | 
|  | ptrs->push_back((uptr)kv.second.args.arg_retval); | 
|  | return true; | 
|  | }); | 
|  | } | 
|  |  | 
|  | }  // namespace __sanitizer |