History

Fix race condition between scheduler and debugger during all thread suspension.

arr committed 9 years ago

Commit bd141a71 parent a6da1dc5 files · +20 −33Browse files

Unified · Split

5 files
  1. src/include/runtime.h +3 −3
  2. src/vm/scheduler.cpp +5 −17
  3. src/vm/scheduler.h +2 −2
  4. src/vm/tool_dispatch.cpp +9 −10
  5. src/vm/tool_dispatch.h +1 −1
src/include/runtime.h+3−3
@@ -827,11 +827,11 @@ namespace disvm · 826 unmodified lines
827827 // There is no indication if this function failed or succeeded.
828828 virtual void enqueue_blocked_thread(uint32_t thread_id) = 0;
829829
830+ // Gets the number of system threads the scheduler is utilizing
831+ virtual std::size_t get_system_thread_count() const = 0;
832+
830833 // Get all VM threads
831834 virtual std::vector<std::shared_ptr<const vm_thread_t>> get_all_threads() const = 0;
832−
833− // Get runnable VM threads
834− virtual std::vector<std::shared_ptr<const vm_thread_t>> get_runnable_threads() const = 0;
835835 };
836836
837837 // VM thread scheduler interface
src/vm/scheduler.cpp+5−17
@@ -259,31 +259,19 @@ void default_scheduler_t::enqueue_blocked_thread(uint32_t thread_id) · 258 unmodified lines
259259 }
260260 }
261261
262−std::vector<std::shared_ptr<const vm_thread_t>> default_scheduler_t::get_all_threads() const
262+std::size_t default_scheduler_t::get_system_thread_count() const
263263 {
264− auto result = std::vector<std::shared_ptr<const vm_thread_t>>{};
265−
266− {
267− std::lock_guard<std::mutex> lock{ _vm_threads_lock };
268− for (auto &p : _all_vm_threads)
269− result.push_back(p.second->vm_thread);
270− }
271−
272− return result;
264+ return _worker_thread_count;
273265 }
274266
275−std::vector<std::shared_ptr<const vm_thread_t>> default_scheduler_t::get_runnable_threads() const
267+std::vector<std::shared_ptr<const vm_thread_t>> default_scheduler_t::get_all_threads() const
276268 {
277269 auto result = std::vector<std::shared_ptr<const vm_thread_t>>{};
278270
279271 {
280272 std::lock_guard<std::mutex> lock{ _vm_threads_lock };
281− for (auto id : _runnable_vm_thread_ids)
282− {
283− auto iter = _all_vm_threads.find(id);
284− assert(iter != _all_vm_threads.cend());
285− result.push_back(iter->second->vm_thread);
286− }
273+ for (auto &p : _all_vm_threads)
274+ result.push_back(p.second->vm_thread);
287275 }
288276
289277 return result;
src/vm/scheduler.h+2−2
@@ -45,9 +45,9 @@ namespace disvm · 44 unmodified lines
4545 public: // vm_scheduler_control_t
4646 void enqueue_blocked_thread(uint32_t thread_id) override;
4747
48− std::vector<std::shared_ptr<const vm_thread_t>> get_all_threads() const override;
48+ std::size_t get_system_thread_count() const override;
4949
50− std::vector<std::shared_ptr<const vm_thread_t>> get_runnable_threads() const override;
50+ std::vector<std::shared_ptr<const vm_thread_t>> get_all_threads() const override;
5151
5252 private:
5353 struct thread_instance_t final
src/vm/tool_dispatch.cpp+9−10
@@ -332,23 +332,22 @@ void vm_tool_dispatch_t::suspend_all_threads() · 331 unmodified lines
332332
333333 const auto &sched = _vm.get_scheduler_control();
334334
335− auto current = _threads_suspended_count.load();
336− auto max = static_cast<uint32_t>(sched.get_runnable_threads().size());
335+ const auto sys_thread_count = static_cast<int32_t>(sched.get_system_thread_count());
336+ assert(sys_thread_count != 0);
337337
338− // Sleep and check until we determine all threads have been suspended
339− // Minus 1 to exclude the current thread.
340− while (max != 0 && current < (max - 1))
338+ // Sleep and check until we account for all system threads associated with the VM.
339+ auto suspended = _threads_suspended_count.load();
340+ while ((suspended + 1) < sys_thread_count)
341341 {
342342 std::this_thread::sleep_for(std::chrono::milliseconds{ 20 });
343− current = _threads_suspended_count.load();
344− max = static_cast<uint32_t>(sched.get_runnable_threads().size());
343+ suspended = _threads_suspended_count.load();
345344 }
346345
347− // If the current count is ever equal to the max, then a non vm thread is
348− // inside the tool dispatcher which may be okay, but it indicates a type of
346+ // If the suspended count is ever equal to the system thread count, then a non-vm thread
347+ // is inside the tool dispatcher which may be okay, but it indicates a type of
349348 // non-invasive debugging that was not originally incorporated into this
350349 // dispatcher design.
351− assert(max == 0 || current < max);
350+ assert(suspended < sys_thread_count);
352351 }
353352
354353 void vm_tool_dispatch_t::resume_all_threads()
src/vm/tool_dispatch.h+1−1
@@ -82,7 +82,7 @@ namespace disvm · 81 unmodified lines
8282 vm_t &_vm;
8383
8484 std::atomic_bool _threads_suspended;
85− std::atomic<uint32_t> _threads_suspended_count;
85+ std::atomic<int32_t> _threads_suspended_count;
8686 std::mutex _threads_suspend_lock;
8787 std::condition_variable _threads_resume;
8888 std::mutex _threads_resume_lock;