Harry
Harry

Reputation: 2969

process shared pthread_cond_t doesn't work in gcc 8.4 but works in gcc 4.8.5

Recently I upgraded my OS from RHEL 7.6(gcc 4.8.5) to RHEL 8.4(gcc 8.4) and I'm facing issues related to process synchronization using pthread_mutex_t and pthread_cond_t. The reason I'm not using C++ std::mutex and std::condition_variable is because they doesn't support synchronization between processes. This used to work well in gcc 4.8.5 but not in gcc 8.4. This is my code

Binary_Semaphore.h

#ifndef BINARY_SEMAPHORE_H
#define BINARY_SEMAPHORE_H

#include <iostream>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>

struct binary_semaphore_attr {
    pthread_mutex_t mutex;
    pthread_cond_t cvar;
    bool flag;
};

class Binary_Semaphore {

    struct binary_semaphore_attr *bin_sem_attr;
    const std::string bin_sem_attr_shm_ID;

    const bool is_process_shared;
    const bool is_to_be_created;

public:
    Binary_Semaphore(const std::string& bin_sem_attr_shm_ID, const bool is_process_shared, const bool is_to_be_created);
    ~Binary_Semaphore();
    
    void post();
    void wait();

    template<typename T>
    static void create_shared_memory(T **shm, const std::string& shm_ID, const bool is_to_be_created, const int o_flags, const int mode) {
        int shm_fd;
        if ((shm_fd = shm_open(shm_ID.c_str(), o_flags, mode)) == -1) {
            std::cerr << "shm_open failed with " << shm_ID << "\n";

            exit(EXIT_FAILURE);
        }

        if (is_to_be_created) {
            if (ftruncate(shm_fd, sizeof(T)) == -1) {
                std::cerr << "ftruncate failed with " << shm_ID << "\n";

                exit(EXIT_FAILURE);
            }
        }

        if ((*shm = reinterpret_cast<T*>(mmap(nullptr, sizeof(T), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0))) == MAP_FAILED) {
            std::cerr << "mmap failed with " << shm_ID << "\n";

            exit(EXIT_FAILURE);
        }

        close(shm_fd);
    }
};

#endif

Binary_Semaphore.cpp

#include "Binary_Semaphore.h"

Binary_Semaphore::Binary_Semaphore(const std::string& bin_sem_attr_shm_ID, const bool is_process_shared, const bool is_to_be_created) : bin_sem_attr_shm_ID(bin_sem_attr_shm_ID), is_process_shared(is_process_shared), is_to_be_created(is_to_be_created) {
    /* set binary semaphore attribute */
    if (is_to_be_created) {
        if (is_process_shared) {
            create_shared_memory(&bin_sem_attr, bin_sem_attr_shm_ID, is_to_be_created, O_CREAT | O_RDWR | O_TRUNC, S_IRWXU | S_IRWXG);

            /* set mutex shared between processes */
            pthread_mutexattr_t mutex_attr;
            pthread_mutexattr_init(&mutex_attr);
            pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
            pthread_mutexattr_setrobust(&mutex_attr, PTHREAD_MUTEX_ROBUST);
            pthread_mutex_init(&bin_sem_attr->mutex, &mutex_attr);
            pthread_mutexattr_destroy(&mutex_attr);

            /* set cvar shared between processes */
            pthread_condattr_t cvar_attr;
            pthread_condattr_init(&cvar_attr);
            pthread_condattr_setpshared(&cvar_attr, PTHREAD_PROCESS_SHARED);
            pthread_cond_init(&bin_sem_attr->cvar, &cvar_attr);
            pthread_condattr_destroy(&cvar_attr);
        } else
            bin_sem_attr = new binary_semaphore_attr();
    } else {
        if (is_process_shared)
            create_shared_memory(&bin_sem_attr, bin_sem_attr_shm_ID, is_to_be_created, O_RDWR, S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP);
    }
}

Binary_Semaphore::~Binary_Semaphore() {
    if (is_to_be_created) {
        pthread_mutex_destroy(&bin_sem_attr->mutex);
        pthread_cond_destroy(&bin_sem_attr->cvar);
    }

    if (is_process_shared) {
        munmap(bin_sem_attr, sizeof(binary_semaphore_attr));
        shm_unlink(bin_sem_attr_shm_ID.c_str());
    }
}

void Binary_Semaphore::post() {
    if (pthread_mutex_lock(&bin_sem_attr->mutex) == EOWNERDEAD)
        pthread_mutex_consistent(&bin_sem_attr->mutex);
    bin_sem_attr->flag = true;
    pthread_mutex_unlock(&bin_sem_attr->mutex);
    pthread_cond_signal(&bin_sem_attr->cvar);
}

void Binary_Semaphore::wait() {
    if (pthread_mutex_lock(&bin_sem_attr->mutex) == EOWNERDEAD)
        pthread_mutex_consistent(&bin_sem_attr->mutex);
    while (!bin_sem_attr->flag) {
        if (pthread_cond_wait(&bin_sem_attr->cvar, &bin_sem_attr->mutex) == EOWNERDEAD)
            pthread_mutex_consistent(&bin_sem_attr->mutex);
    }
    bin_sem_attr->flag = false;
    pthread_mutex_unlock(&bin_sem_attr->mutex);
}

First_Process.cpp

#include <iostream>
#include <string>
#include <chrono>
#include <thread>

#include "Binary_Semaphore.h"

int main() {
    static const std::string BSEM = R"(/BSEM)";
    
    Binary_Semaphore *binary_sem = new Binary_Semaphore(BSEM, true, true);
    while (true) {
        binary_sem->post();
        std::cout << "signal posted" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1LL));
    }
}

Second_Process.cpp

#include <iostream>
#include <string>

#include "Binary_Semaphore.h"

int main() {
    static const std::string BSEM = R"(/BSEM)";
    
    Binary_Semaphore *binary_sem = new Binary_Semaphore(BSEM, true, false);
    while (true) {
        binary_sem->wait();
        std::cout << "signal received" << std::endl;
    }
}

Run first process followed by second process and then abruptly terminate second process using Ctrl^C and then rerun second process, no more prints on the terminal(both first process and second process).

Did anybody face same kind of issue with latest gcc versions?

Upvotes: 4

Views: 255

Answers (1)

Florian Weimer
Florian Weimer

Reputation: 33694

glibc does not support robust condition variables. (They are not part of POSIX.) You need to re-create the shared memory segment with the condition variables if one of the participating processes terminates abnormally.

Upvotes: 2

Related Questions