Я не совсем уверен, как лучше всего назвать этот вопрос, поскольку я не совсем уверен, какова на самом деле природа проблемы (я думаю, «как исправить segfault» - не лучший заголовок).
Ситуация такова, что я написал этот код:
template <typename T> class LatchedSubscriber {
private:
ros::Subscriber sub;
std::shared_ptr<T> last_received_msg;
std::shared_ptr<std::mutex> mutex;
int test;
void callback(T msg) {
std::shared_ptr<std::mutex> thread_local_mutex = mutex;
std::shared_ptr<T> thread_local_msg = last_received_msg;
if (!thread_local_mutex) {
ROS_INFO("Mutex pointer is null in callback");
}
if (!thread_local_msg) {
ROS_INFO("lrm: pointer is null in callback");
}
ROS_INFO("Test is %d", test);
std::lock_guard<std::mutex> guard(*thread_local_mutex);
*thread_local_msg = msg;
}
public:
LatchedSubscriber() {
last_received_msg = std::make_shared<T>();
mutex = std::make_shared<std::mutex>();
test = 42;
if (!mutex) {
ROS_INFO("Mutex pointer is null in constructor");
}
else {
ROS_INFO("Mutex pointer is not null in constructor");
}
}
void start(ros::NodeHandle &nh, const std::string &topic) {
sub = nh.subscribe(topic, 1000, &LatchedSubscriber<T>::callback, this);
}
T get_last_msg() {
std::lock_guard<std::mutex> guard(*mutex);
return *last_received_msg;
}
};
По сути, он подписывается на topi c (канал), что означает, что функция обратного вызова вызывается каждый раз, когда приходит сообщение. Задача этого класса - сохранить последнее полученное сообщение, чтобы пользователь класса всегда мог получить к нему доступ.
В конструкторе я выделяю shared_ptr для сообщения и для мьютекса для синхронизации доступа к этому сообщению. Причина использования кучи памяти здесь в том, что LatchedSubscriber
может быть скопировано, и то же зафиксированное сообщение все еще может быть прочитано. (Subscriber
уже реализует такое поведение, при котором его копирование ничего не делает, кроме того факта, что обратный вызов перестает вызываться, как только последний экземпляр выходит за пределы области действия).
Проблема в основном в том, что ошибка кода. Я почти уверен, что причина этого в том, что мои общие указатели становятся null
в функции обратного вызова, несмотря на то, что они не равны нулю в конструкторе.
ROS_INFO
вызывает print:
Mutex pointer is not null in constructor
Mutex pointer is null in callback
lrm: pointer is null in callback
Test is 42
Я не понимаю, как такое может случиться. Полагаю, я что-то неправильно понял в отношении общих указателей, подписок ros topi c или обоих.
Что я сделал:
- Сначала у меня был вызов подписки, происходивший в конструктор. Я думаю, что указывать указатель
this
на другой поток до того, как конструктор вернется, может быть плохим, поэтому я переместил его в функцию start
, которая вызывается после создания объекта. - Есть много аспектов к потокобезопасности
shared_ptr
s кажется. Сначала я использовал mutex
и last_received_msg
непосредственно в обратном вызове. Теперь я скопировал их в локальные переменные, надеясь, что это поможет. Но, похоже, это не имеет значения. - Я добавил локальную целочисленную переменную. Я могу прочитать целое число, присвоенное этой переменной в конструкторе, из обратного вызова. Просто проверка работоспособности, чтобы убедиться, что обратный вызов действительно вызывается для экземпляра, созданного моим конструктором.