1351282Sdim//===-- mutex.h -------------------------------------------------*- C++ -*-===//
2351282Sdim//
3351282Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4351282Sdim// See https://llvm.org/LICENSE.txt for license information.
5351282Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6351282Sdim//
7351282Sdim//===----------------------------------------------------------------------===//
8351282Sdim
9351282Sdim#ifndef SCUDO_MUTEX_H_
10351282Sdim#define SCUDO_MUTEX_H_
11351282Sdim
12351282Sdim#include "atomic_helpers.h"
13351282Sdim#include "common.h"
14351282Sdim
15351282Sdim#include <string.h>
16351282Sdim
17351282Sdim#if SCUDO_FUCHSIA
18351282Sdim#include <lib/sync/mutex.h> // for sync_mutex_t
19351282Sdim#endif
20351282Sdim
21351282Sdimnamespace scudo {
22351282Sdim
23351282Sdimclass HybridMutex {
24351282Sdimpublic:
25351282Sdim  void init() { memset(this, 0, sizeof(*this)); }
26351282Sdim  bool tryLock();
27351282Sdim  NOINLINE void lock() {
28360784Sdim    if (LIKELY(tryLock()))
29351282Sdim      return;
30351282Sdim      // The compiler may try to fully unroll the loop, ending up in a
31351282Sdim      // NumberOfTries*NumberOfYields block of pauses mixed with tryLocks. This
32351282Sdim      // is large, ugly and unneeded, a compact loop is better for our purpose
33351282Sdim      // here. Use a pragma to tell the compiler not to unroll the loop.
34351282Sdim#ifdef __clang__
35351282Sdim#pragma nounroll
36351282Sdim#endif
37351282Sdim    for (u8 I = 0U; I < NumberOfTries; I++) {
38351282Sdim      yieldProcessor(NumberOfYields);
39351282Sdim      if (tryLock())
40351282Sdim        return;
41351282Sdim    }
42351282Sdim    lockSlow();
43351282Sdim  }
44351282Sdim  void unlock();
45351282Sdim
46351282Sdimprivate:
47360784Sdim  static constexpr u8 NumberOfTries = 8U;
48360784Sdim  static constexpr u8 NumberOfYields = 8U;
49351282Sdim
50351282Sdim#if SCUDO_LINUX
51351282Sdim  atomic_u32 M;
52351282Sdim#elif SCUDO_FUCHSIA
53351282Sdim  sync_mutex_t M;
54351282Sdim#endif
55351282Sdim
56351282Sdim  void lockSlow();
57351282Sdim};
58351282Sdim
59351282Sdimclass ScopedLock {
60351282Sdimpublic:
61351282Sdim  explicit ScopedLock(HybridMutex &M) : Mutex(M) { Mutex.lock(); }
62351282Sdim  ~ScopedLock() { Mutex.unlock(); }
63351282Sdim
64351282Sdimprivate:
65351282Sdim  HybridMutex &Mutex;
66351282Sdim
67351282Sdim  ScopedLock(const ScopedLock &) = delete;
68351282Sdim  void operator=(const ScopedLock &) = delete;
69351282Sdim};
70351282Sdim
71351282Sdim} // namespace scudo
72351282Sdim
73351282Sdim#endif // SCUDO_MUTEX_H_
74