Class Destructor call when using multithreading on socketcan

37 views Asked by At

I am trying to understand why the class destructor is being called right at the beginning after the program starts !?

Here is my program snippet:

#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <mutex>
#include <atomic>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <linux/can.h>
#include <linux/can/raw.h>

class Canloop {
private:
    int socketFileDescriptor;
    struct sockaddr_can socketAdressConfig;
    struct ifreq interfaceRequestConfig;
    struct can_frame canFrameRx;
    struct can_frame canFrameTx;
    int sendCounter{0};
    std::thread receive_;
    std::thread transmit_;
    std::mutex readWriteMutex;
    std::atomic<bool> running{true};

    void receiveThread();
    void transmitThread();

public:
    Canloop(std::string &canInterface);
    void setCanData(can_frame &);
    void start();
    void stop();
    ~Canloop();
};

Canloop::Canloop(std::string &canInterface) {
    if ((socketFileDescriptor = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
        perror("Socket");
    }

    strcpy(interfaceRequestConfig.ifr_name, canInterface.c_str());
    ioctl(socketFileDescriptor, SIOCGIFINDEX, &interfaceRequestConfig);

    memset(&socketAdressConfig, 0, sizeof(socketAdressConfig));
    socketAdressConfig.can_family = AF_CAN;
    socketAdressConfig.can_ifindex = interfaceRequestConfig.ifr_ifindex;

    if (bind(socketFileDescriptor, (struct sockaddr *)&socketAdressConfig, sizeof(socketAdressConfig)) < 0) {
        perror("Bind");
    }

    int flags = fcntl(socketFileDescriptor, F_GETFL, 0);
    if (flags == -1) {
        perror("fcntl non blocking set error");
    }

    flags != O_NONBLOCK;
    if (fcntl(socketFileDescriptor, F_SETFL, flags) == -1) {
        perror("fcntl");
    }

    int bufferSize = 2;
    if (setsockopt(socketFileDescriptor, SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize))) {
        perror("setsocketopt");
    }
    if (setsockopt(socketFileDescriptor, SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize))) {
        perror("setsocketopt");
    }

    struct timeval timeout;

    timeout.tv_sec = 0;
    timeout.tv_usec = 1000;

    if (setsockopt(socketFileDescriptor, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout))) {
        perror("time set error");
    }

    if (setsockopt(socketFileDescriptor, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout))) {
        perror("time set error");
    }
}

void Canloop::start() {
    receive_ = std::thread([=] { receiveThread(); });
    transmit_ = std::thread([=] { transmitThread(); });
}

void Canloop::setCanData(can_frame &data) {
    this->canFrameTx = data;
}

void Canloop::stop() {
    if (close(socketFileDescriptor) < 0) {
        perror("Close");
    }

    running = false;
    if (receive_.joinable()) {
        receive_.join();
    }

    if (transmit_.joinable()) {
        transmit_.join();
    }
}

void Canloop::receiveThread() {
    std::cout << "Starting Rx_ " << std::endl;

    while (running) {
        ssize_t bytesRead;
        {
            // Mutex to guard the socket for Read access
            std::lock_guard<std::mutex> lock(readWriteMutex);
            bytesRead = read(socketFileDescriptor, &canFrameRx, sizeof(struct can_frame));
        }
        if (running) {
            if (bytesRead == -1) {
                if (errno == EAGAIN || errno == EWOULDBLOCK) {
                    // Handle non-blocking read
                } else {
                    perror("read error");
                }
            } else if (bytesRead == sizeof(struct can_frame)) {
                // Print data
                printf("0x%03X [%d] ", canFrameRx.can_id, canFrameRx.can_dlc);
                for (int i = 0; i < canFrameRx.can_dlc; i++)
                    printf("%02X ", canFrameRx.data[i]);
                printf("\r\n");
            } else {
                std::cout << "Incomplete data" << std::endl;
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
}

void Canloop::transmitThread() {
    std::cout << "Starting Tx_" << std::endl;

    while (running) {
        ssize_t nbytes ;
        {
            // Lock for write
            std::lock_guard<std::mutex> lock(readWriteMutex);
            nbytes = write(socketFileDescriptor, &canFrameTx, sizeof(struct can_frame));
        }
            if (running && nbytes == -1) {
                if (errno == EAGAIN || errno == EWOULDBLOCK) {
                    std::cout << "Would block" << std::endl;
                } else if (errno == ENOBUFS) {
                    perror("Buffer fail: ");
                } else {
                    perror("Write fail");
                }
            }
        
        std::cout << "Data written " << sendCounter++ << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

Canloop::~Canloop() {

    std::cout << "Destructor called" << std::endl;
    if (transmit_.joinable()) {
            transmit_.join();
    }

    if(receive_.joinable()) {
        receive_.join();
    }

    
}

int main() {
    std::string canInterface("can0");
    Canloop cantester(canInterface);

    can_frame localCanData;
    char hello[5] = "Hell";
    localCanData.can_id = 0x123;
    localCanData.can_dlc = sizeof(hello) / sizeof(hello[0]);
    sprintf((char *)localCanData.data, hello);
    cantester.setCanData(localCanData);
    cantester.start();
    return 0;
}

As i never shutdown the program, neither destroy the class explicitly, the destructor should not be invoked. Yet i see the following output.

Starting Rx_
Starting Tx_
Destructor called
Data written 0
Data written 1
Data written 2
Data written 3
Data written 4
Data written 5
Data written 6
Data written 7
Data written 8
Data written 9

Why is it being called ? I do not expect an invoke !?

0

There are 0 answers