blob: d4d6e45c7fc5a54472e6b85c2871c9f23ab294c4 [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
#pragma once
#include <assert.h>
#include <debug.h>
#include <kernel/atomic.h>
#include <kernel/thread.h>
#include <lockdep/lock_policy.h>
#include <lockdep/lock_traits.h>
#include <stdint.h>
#include <zircon/compiler.h>
#include <zircon/thread_annotations.h>
#ifndef __cplusplus
static_assert(false, "mutex.h requires C++");
#endif
constexpr uint32_t MUTEX_MAGIC = 0x6D757478; // 'mutx'
constexpr uintptr_t MUTEX_FLAG_QUEUED = 1u;
// Body of the mutex.
// The val field holds either 0 or a pointer to the thread_t holding the mutex.
// If one or more threads are blocking and queued up, MUTEX_FLAG_QUEUED is ORed in as well.
// NOTE: MUTEX_FLAG_QUEUED is only manipulated under the THREAD_LOCK.
typedef struct TA_CAP("mutex") mutex {
constexpr mutex() = default;
~mutex();
uint32_t magic = MUTEX_MAGIC;
uintptr_t val = 0;
wait_queue_t wait = WAIT_QUEUE_INITIAL_VALUE(this->wait);
} mutex_t;
// accessors to extract the holder pointer from the val member
static inline uintptr_t mutex_val(const mutex_t* m) {
static_assert(sizeof(uintptr_t) == sizeof(uint64_t), "");
return atomic_load_u64_relaxed((uint64_t*)&m->val);
}
static inline thread_t* mutex_holder(const mutex_t* m) {
static_assert(sizeof(uintptr_t) == sizeof(uint64_t), "");
return (thread_t*)(mutex_val(m) & ~MUTEX_FLAG_QUEUED);
}
// Rules for Mutexes:
// - Mutexes are only safe to use from thread context.
// - Mutexes are non-recursive.
void mutex_acquire(mutex_t* m) TA_ACQ(m);
void mutex_release(mutex_t* m) TA_REL(m);
// special version of the above with the thread lock held
void mutex_release_thread_locked(mutex_t* m, bool reschedule) TA_REL(m);
// does the current thread hold the mutex?
static inline bool is_mutex_held(const mutex_t* m) {
return (mutex_holder(m) == get_current_thread());
}
// Declares a fbl::Mutex member of the struct or class |containing_type|.
//
// Example usage:
//
// struct MyType {
// DECLARE_MUTEX(MyType) lock;
// };
//
#define DECLARE_MUTEX(containing_type) \
LOCK_DEP_INSTRUMENT(containing_type, fbl::Mutex)
// Declares a |lock_type| member of the struct or class |containing_type|.
//
// Example usage:
//
// struct MyType {
// DECLARE_LOCK(MyType, LockType) lock;
// };
//
#define DECLARE_LOCK(containing_type, lock_type) \
LOCK_DEP_INSTRUMENT(containing_type, lock_type)
// Declares a singleton fbl::Mutex with the name |name|.
//
// Example usage:
//
// DECLARE_SINGLETON_MUTEX(MyGlobalLock [, LockFlags]);
//
#define DECLARE_SINGLETON_MUTEX(name, ...) \
LOCK_DEP_SINGLETON_LOCK(name, fbl::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__)
// Forward declaration.
struct MutexPolicy;
namespace fbl {
// Forward declaration. In the kernel this header is included by fbl/mutext.h.
class Mutex;
// Configure Guard<fbl::Mutex> to use the following policy. This must be done
// in the same namespace as the mutex type.
LOCK_DEP_POLICY(Mutex, MutexPolicy);
} // namespace fbl
// Lock policy for acquiring an fbl::Mutex.
struct MutexPolicy {
// No extra state required for mutexes.
struct State {};
// Basic acquire and release operations.
template <typename LockType>
static bool Acquire(LockType* lock, State*) TA_ACQ(lock) {
lock->Acquire();
return true;
}
template <typename LockType>
static void Release(LockType* lock, State*) TA_REL(lock) {
lock->Release();
}
// A enum tag that can be passed to Guard<fbl::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<fbl::Mutex>::Release(ThreadLockHeld [, Reschedule | NoReschedule])
//
template <typename LockType>
static void Release(LockType* lock, State*, SelectThreadLockHeld,
RescheduleOption reschedule = Reschedule)
TA_NO_THREAD_SAFETY_ANALYSIS {
mutex_release_thread_locked(lock->GetInternal(), reschedule);
}
};