From e406a45f0799955ecb192b947d71ffc137fe1c26 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Fri, 30 May 2025 20:11:13 -0500 Subject: [PATCH] feat(software): gui multithreading --- .../multi-threaded-implementation.webp | Bin 0 -> 29356 bytes .../single-threaded-design.webp | Bin 0 -> 30210 bytes .../posts/software/multithreading-a-gui.mdx | 131 ++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 public/posts/multithreading-a-gui/multi-threaded-implementation.webp create mode 100644 public/posts/multithreading-a-gui/single-threaded-design.webp create mode 100644 src/content/posts/software/multithreading-a-gui.mdx diff --git a/public/posts/multithreading-a-gui/multi-threaded-implementation.webp b/public/posts/multithreading-a-gui/multi-threaded-implementation.webp new file mode 100644 index 0000000000000000000000000000000000000000..696f30b2cb3a203145b0c92bbf414dcf52dff6f5 GIT binary patch literal 29356 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 XWKep(*Y>4er1BFnpF*w#1_%HEW3A+a literal 0 HcmV?d00001 diff --git a/public/posts/multithreading-a-gui/single-threaded-design.webp b/public/posts/multithreading-a-gui/single-threaded-design.webp new file mode 100644 index 0000000000000000000000000000000000000000..c0100297e49abf2c694a1d464184abc0827666fe GIT binary patch literal 30210 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 + + executor.add_node(node); + while (init_gui->is_running) { + executor.spin_once(timeout); + } +} +``` + +All of 30+ topic callbacks were registered on this thread: + +```cpp +GuiNode::GuiNode(basestation::Gui* init_gui) : Node("uva_gui") { + this->gui = init_gui; + + rclcpp::QoS best_effort_qos = rclcpp::QoS(rclcpp::QoSInitialization(RMW_QOS_POLICY_HISTORY_KEEP_LAST, 1)); + best_effort_qos.best_effort(); + + this->pubDesVel_ = create_publisher("vehicle/set_desired_velocity", 1); + ... + this->subAutonomy_ = create_subscription("/telemetry/autonomy", 1, std::bind(&GuiNode::receiveAutonomy, this, std::placeholders::_1)); +} +``` + +The Qt Framework is quite complex and beyond the scope of this post. Big picture, the GUI controls a bunch of data-independent visualizations that are handled on this GUI thread. + +How do we optimize this? It is necessary to take a step back and observe the structure of the entire application: + +![single-threaded-design](/public/posts/multithreading-a-gui/single-threaded-design.webp) + +Many flaws are now clear: + +- Single, high-frequency topics can flood the GUI, causing stale data for other Widgets +- Data races in which multiple callbacks modify shared data are especially difficult to handle +- Poor separation of responsibility: the `MainWindow` is responsible for too much—it should not need first-hand knowledge of every Widget's API + +Luckily, we're far from the first (and the last) to encounter a problem such as this. Enter ROS's exhaustive suite of concurrency tools. + +# multi-threaded architecture + +First, let's pin down what exactly can be parallelized. + +1. Subscriptions: group subscription callbacks[^1] according to the displayed widgets. For the CAR, this was three `Vehicle`, `Trajectory`, and `Perception` groups. Now, widgets can be updated as dependent data is received. +2. Visualizations: now that data is handled independently, GUI widgets can be updated (with care) as well. + +> Just kidding. Because of ROS, all gui widgets can only be updated on the main GUI thread. + +This lead me to the follow structure: + +![single-threaded-design](/public/posts/multithreading-a-gui/multi-threaded-implementation.webp) + +- Three callback groups are triggered at differing intervals according to their urgency on the GUI node + - A thread-safe queue[^2] processes all ingested data for each callback group +- Every 10ms, the GUI is updated, highest to lowest urgency messages first +- The `MainWindow` houses the visualization widgets as before—however, the GUI thread actually performs the update logic +- GUI Widgets were re-implemented to be thread-safe with basic locking, a small amount of overhead to ensure safe memory access + +# retrospective + +Looking back, this GUI should've been implemented with a modern web framework such as [React](https://react.dev/) with [react-ros](https://github.com/flynneva/react-ros?tab=readme-ov-file). CAR needs high-speed, reactive data, and QtC++ is simply not meant for this level of complexity. + +The lack of concurrent GUI updates was a major buzzkill, providing a limit to the ultimate amount I could parallelize this application. While it ran *much* faster than before, more sophisticated solutions such as batching and timestamping would likely improve accuracy and keep lower priority visualizations more up to date. However, I see this as a non-issue—the source of the problem is truly ROS's lack of support for concurrency. + +[^1]: See [the ROS documentation](https://docs.ros.org/en/foxy/How-To-Guides/Using-callback-groups.onhtml) to learn more. The CAR publishes various topic-related data at set rates, so I'm looking to run various groups of mutually exclusive callbacks at a set interval (i.e. `MutuallyExclusive`) +[^2]: The simplest implementation did the job: + + ```cpp + ... + template + class ThreadSafeQueue { + public: + void push(const T& item) { + std::lock_guard lock(mutex_); + queue_.push(item); + condition_.notify_one(); + } + + bool pop(T& item, std::chrono::milliseconds timeout = std::chrono::milliseconds(0)) { + std::unique_lock lock(mutex_); + if (timeout.count() > 0) { + if (!condition_.wait_for(lock, timeout, [this] { return !queue_.empty(); })) { + return false; + } + } else if (queue_.empty()) { + return false; + } + + item = queue_.front(); + queue_.pop(); + return true; + } + + bool empty() const { + std::lock_guard lock(mutex_); + return queue_.empty(); + } + + size_t size() const { + std::lock_guard lock(mutex_); + return queue_.size(); + } + + void clear() { + std::lock_guard lock(mutex_); + std::queue empty; + std::swap(queue_, empty); + } + ... + }; + ```