urcu/annotate: Add CMM annotation
authorOlivier Dion <odion@efficios.com>
Fri, 31 Mar 2023 17:47:17 +0000 (13:47 -0400)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Mon, 14 Aug 2023 19:46:29 +0000 (15:46 -0400)
commit601922a81d884e16ff404cee7534ede56fb87d0a
tree8ee32b2a6cb0e0ef470c7fa9068a316bf98ea884
parentfcab075f67cf3e29e8556b4af1bbbfb210977ac2
urcu/annotate: Add CMM annotation

The CMM annotation is highly experimental and not meant to be used by
user for now, even though it is exposed in the public API since some
parts of the liburcu public API require those annotations.

The main primitive is the cmm_annotate_t which denotes a group of memory
operations associated with a memory barrier. A group follows a state
machine, starting from the `CMM_ANNOTATE_VOID' state. The following are
the only valid transitions:

  CMM_ANNOTATE_VOID -> CMM_ANNOTATE_MB (acquire & release MB)
  CMM_ANNOTATE_VOID -> CMM_ANNOTATE_LOAD (acquire memory)
  CMM_ANNOTATE_LOAD -> CMM_ANNOTATE_MB (acquire MB)

The macro `cmm_annotate_define(name)' can be used to create an
annotation object on the stack. The rest of the `cmm_annotate_*' macros
can be used to change the state of the group after validating that the
transition is allowed. Some of these macros also inject TSAN annotations
to help it understand the flow of events in the program since it does
not currently support thread fence.

Sometime, a single memory access does not need to be associated with a
group. In the case, the acquire/release macros variant without the
`group' infix can be used to annotate memory accesses.

Note that TSAN can not be used on the liburcu-signal flavor. This is
because TSAN hijacks calls to sigaction(3) and places its own handler
that will deliver the signal to the application at a synchronization
point.

Thus, the usage of TSAN on the signal flavor is undefined
behavior. However, there's at least one known behavior which is a
deadlock between readers that want to unregister them-self by locking
the `rcu_registry_lock' while a synchronize RCU is made on the writer
side which has already locked that mutex until all the registered
readers execute a memory barrier in a signal handler defined by
liburcu-signal. However, TSAN will not call the registered handler while
waiting on the mutex. Therefore, the writer spin infinitely on
pthread_kill(3p) because the reader simply never complete the handshake.

See the deadlock minimal reproducer below.

Deadlock reproducer:
```
  #include <poll.h>
  #include <signal.h>

  #include <pthread.h>

  #define SIGURCU SIGUSR1

  static pthread_mutex_t rcu_registry_lock = PTHREAD_MUTEX_INITIALIZER;
  static int need_mb = 0;

  static void *reader_side(void *nil)
  {
   (void) nil;

   pthread_mutex_lock(&rcu_registry_lock);
   pthread_mutex_unlock(&rcu_registry_lock);

   return NULL;
  }

  static void writer_side(pthread_t reader)
  {
   __atomic_store_n(&need_mb, 1, __ATOMIC_RELEASE);
   while (__atomic_load_n(&need_mb, __ATOMIC_ACQUIRE)) {
   pthread_kill(reader, SIGURCU);
   (void) poll(NULL, 0, 1);
   }
   pthread_mutex_unlock(&rcu_registry_lock);

   pthread_join(reader, NULL);
  }

  static void sigrcu_handler(int signo, siginfo_t *siginfo, void *context)
  {
   (void) signo;
   (void) siginfo;
   (void) context;

   __atomic_store_n(&need_mb, 0, __ATOMIC_SEQ_CST);
  }

  static void install_signal(void)
  {
   struct sigaction act;

   act.sa_sigaction = sigrcu_handler;
   act.sa_flags     = SA_SIGINFO | SA_RESTART;

   sigemptyset(&act.sa_mask);

   (void) sigaction(SIGURCU, &act, NULL);
  }

  int main(void)
  {
   pthread_t th;

   install_signal();

   pthread_mutex_lock(&rcu_registry_lock);
   pthread_create(&th, NULL, reader_side, NULL);

   writer_side(th);

   return 0;
  }
```

Change-Id: I9c234bb311cc0f82ea9dbefdf4fee07047ab93f9
Co-authored-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Signed-off-by: Olivier Dion <odion@efficios.com>
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
13 files changed:
include/Makefile.am
include/urcu/annotate.h [new file with mode: 0644]
include/urcu/arch/generic.h
include/urcu/static/urcu-bp.h
include/urcu/static/urcu-common.h
include/urcu/static/urcu-mb.h
include/urcu/static/urcu-memb.h
include/urcu/static/urcu-qsbr.h
src/rculfhash.c
src/urcu-bp.c
src/urcu-qsbr.c
src/urcu-wait.h
src/urcu.c
This page took 0.026656 seconds and 4 git commands to generate.