aboutsummaryrefslogtreecommitdiff
path: root/pw_sync/public/pw_sync/mutex.h
blob: 5b42468121c1ad5645b815065f6087958c77728d (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
// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once

#include <stdbool.h>

#include "pw_preprocessor/util.h"
#include "pw_sync/lock_annotations.h"

#ifdef __cplusplus

#include "pw_sync/virtual_basic_lockable.h"
#include "pw_sync_backend/mutex_native.h"

namespace pw::sync {

/// The `Mutex` is a synchronization primitive that can be used to protect
/// shared data from being simultaneously accessed by multiple threads.  It
/// offers exclusive, non-recursive ownership semantics where priority
/// inheritance is used to solve the classic priority-inversion problem.  This
/// is thread safe, but NOT IRQ safe.
///
/// @rst
/// .. warning::
///
///    In order to support global statically constructed Mutexes, the user
///    and/or backend MUST ensure that any initialization required in your
///    environment is done prior to the creation and/or initialization of the
///    native synchronization primitives (e.g. kernel initialization).
/// @endrst
class PW_LOCKABLE("pw::sync::Mutex") Mutex {
 public:
  using native_handle_type = backend::NativeMutexHandle;

  Mutex();
  ~Mutex();
  Mutex(const Mutex&) = delete;
  Mutex(Mutex&&) = delete;
  Mutex& operator=(const Mutex&) = delete;
  Mutex& operator=(Mutex&&) = delete;

  /// Locks the mutex, blocking indefinitely. Failures are fatal.
  ///
  /// @b PRECONDITION:
  ///   The lock isn't already held by this thread. Recursive locking is
  ///   undefined behavior.
  void lock() PW_EXCLUSIVE_LOCK_FUNCTION();

  /// Attempts to lock the mutex in a non-blocking manner.
  /// Returns true if the mutex was successfully acquired.
  ///
  /// @b PRECONDITION:
  ///   The lock isn't already held by this thread. Recursive locking is
  ///   undefined behavior.
  bool try_lock() PW_EXCLUSIVE_TRYLOCK_FUNCTION(true);

  /// Unlocks the mutex. Failures are fatal.
  ///
  /// @b PRECONDITION:
  ///   The mutex is held by this thread.
  void unlock() PW_UNLOCK_FUNCTION();

  native_handle_type native_handle();

 protected:
  /// Expose the NativeMutex directly to derived classes (TimedMutex) in case
  /// implementations use different types for backend::NativeMutex and
  /// native_handle().
  backend::NativeMutex& native_type() { return native_type_; }
  const backend::NativeMutex& native_type() const { return native_type_; }

 private:
  /// This may be a wrapper around a native type with additional members.
  backend::NativeMutex native_type_;
};

class PW_LOCKABLE("pw::sync::VirtualMutex") VirtualMutex final
    : public VirtualBasicLockable {
 public:
  VirtualMutex() = default;

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

  Mutex& mutex() { return mutex_; }

 private:
  void DoLockOperation(Operation operation) override
      PW_NO_LOCK_SAFETY_ANALYSIS {
    switch (operation) {
      case Operation::kLock:
        return mutex_.lock();

      case Operation::kUnlock:
      default:
        return mutex_.unlock();
    }
  }

  Mutex mutex_;
};

}  // namespace pw::sync

#include "pw_sync_backend/mutex_inline.h"

using pw_sync_Mutex = pw::sync::Mutex;

#else  // !defined(__cplusplus)

typedef struct pw_sync_Mutex pw_sync_Mutex;

#endif  // __cplusplus

PW_EXTERN_C_START

/// Invokes the `Mutex::lock` member function on the given `mutex`.
void pw_sync_Mutex_Lock(pw_sync_Mutex* mutex) PW_NO_LOCK_SAFETY_ANALYSIS;

/// Invokes the `Mutex::try_lock` member function on the given `mutex`.
bool pw_sync_Mutex_TryLock(pw_sync_Mutex* mutex) PW_NO_LOCK_SAFETY_ANALYSIS;

/// Invokes the `Mutex::unlock` member function on the given `mutex`.
void pw_sync_Mutex_Unlock(pw_sync_Mutex* mutex) PW_NO_LOCK_SAFETY_ANALYSIS;

PW_EXTERN_C_END