1

I am facing a very weird issue. My source directory contains the following:

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

0 directories, 3 files

Where the contents of stoppable_task.hpp is:

#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

and contents of main.cpp is:

#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;
}

I am using the following CMakeLists.txt file to compile.

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)

On compiling through CMake, the program throuws a SIGABRT error as shown below:

❯ ./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)

The solution from this page says to manually compile as follows:

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

Surprisingly, the manual compilation works and throws no error. Can anyone explain what is the issue with the first CMake method and how can I resolve that? Thanks in advance.

1 Answers1

2

While I don't have such an error on Fedora 33 x86_64 (cmake version 3.19.6, g++ (GCC) 10.2.1 20201125 (Red Hat 10.2.1-9))

But if I compile it as follows I'm able to reproduce your issue

╰─$ 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

So the difference as follows:

CMake doesn't provide -pthread flag on compilation (log with 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

To enforce CMake using -pthread compiler option you should add

set(THREADS_PREFER_PTHREAD_FLAG ON)

before

find_package(Threads REQUIRED)
Sergei Nikulov
  • 5,029
  • 23
  • 36