Linux에서 POSIX 타이머 시스템 용 C++ "래퍼"를 작성하려고 시도하고 있으므로 C++ 프로그램이 시스템 시계를 사용하지 않고 (예 : 네트워크를 통해 메시지가 도착할 때까지 기다리는 것과 같은) 시간 제한을 설정할 수 있습니다. POSIX의 못생긴 C 인터페이스를 다루고있다. 대부분의 경우 작동하는 것으로 보이지만 때로는 몇 분이 지나면 내 프로그램이 세그 폴트됩니다. 문제는 내 LinuxTimerManager 개체 (또는 해당 멤버 개체 중 하나)가 메모리가 손상된 것으로 보이지만 불행히도 Valgrind에서 프로그램을 실행하면 문제가 나타나지 않으므로 내 코드를 쳐다 보려고 노력하고 있습니다. 그게 잘못된거야.POSIX 타이머가 C++ STL 개체를 안전하게 수정할 수 있습니까?
LinuxTimerManager.h :
namespace util {
using timer_id_t = int;
class LinuxTimerManager {
private:
timer_id_t next_id;
std::map<timer_id_t, timer_t> timer_handles;
std::map<timer_id_t, std::function<void(void)>> timer_callbacks;
std::set<timer_id_t> cancelled_timers;
friend void timer_signal_handler(int signum, siginfo_t* info, void* ucontext);
public:
LinuxTimerManager();
timer_id_t register_timer(const int delay_ms, std::function<void(void)> callback);
void cancel_timer(const timer_id_t timer_id);
};
void timer_signal_handler(int signum, siginfo_t* info, void* ucontext);
}
LinuxTimerManager.cpp : 여기
내 타이머 래퍼 구현의 핵심이다namespace util {
LinuxTimerManager* tm_instance;
LinuxTimerManager::LinuxTimerManager() : next_id(0) {
tm_instance = this;
struct sigaction sa = {0};
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timer_signal_handler;
sigemptyset(&sa.sa_mask);
int success_flag = sigaction(SIGRTMIN, &sa, NULL);
assert(success_flag == 0);
}
void timer_signal_handler(int signum, siginfo_t* info, void* ucontext) {
timer_id_t timer_id = info->si_value.sival_int;
auto cancelled_location = tm_instance->cancelled_timers.find(timer_id);
//Only fire the callback if the timer is not in the cancelled set
if(cancelled_location == tm_instance->cancelled_timers.end()) {
tm_instance->timer_callbacks.at(timer_id)();
} else {
tm_instance->cancelled_timers.erase(cancelled_location);
}
tm_instance->timer_callbacks.erase(timer_id);
timer_delete(tm_instance->timer_handles.at(timer_id));
tm_instance->timer_handles.erase(timer_id);
}
timer_id_t LinuxTimerManager::register_timer(const int delay_ms, std::function<void(void)> callback) {
struct sigevent timer_event = {0};
timer_event.sigev_notify = SIGEV_SIGNAL;
timer_event.sigev_signo = SIGRTMIN;
timer_event.sigev_value.sival_int = next_id;
timer_t timer_handle;
int success_flag = timer_create(CLOCK_REALTIME, &timer_event, &timer_handle);
assert(success_flag == 0);
timer_handles[next_id] = timer_handle;
timer_callbacks[next_id] = callback;
struct itimerspec timer_spec = {0};
timer_spec.it_interval.tv_sec = 0;
timer_spec.it_interval.tv_nsec = 0;
timer_spec.it_value.tv_sec = 0;
timer_spec.it_value.tv_nsec = delay_ms * 1000000;
timer_settime(timer_handle, 0, &timer_spec, NULL);
return next_id++;
}
void LinuxTimerManager::cancel_timer(const timer_id_t timer_id) {
if(timer_handles.find(timer_id) != timer_handles.end()) {
cancelled_timers.emplace(timer_id);
}
}
}
때 내 프로그램이 충돌의 segfault는 항상 timer_signal_handler()
에서 비롯되며 일반적으로 줄은입니다.또는 tm_instance->timer_handles.erase(timer_id)
. 실제 segfault는 std::map
구현 (예 : stl_tree.h
)의 깊은 곳에서 발생합니다.
동일한 LinuxTimerManager를 수정하는 다른 타이머 신호 사이의 경쟁 조건으로 인해 메모리가 손상 될 수 있습니까? 한 번에 하나의 타이머 신호 만 전달되었다고 생각했지만 어쩌면 내가 맨 페이지를 오해했을 수도 있습니다. 리눅스 시그널 핸들러가 std::map
과 같은 복잡한 C++ 객체를 수정하는 것은 일반적으로 안전하지 않습니까?
신호 처리기에서 안전하게 호출 할 수있는 것은 [async-signal-safe functions] (https://stackoverflow.com/questions/8493095/what-constitutes-asynchronous-safeness)뿐입니다. 신호 처리기가 액세스하는 객체가 반드시 일관된 상태가 아닐 수도 있습니다. –
신호 처리기는 async-signal-safe 함수 만 호출 할 수 있으며 함수가 명시 적으로 나열되지 않으면 (특히 메모리 할당을 수행하는 경우) STL 멤버 함수가 그렇지 않은 경우에는 비동기 신호 안전 함수가 아닌 것으로 간주해야합니다 기능. 할 수있는 일은 휘발성 플래그를 설정하고 나중에 일반 프로그램 컨텍스트에서 확인하고 거기에서 작업을 실행하는 것입니다. – PSkocik