-
Notifications
You must be signed in to change notification settings - Fork 351
Add a mutex to protect ThreadPlanStack's from simultaneous access #3164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: swift/release/5.5
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -95,7 +95,9 @@ class ThreadPlanStack { | |
|
|
||
| void WillResume(); | ||
|
|
||
| bool IsTID(lldb::tid_t tid); | ||
| bool IsTID(lldb::tid_t tid) { | ||
| return GetTID() == tid; | ||
| } | ||
| lldb::tid_t GetTID(); | ||
| void SetTID(lldb::tid_t tid); | ||
|
|
||
|
|
@@ -116,6 +118,10 @@ class ThreadPlanStack { | |
| size_t m_completed_plan_checkpoint = 0; // Monotonically increasing token for | ||
| // completed plan checkpoints. | ||
| std::unordered_map<size_t, PlanStack> m_completed_plan_store; | ||
| mutable std::recursive_mutex m_stack_mutex; | ||
|
|
||
| // ThreadPlanStacks shouldn't be copied. | ||
| ThreadPlanStack(ThreadPlanStack &rhs) = delete; | ||
| }; | ||
|
|
||
| class ThreadPlanStackMap { | ||
|
|
@@ -129,15 +135,34 @@ class ThreadPlanStackMap { | |
|
|
||
| void AddThread(Thread &thread) { | ||
| lldb::tid_t tid = thread.GetID(); | ||
| m_plans_list.emplace(tid, thread); | ||
| std::shared_ptr<ThreadPlanStack> new_elem_sp(new ThreadPlanStack(thread)); | ||
| m_plans_sp_container.push_back(new_elem_sp); | ||
| m_plans_list.emplace(tid, new_elem_sp.get()); | ||
| } | ||
|
|
||
| bool RemoveTID(lldb::tid_t tid) { | ||
| auto result = m_plans_list.find(tid); | ||
| if (result == m_plans_list.end()) | ||
| return false; | ||
| result->second.ThreadDestroyed(nullptr); | ||
| ThreadPlanStack *removed_stack = result->second; | ||
| m_plans_list.erase(result); | ||
| // Also remove this from the stack storage: | ||
| PlansStore::iterator end | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this the same as |
||
| = m_plans_sp_container.end(); | ||
| PlansStore::iterator iter; | ||
| for (iter = m_plans_sp_container.begin(); | ||
| iter != end; iter++) { | ||
| if ((*iter)->IsTID(tid)) { | ||
| break; | ||
| } | ||
| } | ||
| if (iter == end) | ||
| return false; | ||
|
|
||
| // Finally, tell the stack its thread has been destroyed: | ||
| removed_stack->ThreadDestroyed(nullptr); | ||
| // And THEN remove it from the container so it goes away. | ||
| m_plans_sp_container.erase(iter); | ||
| return true; | ||
| } | ||
|
|
||
|
|
@@ -146,38 +171,49 @@ class ThreadPlanStackMap { | |
| if (result == m_plans_list.end()) | ||
| return nullptr; | ||
| else | ||
| return &result->second; | ||
| return result->second; | ||
| } | ||
|
|
||
| // rename to Reactivate? | ||
| void Activate(ThreadPlanStack &&stack) { | ||
| void Activate(ThreadPlanStack &stack) { | ||
| // Remove this from the detached plan list: | ||
| std::vector<ThreadPlanStack *>::iterator end = m_detached_plans.end(); | ||
|
|
||
| for (std::vector<ThreadPlanStack *>::iterator iter | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks like another candidtae for find_if or at least |
||
| = m_detached_plans.begin(); iter != end; iter++) { | ||
| if (*iter == &stack) { | ||
| m_detached_plans.erase(iter); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (m_plans_list.find(stack.GetTID()) == m_plans_list.end()) | ||
| m_plans_list.emplace(stack.GetTID(), std::move(stack)); | ||
| m_plans_list.emplace(stack.GetTID(), &stack); | ||
| else | ||
| m_plans_list.at(stack.GetTID()) = std::move(stack); | ||
| m_plans_list.at(stack.GetTID()) = &stack; | ||
| } | ||
|
|
||
| // rename to ...? | ||
| std::vector<ThreadPlanStack> CleanUp() { | ||
| void ScanForDetachedPlanStacks() { | ||
| llvm::SmallVector<lldb::tid_t, 2> invalidated_tids; | ||
| for (auto &pair : m_plans_list) | ||
| if (pair.second.GetTID() != pair.first) | ||
| if (pair.second->GetTID() != pair.first) | ||
| invalidated_tids.push_back(pair.first); | ||
|
|
||
| std::vector<ThreadPlanStack> detached_stacks; | ||
| detached_stacks.reserve(invalidated_tids.size()); | ||
| for (auto tid : invalidated_tids) { | ||
| auto it = m_plans_list.find(tid); | ||
| auto stack = std::move(it->second); | ||
| ThreadPlanStack *stack = it->second; | ||
| m_plans_list.erase(it); | ||
| detached_stacks.emplace_back(std::move(stack)); | ||
| m_detached_plans.push_back(stack); | ||
| } | ||
| return detached_stacks; | ||
| } | ||
|
|
||
| std::vector<ThreadPlanStack *> &GetDetachedPlanStacks() { | ||
| return m_detached_plans; | ||
| } | ||
|
|
||
| void Clear() { | ||
| for (auto &plan : m_plans_list) | ||
| plan.second.ThreadDestroyed(nullptr); | ||
| plan.second->ThreadDestroyed(nullptr); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this changed from a reference to a pointer, why do we not have to check for nullptr? |
||
| m_plans_list.clear(); | ||
| } | ||
|
|
||
|
|
@@ -194,7 +230,23 @@ class ThreadPlanStackMap { | |
|
|
||
| private: | ||
| Process &m_process; | ||
| using PlansList = std::unordered_map<lldb::tid_t, ThreadPlanStack>; | ||
| // We don't want to make copies of these ThreadPlanStacks, there needs to be | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you're using shared pointers, I'm not sure why we can't store them directly in the unordered map. The map would only ever be copying shared_pointers around, which won't touch the underlying object. |
||
| // just one of these tracking each piece of work. But we need to move the | ||
| // work from "attached to a TID" state to "detached" state, which is most | ||
| // conveniently done by having separate containers for the two states. | ||
| // To do that w/o having to spend effort fighting with C++'s desire to invoke | ||
| // copy constructors, we keep a store for the real object, and then keep | ||
| // pointers to the objects in the two containers. The store actually holds | ||
| // shared_ptrs to the objects, so that the pointers in the containers will stay | ||
| // valid. | ||
|
|
||
| // Use a shared pointer rather than unique_ptr here to prevent C++ from trying | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't really make sense, regardless of whether and object is stored in a shared or unique pointer, if you're moving the smart pointer around, it will copy/move the pointer, not the underlying object. Do you mean here that ThreadPlanStack itself should be copyable? |
||
| // to use a copy constructor on the stored type. | ||
| using PlansStore = std::vector<std::shared_ptr<ThreadPlanStack>>; | ||
| PlansStore m_plans_sp_container; | ||
| std::vector<ThreadPlanStack *> m_detached_plans; | ||
|
|
||
| using PlansList = std::unordered_map<lldb::tid_t, ThreadPlanStack *>; | ||
| PlansList m_plans_list; | ||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1453,25 +1453,23 @@ void Process::UpdateThreadListIfNeeded() { | |
| } | ||
|
|
||
| void Process::SynchronizeThreadPlans() { | ||
| for (auto &stack : m_thread_plans.CleanUp()) | ||
| m_async_thread_plans.emplace_back(std::move(stack)); | ||
| m_thread_plans.ScanForDetachedPlanStacks(); | ||
| } | ||
|
|
||
| ThreadPlanSP Process::FindDetachedPlanExplainingStop(Thread &thread, | ||
| Event *event_ptr) { | ||
| auto end = m_async_thread_plans.end(); | ||
| for (auto it = m_async_thread_plans.begin(); it != end; ++it) { | ||
| auto plan_sp = it->GetCurrentPlan(); | ||
| std::vector<ThreadPlanStack *> &detached_plans | ||
| = m_thread_plans.GetDetachedPlanStacks(); | ||
| size_t num_detached_plans = detached_plans.size(); | ||
| for (size_t idx = 0; idx < num_detached_plans; idx++) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not |
||
| ThreadPlanStack *cur_stack = detached_plans[idx]; | ||
| ThreadPlanSP plan_sp = cur_stack->GetCurrentPlan(); | ||
| plan_sp->SetTID(thread.GetID()); | ||
| if (!plan_sp->DoPlanExplainsStop(event_ptr)) { | ||
| plan_sp->ClearTID(); | ||
| continue; | ||
| } | ||
|
|
||
| auto stack = std::move(*it); | ||
| m_async_thread_plans.erase(it); | ||
| stack.SetTID(plan_sp->GetTID()); | ||
| m_thread_plans.Activate(std::move(stack)); | ||
| m_thread_plans.Activate(*cur_stack); | ||
| return plan_sp; | ||
| } | ||
| return {}; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
auto new_elem_sp = std::make_shared(thread);