SIGABRT:CMake 与手动 g++ 编译

问题描述

我面临一个非常奇怪的问题。我的源目录包含以下内容

❯ tree .
├── CMakeLists.txt
├── main.cpp
└── stoppable_task.hpp

0 directories,3 files

stoppable_task.hpp内容在哪里:

#ifndef STOPPABLE_TASK_HPP
#define STOPPABLE_TASK_HPP

#include <future>

enum class TaskState : unsigned char {
    Ready = 0,Executing = 1,Terminating = 2
};

class StoppableTask {
    std::promise<void>  exit_signal;
    std::future<void>   future_obj;

protected:
    TaskState           task_state;
    virtual void run() = 0;

public:

    StoppableTask() 
    :   future_obj(exit_signal.get_future()),task_state(TaskState::Ready) {}

    StoppableTask(const StoppableTask&) = delete;
    
    StoppableTask& operator=(const StoppableTask&) = delete;

    virtual void operator()() {
        task_state = TaskState::Executing;
        run();
    }

    bool is_stop_requested(int timeout_in_ms = 100) const {
        std::chrono::milliseconds t(timeout_in_ms);
        return (future_obj.wait_for(t) == std::future_status::ready);
    }

    void request_stop() {
        task_state = TaskState::Terminating;
        exit_signal.set_value();
    }

    TaskState get_task_state() {
        return task_state;
    }
};

#endif

main.cpp内容是:

#include "stoppable_task.hpp"
#include <iostream>
#include <memory>

class Speak : public StoppableTask {
    int count;
public:
    Speak() : count(0),StoppableTask() {}

    Speak(const Speak&) = delete;
    Speak& operator=(const Speak&) = delete;

    void run() override {
        std::cout << ++count << " -- speaking ... " << std::endl;
    }
};

int main() {
    int i = 10;

    std::unique_ptr<Speak> spk = std::make_unique<Speak>();

    do {
        (*spk)();
        if (--i <= 0) {
            spk->request_stop();
        }
    } while(not spk->is_stop_requested());

    return 0;
}

我正在使用以下 CMakeLists.txt 文件进行编译。

cmake_minimum_required(VERSION 3.16)
project(stoppable_task)

set(CMAKE_CXX_STANDARD 17)

find_package(Threads required)

set(SRC_FILES
    main.cpp
)

set(HDR_FILES
    stoppable_task.hpp
)

add_executable(stoppable_task ${SRC_FILES} ${HDR_FILES})

target_link_libraries(stoppable_task Threads::Threads)

在通过 CMake 编译时,程序抛出 SIGABRT 错误,如下所示:

❯ ./stoppable_task
1 -- speaking ...
2 -- speaking ...
3 -- speaking ...
4 -- speaking ...
5 -- speaking ...
6 -- speaking ...
7 -- speaking ...
8 -- speaking ...
9 -- speaking ...
10 -- speaking ...
terminate called after throwing an instance of 'std::system_error'
  what():  UnkNown error -1
fish: “./stoppable_task” terminated by signal SIGABRT (Abort)

this page解决方案说手动编译如下:

❯ g++ -std=c++17 main.cpp -pthread

令人惊讶的是,手动编译有效并且没有抛出错误。任何人都可以解释第一个 CMake 方法的问题是什么,我该如何解决?提前致谢。

解决方法

虽然我在 Fedora 33 x86_64 (cmake version 3.19.6,g++ (GCC) 10.2.1 20201125 (Red Hat 10.2.1-9)) 上没有这样的错误

但是如果我按如下方式编译它,我就可以重现您的问题

╰─$ g++ -std=c++17 main.cpp -o main         
╭─snikulov@snikulov ~/work/so/stopable 
╰─$ ./main 
1 -- speaking ... 
2 -- speaking ... 
3 -- speaking ... 
4 -- speaking ... 
5 -- speaking ... 
6 -- speaking ... 
7 -- speaking ... 
8 -- speaking ... 
9 -- speaking ... 
10 -- speaking ... 
terminate called after throwing an instance of 'std::system_error'
  what():  Unknown error -1
[1]    27092 IOT instruction (core dumped)  ./main

所以区别如下:

CMake 在编译时不提供 -pthread 标志(日志为 VERBOSE=1):

[ 50%] Building CXX object CMakeFiles/stoppable_task.dir/main.cpp.o
/usr/lib64/ccache/c++   -fsanitize=thread -std=c++17 -o CMakeFiles/stoppable_task.dir/main.cpp.o -c /home/snikulov/work/so/stopable/main.cpp
[100%] Linking CXX executable stoppable_task
/var/lib/snapd/snap/cmake/805/bin/cmake -E cmake_link_script CMakeFiles/stoppable_task.dir/link.txt --verbose=1
/usr/lib64/ccache/c++ -fsanitize=thread CMakeFiles/stoppable_task.dir/main.cpp.o -o stoppable_task  -lpthread 
gmake[2]: Leaving directory '/home/snikulov/work/so/stopable/build'
[100%] Built target stoppable_task

要使用 -pthread 编译器选项强制执行 CMake,您应该添加

set(THREADS_PREFER_PTHREAD_FLAG ON)

之前

find_package(Threads REQUIRED)