qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Use the new lock guard macros for safe locking


From: Stefan Hajnoczi
Subject: Use the new lock guard macros for safe locking
Date: Thu, 19 Mar 2020 11:18:02 +0000

Below is a wiki page I just created explaining the lock guard macros
that make locking code simpler and safer.

I wanted to make everyone aware of these new macros.  Please consider
using them!

Thanks,
Stefan
---
>From https://wiki.qemu.org/ToDo/LockGuards:

Lock guards are a safe alternative to manual lock()/unlock() calls.
They take a lock and automatically unlock it at the end of the scope
or when the function returns.  This prevents common bugs when locks
are not released in error code paths.

Lock guards were added to QEMU in March 2020.  Much of the codebase
does not take advantage of them yet.  This page explains how to use
them and when to convert existing code.

==Lock guard macros==
Two lock guard macros are defined in "qemu/lockable.h":

  /**
   * WITH_QEMU_LOCK_GUARD - Lock a lock object for scope
   *
   * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin).
   *
   * This macro defines a lock scope such that entering the scope takes the lock
   * and leaving the scope releases the lock.  Return statements are allowed
   * within the scope and release the lock.  Break and continue statements leave
   * the scope early and release the lock.
   *
   *   WITH_QEMU_LOCK_GUARD(&mutex) {
   *       ...
   *       if (error) {
   *           return; <-- mutex is automatically unlocked
   *       }
   *
   *       if (early_exit) {
   *           break;  <-- leave this scope early
   *       }
   *       ...
   *   }
   */
  #define WITH_QEMU_LOCK_GUARD(x)

and

  /**
   * QEMU_LOCK_GUARD - Lock an object until the end of the scope
   *
   * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin).
   *
   * This macro takes a lock until the end of the scope.  Return statements
   * release the lock.
   *
   *   ... <-- mutex not locked
   *   QEMU_LOCK_GUARD(&mutex); <-- mutex locked from here onwards
   *   ...
   *   if (error) {
   *       return; <-- mutex is automatically unlocked
   *   }
   */
  #define QEMU_LOCK_GUARD(x)

==How to use lock guards==
Use WITH_QEMU_LOCK_GUARD() when the lock must be released before the
end of the function.  Use QEMU_LOCK_GUARD() when the lock must be held
until the end of the function.

Here is an example conversion from manual lock()/unlock() calls to
WITH_QEMU_LOCK_GUARD().  Before:

  qemu_mutex_lock(&mutex);
  if (data == NULL) {
      qemu_mutex_unlock(&mutex);
      return false;
  }
  ...use data...
  qemu_mutex_unlock(&mutex);
  return true;

After:

  WITH_QEMU_LOCK_GUARD(&mutex) {
      if (data == NULL) {
          return false;
      }
      ...use data...
  }

Notice that it is no longer necessary to manually call qemu_mutex_unlock().

==Which types of locks are supported==
QemuMutex, QemuRecMutex, CoMutex, and QemuSpin are supported by
WITH_QEMU_LOCK_GUARD() and QEMU_LOCK_GUARD().

The RCU read lock has its own WITH_RCU_READ_LOCK_GUARD() and
RCU_READ_LOCK_GUARD() macros in "qemu/rcu.h" that can replace manual
rcu_read_lock()/rcu_read_unlock() calls.

==When to convert existing code to lock guards==
Lock guards usually make code easier to read by eliminating unlock()
calls and gotos.  However, there are some exceptions where it is
either not worth it or when lock guards cannot handle complex cases.

They do not provide a significant advantage for straight-line code
without conditional statements:

  qemu_mutex_lock(&mutex);
  data++;
  qemu_mutex_unlock(&mutex);

Leave simple code alone because it is not worth developing and
reviewing patches that have no benefit.

They also do not handle complex control flow such as loops that
temporarily drop a lock:

  qemu_mutex_lock(&mutex);
  while (running) {
      ...
      qemu_mutex_unlock(&mutex);
      wait();
      qemu_mutex_lock(&mutex);
      ...
  }
  qemu_mutex_unlock(&mutex);

Although lock guard macros could be mixed with manual lock()/unlock()
calls to handle some complex control flow cases, this can be subtle
and may lead to bugs.  Do not try to convert these cases to use lock
guards.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]