blob: 6f1293510ded72333dd9ed999fe625def18d34c4 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008-2014 Travis Geiselbrecht
// Copyright (c) 2012 Shantanu Gupta
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#ifndef ZIRCON_KERNEL_INCLUDE_KERNEL_MUTEX_H_
#define ZIRCON_KERNEL_INCLUDE_KERNEL_MUTEX_H_
#include <assert.h>
#include <debug.h>
#include <lib/zircon-internal/thread_annotations.h>
#include <stdint.h>
#include <zircon/compiler.h>
#include <zircon/time.h>
#include <fbl/canary.h>
#include <fbl/macros.h>
#include <kernel/lockdep.h>
#include <kernel/owned_wait_queue.h>
#include <kernel/thread.h>
#include <ktl/atomic.h>
// Kernel mutex support.
//
class TA_CAP("mutex") Mutex {
public:
constexpr Mutex() = default;
~Mutex();
// No moving or copying allowed.
DISALLOW_COPY_ASSIGN_AND_MOVE(Mutex);
// The maximum duration to spin before falling back to blocking.
// TODO(ZX-4873): Decide how to make this configurable per device/platform
// and describe how to optimize this value.
static constexpr zx_duration_t SPIN_MAX_DURATION = ZX_USEC(150);
void Acquire(zx_duration_t spin_max_duration = SPIN_MAX_DURATION) TA_ACQ() TA_EXCL(thread_lock);
void Release() TA_REL() TA_EXCL(thread_lock);
// Special version of Release which operates with the thread lock held
void ReleaseThreadLocked(const bool allow_reschedule) TA_REL() TA_REQ(thread_lock);
// does the current thread hold the mutex?
bool IsHeld() const { return (holder() == Thread::Current::Get()); }
// Panic unless the given lock is held.
//
// Can be used when thread safety analysis can't prove you are holding
// a lock. The asserts may be optimized away in release builds.
void AssertHeld() const TA_ASSERT() { DEBUG_ASSERT(IsHeld()); }
private:
enum class ThreadLockState : bool { NotHeld = false, Held = true };
static constexpr uint32_t MAGIC = 0x6D757478; // 'mutx'
static constexpr uintptr_t STATE_FREE = 0u;
static constexpr uintptr_t STATE_FLAG_CONTESTED = 1u;
template <ThreadLockState TLS>
void ReleaseInternal(const bool allow_reschedule) TA_REL() __TA_NO_THREAD_SAFETY_ANALYSIS;
// Accessors to extract the holder pointer from the val member
uintptr_t val() const { return val_.load(ktl::memory_order_relaxed); }
static Thread* holder_from_val(uintptr_t value) {
return reinterpret_cast<Thread*>(value & ~STATE_FLAG_CONTESTED);
}
Thread* holder() const { return holder_from_val(val()); }
fbl::Canary<MAGIC> magic_;
ktl::atomic<uintptr_t> val_{STATE_FREE};
OwnedWaitQueue wait_;
};
// Lock policy for kernel mutexes
//
struct MutexPolicy {
struct State {
const zx_duration_t spin_max_duration{Mutex::SPIN_MAX_DURATION};
};
// Basic acquire and release operations.
template <typename LockType>
static bool Acquire(LockType* lock, State* state) TA_ACQ(lock) TA_EXCL(thread_lock) {
lock->Acquire(state->spin_max_duration);
return true;
}
template <typename LockType>
static void Release(LockType* lock, State*) TA_REL(lock) TA_EXCL(thread_lock) {
lock->Release();
}
// Runtime lock assertions.
template <typename LockType>
static void AssertHeld(const LockType& lock) TA_ASSERT(lock) {
lock.AssertHeld();
}
// A enum tag that can be passed to Guard<Mutex>::Release(...) to
// select the special-case release method below.
enum SelectThreadLockHeld { ThreadLockHeld };
// Specifies whether the special-case release method below should
// reschedule.
enum RescheduleOption : bool {
NoReschedule = false,
Reschedule = true,
};
// Releases the lock using the special mutex release operation. This
// is selected by calling:
//
// Guard<TrivialMutex|Mutex|Mutex>::Release(ThreadLockHeld [, Reschedule | NoReschedule])
//
template <typename LockType>
static void Release(LockType* lock, State*, SelectThreadLockHeld,
RescheduleOption reschedule = Reschedule) TA_REL(lock) TA_REQ(thread_lock) {
lock->ReleaseThreadLocked(reschedule);
}
};
// Configure the lockdep::Guard for kernel mutexes to use MutexPolicy.
LOCK_DEP_POLICY(Mutex, MutexPolicy);
// Declares a Mutex member of the struct or class |containing_type|.
//
// Example usage:
//
// struct MyType {
// DECLARE_MUTEX(MyType [, LockFlags]) lock;
// };
//
#define DECLARE_MUTEX(containing_type, ...) \
LOCK_DEP_INSTRUMENT(containing_type, ::Mutex, ##__VA_ARGS__)
// Declares a |lock_type| member of the struct or class |containing_type|.
//
// Example usage:
//
// struct MyType {
// DECLARE_LOCK(MyType, LockType [, LockFlags]) lock;
// };
//
#define DECLARE_LOCK(containing_type, lock_type, ...) \
LOCK_DEP_INSTRUMENT(containing_type, lock_type, ##__VA_ARGS__)
// By default, singleton mutexes in the kernel use Mutex in order to avoid
// a useless global dtor
//
// Example usage:
//
// DECLARE_SINGLETON_MUTEX(MyGlobalLock [, LockFlags]);
//
#define DECLARE_SINGLETON_MUTEX(name, ...) LOCK_DEP_SINGLETON_LOCK(name, ::Mutex, ##__VA_ARGS__)
// Declares a singleton |lock_type| with the name |name|.
//
// Example usage:
//
// DECLARE_SINGLETON_LOCK(MyGlobalLock, LockType, [, LockFlags]);
//
#define DECLARE_SINGLETON_LOCK(name, lock_type, ...) \
LOCK_DEP_SINGLETON_LOCK(name, lock_type, ##__VA_ARGS__)
#endif // ZIRCON_KERNEL_INCLUDE_KERNEL_MUTEX_H_