summaryrefslogtreecommitdiffstats
path: root/mozglue/misc/WinUtils.h
blob: 2291a352a5a33d89cbc1b1ffd8f8ef7b7fdd4f46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_glue_MozglueUtils_h
#define mozilla_glue_MozglueUtils_h

#include <windows.h>

#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h"

namespace mozilla {
namespace glue {

#ifdef DEBUG

class MOZ_STATIC_CLASS Win32SRWLock final {
 public:
  // Microsoft guarantees that '0' is never a valid thread id
  // https://docs.microsoft.com/en-ca/windows/desktop/ProcThread/thread-handles-and-identifiers
  static const DWORD kInvalidThreadId = 0;

  constexpr Win32SRWLock()
      : mExclusiveThreadId(kInvalidThreadId), mLock(SRWLOCK_INIT) {}

  ~Win32SRWLock() { MOZ_ASSERT(mExclusiveThreadId == kInvalidThreadId); }

  void LockShared() {
    MOZ_ASSERT(
        mExclusiveThreadId != GetCurrentThreadId(),
        "Deadlock detected - A thread attempted to acquire a shared lock on "
        "a SRWLOCK when it already owns the exclusive lock on it.");

    ::AcquireSRWLockShared(&mLock);
  }

  void UnlockShared() { ::ReleaseSRWLockShared(&mLock); }

  void LockExclusive() {
    MOZ_ASSERT(
        mExclusiveThreadId != GetCurrentThreadId(),
        "Deadlock detected - A thread attempted to acquire an exclusive lock "
        "on a SRWLOCK when it already owns the exclusive lock on it.");

    ::AcquireSRWLockExclusive(&mLock);
    mExclusiveThreadId = GetCurrentThreadId();
  }

  void UnlockExclusive() {
    MOZ_ASSERT(mExclusiveThreadId == GetCurrentThreadId());

    mExclusiveThreadId = kInvalidThreadId;
    ::ReleaseSRWLockExclusive(&mLock);
  }

  Win32SRWLock(const Win32SRWLock&) = delete;
  Win32SRWLock(Win32SRWLock&&) = delete;
  Win32SRWLock& operator=(const Win32SRWLock&) = delete;
  Win32SRWLock& operator=(Win32SRWLock&&) = delete;

 private:
  // "Relaxed" memory ordering is fine. Threads will see other thread IDs
  // appear here in some non-deterministic ordering (or not at all) and simply
  // ignore them.
  //
  // But a thread will only read its own ID if it previously wrote it, and a
  // single thread doesn't need a memory barrier to read its own write.

  Atomic<DWORD, Relaxed> mExclusiveThreadId;
  SRWLOCK mLock;
};

#else  // DEBUG

class MOZ_STATIC_CLASS Win32SRWLock final {
 public:
  constexpr Win32SRWLock() : mLock(SRWLOCK_INIT) {}

  void LockShared() { ::AcquireSRWLockShared(&mLock); }

  void UnlockShared() { ::ReleaseSRWLockShared(&mLock); }

  void LockExclusive() { ::AcquireSRWLockExclusive(&mLock); }

  void UnlockExclusive() { ::ReleaseSRWLockExclusive(&mLock); }

  ~Win32SRWLock() = default;

  Win32SRWLock(const Win32SRWLock&) = delete;
  Win32SRWLock(Win32SRWLock&&) = delete;
  Win32SRWLock& operator=(const Win32SRWLock&) = delete;
  Win32SRWLock& operator=(Win32SRWLock&&) = delete;

 private:
  SRWLOCK mLock;
};

#endif

class MOZ_RAII AutoSharedLock final {
 public:
  explicit AutoSharedLock(Win32SRWLock& aLock) : mLock(aLock) {
    mLock.LockShared();
  }

  ~AutoSharedLock() { mLock.UnlockShared(); }

  AutoSharedLock(const AutoSharedLock&) = delete;
  AutoSharedLock(AutoSharedLock&&) = delete;
  AutoSharedLock& operator=(const AutoSharedLock&) = delete;
  AutoSharedLock& operator=(AutoSharedLock&&) = delete;

 private:
  Win32SRWLock& mLock;
};

class MOZ_RAII AutoExclusiveLock final {
 public:
  explicit AutoExclusiveLock(Win32SRWLock& aLock) : mLock(aLock) {
    mLock.LockExclusive();
  }

  ~AutoExclusiveLock() { mLock.UnlockExclusive(); }

  AutoExclusiveLock(const AutoExclusiveLock&) = delete;
  AutoExclusiveLock(AutoExclusiveLock&&) = delete;
  AutoExclusiveLock& operator=(const AutoExclusiveLock&) = delete;
  AutoExclusiveLock& operator=(AutoExclusiveLock&&) = delete;

 private:
  Win32SRWLock& mLock;
};

}  // namespace glue
}  // namespace mozilla

#endif  //  mozilla_glue_MozglueUtils_h