vendredi 15 mai 2020

how to use std::atomic_signal_fence() with semaphore and volatile?

std::atomic_signal_fence() Establishes memory synchronization ordering ... between a thread and a signal handler executed on the same thread. -- cppreference

  • In order to find an example for this illustration, I looked at bames53's similar question in stackoverflow. However the answer may not suit my x86_64 environment, since x86_64 CPU is strong memory model and forbids Store-Store re-ordering ^1. Its example will correctly execute even without std::atomic_signal_fence() in my x86_64 environment.

  • So I made a Store-Load re-ordering example suitable for x86_64 after Jeff Preshing's post. The example code is not that short, so I opened this question instead of appending onto bames53's similar question.

#include <atomic>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <semaphore.h>
#include <signal.h>
#include <unistd.h>

sem_t endSema;
// volatile int synchronizer;
int X, Y;
int r1, r2;

void signal_handler(int sig) {
  signal(sig, SIG_IGN);
  Y = 1;
  // std::atomic_signal_fence(std::memory_order_seq_cst);  // if uncommented, assert still may fail
  r2 = X;
  signal(SIGINT, signal_handler);
  sem_post(&endSema);  // if changed to the following, assert never fail
  // synchronizer = 1;
}

int main(int argc, char* argv[]) {
  std::srand(std::time(nullptr));
  sem_init(&endSema, 0, 0);
  signal(SIGINT, signal_handler);
  for (;;) {
    while(std::rand() % std::stol(argv[1]) != 0);  // argv[1] ~ 1000'000
    X = 1;
    // std::atomic_signal_fence(std::memory_order_seq_cst);  // if uncommented, assert still may fail.
    r1 = Y;
    sem_wait(&endSema);  // if changed to the following, assert never fail
    // while (synchronizer == 0); synchronizer = 0;
    std::cout << "r1=" << r1 << " r2=" << r2 << std::endl;

    if (r1 == 0) assert(r2 != 0);

    Y = 0; r1 = 0; r2 = 0; X = 0;
  }
  return 0;
}
  • Firstly semaphore is used to synchronize main() with signal_handler(). In this version, the assert always fail after around received 30 SIGINTs with or without the signal fence. It seems that std::atomic_signal_fence() did not work as I expected.

  • Secondly If semaphore is replaced with volatile int synchronizer, the program seems never fail with or without the signal fence.

What's wrong with the code? Or did I miss-understand the cppreference doc?

Below is some relevant info:

  • compiling & running env: CentOS 8 (Linux 4.18.0) x86_64 single CPU core.

  • Compiler: g++ (GCC) 8.3.1 20190507

  • Compiling command g++ -std=c++17 -o ordering -O2 ordering.cpp -pthread

  • Run with ./ordering 1000000, then keep pressing Ctrl-C to invoke the signal handler.

Aucun commentaire:

Enregistrer un commentaire