Skip to content

Commit

Permalink
[magenta][locks] Add spin_lock_t helper classes.
Browse files Browse the repository at this point in the history
Add simple helper classes for spinlocks which allow C++ to use common
auto initialization and AutoLock patterns.

Change-Id: I32d1c3e3a119d62357a74cb5dd796bfd20db8fcd
  • Loading branch information
johngro committed Oct 20, 2016
1 parent 1e03630 commit da3a1de
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 61 deletions.
59 changes: 36 additions & 23 deletions kernel/include/kernel/auto_lock.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,54 @@
#pragma once

#include <kernel/mutex.h>
#include <kernel/spinlock.h>
#include <mxtl/auto_lock.h>

class AutoLock {
class AutoSpinLock {
public:
AutoLock(mutex_t* mutex)
: mutex_(mutex) {
mutex_acquire(mutex_);
}
explicit AutoSpinLock(spin_lock_t& lock) : spinlock_(&lock) { acquire(); }
explicit AutoSpinLock(SpinLock& lock) : spinlock_(lock.GetInternal()) { acquire(); }
~AutoSpinLock() { release(); }

AutoLock(mutex_t& mutex)
: AutoLock(&mutex) {}
void release() {
if (spinlock_) {
spin_unlock(spinlock_);
spinlock_ = nullptr;
}
}

AutoLock(Mutex& mutex)
: AutoLock(mutex.GetInternal()) {}
// suppress default constructors
AutoSpinLock(const AutoSpinLock& am) = delete;
AutoSpinLock(AutoSpinLock&& c) = delete;
AutoSpinLock& operator=(const AutoSpinLock& am) = delete;
AutoSpinLock& operator=(AutoSpinLock&& c) = delete;

AutoLock(Mutex* mutex)
: AutoLock(mutex->GetInternal()) {}
private:
void acquire() { spin_lock(spinlock_); }
spin_lock_t* spinlock_;
};

~AutoLock() {
release();
}
class AutoSpinLockIrqSave {
public:
explicit AutoSpinLockIrqSave(spin_lock_t& lock) : spinlock_(&lock) { acquire(); }
explicit AutoSpinLockIrqSave(SpinLock& lock) : spinlock_(lock.GetInternal()) { acquire(); }
~AutoSpinLockIrqSave() { release(); }

// early release the mutex before the object goes out of scope
void release() {
if (mutex_) {
mutex_release(mutex_);
mutex_ = nullptr;
if (spinlock_) {
spin_unlock_irqrestore(spinlock_, state_);
spinlock_ = nullptr;
}
}

// suppress default constructors
AutoLock(const AutoLock& am) = delete;
AutoLock& operator=(const AutoLock& am) = delete;
AutoLock(AutoLock&& c) = delete;
AutoLock& operator=(AutoLock&& c) = delete;
AutoSpinLockIrqSave(const AutoSpinLockIrqSave& am) = delete;
AutoSpinLockIrqSave(AutoSpinLockIrqSave&& c) = delete;
AutoSpinLockIrqSave& operator=(const AutoSpinLockIrqSave& am) = delete;
AutoSpinLockIrqSave& operator=(AutoSpinLockIrqSave&& c) = delete;

private:
mutex_t* mutex_;
void acquire() { spin_lock_irqsave(spinlock_, state_); }
spin_lock_t* spinlock_;
spin_lock_saved_state_t state_;
};
42 changes: 4 additions & 38 deletions kernel/include/kernel/mutex.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,44 +60,10 @@ static bool is_mutex_held(const mutex_t *m)

__END_CDECLS;

#ifdef __cplusplus
class Mutex {
public:
constexpr Mutex() : mutex_(MUTEX_INITIAL_VALUE(mutex_)) { }

~Mutex() {
mutex_destroy(&mutex_);
}

void Acquire() {
mutex_acquire(&mutex_);
}

status_t AcquireTimeout(lk_time_t timeout) {
return mutex_acquire_timeout(&mutex_, timeout);
}

void Release() {
mutex_release(&mutex_);
}

bool IsHeld() const {
return is_mutex_held(&mutex_);
}

mutex_t* GetInternal() {
return &mutex_;
}

// suppress default constructors
Mutex(const Mutex& am) = delete;
Mutex& operator=(const Mutex& am) = delete;
Mutex(Mutex&& c) = delete;
Mutex& operator=(Mutex&& c) = delete;
private:
mutex_t mutex_;
};
#endif
// Include the handy C++ Mutex/AutoLock wrappers from mxtl. Note, this include
// must come after the kernel definition of mutex_t and the prototypes for the
// mutex routines.
#include <mxtl/mutex.h>

#endif

32 changes: 32 additions & 0 deletions kernel/include/kernel/spinlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,35 @@ static inline void spin_unlock_restore(
#define spin_unlock_irqrestore(lock, statep) spin_unlock_restore(lock, statep, SPIN_LOCK_FLAG_INTERRUPTS)

__END_CDECLS

#ifdef __cplusplus
class SpinLock {
public:
SpinLock() { spin_lock_init(&spinlock_); }
void Acquire() { spin_lock(&spinlock_); }
void TryAcquire() { spin_trylock(&spinlock_); }
void Release() { spin_unlock(&spinlock_); }
bool IsHeld() { return spin_lock_held(&spinlock_); }

void AcquireIrqSave(spin_lock_saved_state_t& state,
spin_lock_save_flags_t flags = SPIN_LOCK_FLAG_INTERRUPTS) {
spin_lock_save(&spinlock_, &state, flags);
}

void ReleaseIrqRestore(spin_lock_saved_state_t state,
spin_lock_save_flags_t flags = SPIN_LOCK_FLAG_INTERRUPTS) {
spin_unlock_restore(&spinlock_, state, flags);
}

spin_lock_t* GetInternal() { return &spinlock_; }

// suppress default constructors
SpinLock(const SpinLock& am) = delete;
SpinLock& operator=(const SpinLock& am) = delete;
SpinLock(SpinLock&& c) = delete;
SpinLock& operator=(SpinLock&& c) = delete;

private:
spin_lock_t spinlock_;
};
#endif // ifdef __cplusplus
1 change: 1 addition & 0 deletions kernel/kernel/rules.mk
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ MODULE_DEPS := \
lib/libc \
lib/debug \
lib/heap \
lib/mxtl \


MODULE_SRCS := \
Expand Down
76 changes: 76 additions & 0 deletions system/ulib/mxtl/include/mxtl/auto_lock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#pragma once
#ifdef __cplusplus

#include <mxtl/mutex.h>

// Introduce preprocessor definitions for the underlying mutex data type and the
// lock/unlock operations based on whether this code is being used in the kernel
// or in user-mode. Also, if this is the user-mode AutoLock implementation,
// introduce it into the mxtl namespace. Otherwise, add it to the global
// namespace, and create an alias in mxtl.
#ifdef _KERNEL
#define mxtl_mutex_t mutex_t
#define mxtl_mutex_acquire mutex_acquire
#define mxtl_mutex_release mutex_release
#else
namespace mxtl {
#define mxtl_mutex_t mtx_t
#define mxtl_mutex_acquire mtx_lock
#define mxtl_mutex_release mtx_unlock
#endif

class AutoLock {
public:
explicit AutoLock(mxtl_mutex_t* mutex)
: mutex_(mutex) {
mxtl_mutex_acquire(mutex_);
}

explicit AutoLock(mxtl_mutex_t& mutex)
: AutoLock(&mutex) {}

explicit AutoLock(Mutex& mutex)
: AutoLock(mutex.GetInternal()) {}

explicit AutoLock(Mutex* mutex)
: AutoLock(mutex->GetInternal()) {}

~AutoLock() {
release();
}

// early release the mutex before the object goes out of scope
void release() {
if (mutex_) {
mxtl_mutex_release(mutex_);
mutex_ = nullptr;
}
}

// suppress default constructors
AutoLock(const AutoLock& am) = delete;
AutoLock& operator=(const AutoLock& am) = delete;
AutoLock(AutoLock&& c) = delete;
AutoLock& operator=(AutoLock&& c) = delete;

private:
mxtl_mutex_t* mutex_;
};

#if _KERNEL
namespace mxtl { using AutoLock = ::AutoLock; }
#else
} // namespace mxtl
#endif

// Remove the underlying mutex preprocessor definitions. Do not let them leak
// out into the world at large.
#undef mxtl_mutex_t
#undef mxtl_mutex_acquire
#undef mxtl_mutex_release

#endif // #ifdef __cplusplus
95 changes: 95 additions & 0 deletions system/ulib/mxtl/include/mxtl/mutex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#pragma once

#ifdef __cplusplus

// Notes about class Mutex
//
// Mutex is a C++ helper class intended to wrap a mutex-style synchronization
// primative and provide a common interface for library code which is intended
// to be shared between user-mode and kernel code. It is also responsible for
// automatically initializing and destroying the internal mutex object.
//
// For user-mode code, Mutex is defined in the mxtl namespace. For kernel mode
// code, to maintain compatibility with existing code, Mutex is introduced into
// the global namespace, and a using alias is introduced in the mxtl namespace.
// The implication of this is that shared code should always use mxtl::Mutex
// instead of the global Mutex
#if _KERNEL
#include <kernel/mutex.h>
#include <sys/types.h>

class Mutex {
public:
constexpr Mutex() : mutex_(MUTEX_INITIAL_VALUE(mutex_)) { }
~Mutex() { mutex_destroy(&mutex_); }
void Acquire() { mutex_acquire(&mutex_); }
void Release() { mutex_release(&mutex_); }

status_t AcquireTimeout(lk_time_t timeout) {
return mutex_acquire_timeout(&mutex_, timeout);
}

bool IsHeld() const {
return is_mutex_held(&mutex_);
}

mutex_t* GetInternal() {
return &mutex_;
}

// suppress default constructors
Mutex(const Mutex& am) = delete;
Mutex& operator=(const Mutex& am) = delete;
Mutex(Mutex&& c) = delete;
Mutex& operator=(Mutex&& c) = delete;
private:
mutex_t mutex_;
};

namespace mxtl { using Mutex = ::Mutex; }

#else // if _KERNEL

#include <magenta/types.h>
#include <threads.h>

namespace mxtl {

class Mutex {
public:
constexpr Mutex() : mutex_(MTX_INIT) { }
~Mutex() { mtx_destroy(&mutex_); }
void Acquire() { mtx_lock(&mutex_); }
void Release() { mtx_unlock(&mutex_); }

/* AcquireTimeout is not supported by the Mutex wrapper in user-mode.
* Implementation would require creation of a C11 mtx_timed mutex, a
* decision normally made at construction time. */

/* IsHeld is not supported by the Mutex wrapper in user-mode as C11 mtx_t
* instances do not support a direct IsHeld style check. A possible
* implementation could be built out of mtx_trylock, but would require
* either relaxing away the const constraint on the method signature, or
* flagging the mutex_ member as mutable */

mtx_t* GetInternal() {
return &mutex_;
}

// suppress default constructors
Mutex(const Mutex& am) = delete;
Mutex& operator=(const Mutex& am) = delete;
Mutex(Mutex&& c) = delete;
Mutex& operator=(Mutex&& c) = delete;
private:
mtx_t mutex_;
};

}

#endif // if _KERNEL
#endif // ifdef __cplusplus

0 comments on commit da3a1de

Please sign in to comment.