Skip to content

Commit c39cc45

Browse files
committed
fix: prevent segfault when using MuJoCo viewer Reload button
Two race conditions crash the simulator on Reload: 1. PhysicsLoop calls mj_deleteData/mj_deleteModel while the bridge threads are still reading from those pointers. Fix: add two std::atomic<bool> globals (g_bridge_pause_request / g_bridge_paused) as a pause/resume handshake. PhysicsLoop waits for the bridge to stop before freeing memory. UnitreeSdk2BridgeThread destroys and recreates the bridge on each reload instead of holding stale pointers forever. 2. RecurrentThread::~RecurrentThread() destroys mFunc (the run() lambda) before sending pthread_cancel, and never sets mQuit. If the thread wakes between those two steps it calls the destroyed std::function and hits std::terminate(). Fix: replace RecurrentThread with a std::thread owned by RobotBridge, controlled by a std::atomic<bool> stop_flag_. pre_destroy() sets the flag and joins the thread — guaranteed clean exit before any destructor runs.
1 parent c598f10 commit c39cc45

2 files changed

Lines changed: 58 additions & 17 deletions

File tree

simulate/src/main.cc

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ namespace
102102

103103
// control noise variables
104104
mjtNum *ctrlnoise = nullptr;
105+
// Pause/resume handshake for safe model reload: PhysicsLoop signals the
106+
// bridge to stop before freeing m/d, bridge confirms before Physics proceeds.
107+
std::atomic<bool> g_bridge_pause_request{false};
108+
std::atomic<bool> g_bridge_paused{false};
109+
105110

106111
using Seconds = std::chrono::duration<double>;
107112

@@ -350,13 +355,19 @@ namespace
350355
{
351356
sim.Load(mnew, dnew, sim.dropfilename);
352357

358+
g_bridge_pause_request.store(true);
359+
while (!g_bridge_paused.load())
360+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
361+
353362
mj_deleteData(d);
354363
mj_deleteModel(m);
355364

356365
m = mnew;
357366
d = dnew;
358367
mj_forward(m, d);
359368

369+
g_bridge_pause_request.store(false);
370+
360371
// allocate ctrlnoise
361372
free(ctrlnoise);
362373
ctrlnoise = (mjtNum *)malloc(sizeof(mjtNum) * m->nu);
@@ -587,23 +598,32 @@ void *UnitreeSdk2BridgeThread(void *arg)
587598
unitree::robot::ChannelFactory::Instance()->Init(param::config.domain_id, param::config.interface);
588599

589600

590-
int body_id = mj_name2id(m, mjOBJ_BODY, "torso_link");
591-
if (body_id < 0) {
592-
body_id = mj_name2id(m, mjOBJ_BODY, "base_link");
593-
}
594-
param::config.band_attached_link = 6 * body_id;
595-
596-
std::unique_ptr<UnitreeSDK2BridgeBase> interface = nullptr;
597-
if (m->nu > NUM_MOTOR_IDL_GO) {
598-
interface = std::make_unique<G1Bridge>(m, d);
599-
} else {
600-
interface = std::make_unique<Go2Bridge>(m, d);
601-
}
602-
interface->start();
603-
604601
while (true)
605602
{
606-
sleep(1);
603+
int body_id = mj_name2id(m, mjOBJ_BODY, "torso_link");
604+
if (body_id < 0) {
605+
body_id = mj_name2id(m, mjOBJ_BODY, "base_link");
606+
}
607+
param::config.band_attached_link = 6 * body_id;
608+
609+
std::unique_ptr<UnitreeSDK2BridgeBase> interface = nullptr;
610+
if (m->nu > NUM_MOTOR_IDL_GO) {
611+
interface = std::make_unique<G1Bridge>(m, d);
612+
} else {
613+
interface = std::make_unique<Go2Bridge>(m, d);
614+
}
615+
interface->start();
616+
617+
while (!g_bridge_pause_request.load())
618+
std::this_thread::sleep_for(std::chrono::milliseconds(10));
619+
620+
interface->pre_destroy();
621+
interface.reset();
622+
g_bridge_paused.store(true);
623+
624+
while (g_bridge_pause_request.load())
625+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
626+
g_bridge_paused.store(false);
607627
}
608628
}
609629
//------------------------------------------ main --------------------------------------------------

simulate/src/unitree_sdk2_bridge.h

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
#include <unitree/idl/hg/IMUState_.hpp>
1111

1212
#include <iostream>
13+
#include <atomic>
14+
#include <chrono>
15+
#include <thread>
1316

1417
#include "param.h"
1518
#include "physics_joystick.h"
@@ -40,6 +43,7 @@ class UnitreeSDK2BridgeBase
4043
}
4144

4245
virtual void start() {}
46+
virtual void pre_destroy() {}
4347

4448
void printSceneInformation()
4549
{
@@ -165,10 +169,27 @@ using WirelessController_t = unitree::robot::go2::publisher::WirelessController;
165169
wireless_controller->joystick = joystick;
166170
}
167171

172+
~RobotBridge()
173+
{
174+
pre_destroy();
175+
}
176+
168177
void start()
169178
{
170-
thread_ = std::make_shared<unitree::common::RecurrentThread>(
171-
"unitree_bridge", UT_CPU_ID_NONE, 1000, [this]() { this->run(); });
179+
stop_flag_.store(false);
180+
run_thread_ = std::thread([this]() {
181+
while (!stop_flag_.load()) {
182+
this->run();
183+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
184+
}
185+
});
186+
}
187+
188+
void pre_destroy() override
189+
{
190+
stop_flag_.store(true);
191+
if (run_thread_.joinable())
192+
run_thread_.join();
172193
}
173194

174195
virtual void run()

0 commit comments

Comments
 (0)