175 lines
4.9 KiB
C
175 lines
4.9 KiB
C
|
/*
|
||
|
Copyright (c) 2005-2022 Intel Corporation
|
||
|
|
||
|
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
|
||
|
|
||
|
http://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.
|
||
|
*/
|
||
|
|
||
|
#ifndef __TBB_detail_scoped_lock_H
|
||
|
#define __TBB_detail_scoped_lock_H
|
||
|
|
||
|
namespace tbb {
|
||
|
namespace detail {
|
||
|
namespace d1 {
|
||
|
|
||
|
// unique_scoped_lock supposes that Mutex operations never throw
|
||
|
template <typename Mutex>
|
||
|
class unique_scoped_lock {
|
||
|
//! Points to currently held Mutex, or nullptr if no lock is held.
|
||
|
Mutex* m_mutex{};
|
||
|
|
||
|
public:
|
||
|
//! Construct without acquiring a Mutex.
|
||
|
constexpr unique_scoped_lock() noexcept : m_mutex(nullptr) {}
|
||
|
|
||
|
//! Construct and acquire lock on a Mutex.
|
||
|
unique_scoped_lock(Mutex& m) {
|
||
|
acquire(m);
|
||
|
}
|
||
|
|
||
|
//! No Copy
|
||
|
unique_scoped_lock(const unique_scoped_lock&) = delete;
|
||
|
unique_scoped_lock& operator=(const unique_scoped_lock&) = delete;
|
||
|
|
||
|
//! Acquire lock.
|
||
|
void acquire(Mutex& m) {
|
||
|
__TBB_ASSERT(m_mutex == nullptr, "The mutex is already acquired");
|
||
|
m_mutex = &m;
|
||
|
m.lock();
|
||
|
}
|
||
|
|
||
|
//! Try acquiring lock (non-blocking)
|
||
|
/** Return true if lock acquired; false otherwise. */
|
||
|
bool try_acquire(Mutex& m) {
|
||
|
__TBB_ASSERT(m_mutex == nullptr, "The mutex is already acquired");
|
||
|
bool succeed = m.try_lock();
|
||
|
if (succeed) {
|
||
|
m_mutex = &m;
|
||
|
}
|
||
|
return succeed;
|
||
|
}
|
||
|
|
||
|
//! Release lock
|
||
|
void release() {
|
||
|
__TBB_ASSERT(m_mutex, "release on Mutex::unique_scoped_lock that is not holding a lock");
|
||
|
m_mutex->unlock();
|
||
|
m_mutex = nullptr;
|
||
|
}
|
||
|
|
||
|
//! Destroy lock. If holding a lock, releases the lock first.
|
||
|
~unique_scoped_lock() {
|
||
|
if (m_mutex) {
|
||
|
release();
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// rw_scoped_lock supposes that Mutex operations never throw
|
||
|
template <typename Mutex>
|
||
|
class rw_scoped_lock {
|
||
|
public:
|
||
|
//! Construct lock that has not acquired a mutex.
|
||
|
/** Equivalent to zero-initialization of *this. */
|
||
|
constexpr rw_scoped_lock() noexcept {}
|
||
|
|
||
|
//! Acquire lock on given mutex.
|
||
|
rw_scoped_lock(Mutex& m, bool write = true) {
|
||
|
acquire(m, write);
|
||
|
}
|
||
|
|
||
|
//! Release lock (if lock is held).
|
||
|
~rw_scoped_lock() {
|
||
|
if (m_mutex) {
|
||
|
release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! No Copy
|
||
|
rw_scoped_lock(const rw_scoped_lock&) = delete;
|
||
|
rw_scoped_lock& operator=(const rw_scoped_lock&) = delete;
|
||
|
|
||
|
//! Acquire lock on given mutex.
|
||
|
void acquire(Mutex& m, bool write = true) {
|
||
|
__TBB_ASSERT(m_mutex == nullptr, "The mutex is already acquired");
|
||
|
m_is_writer = write;
|
||
|
m_mutex = &m;
|
||
|
if (write) {
|
||
|
m_mutex->lock();
|
||
|
} else {
|
||
|
m_mutex->lock_shared();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! Try acquire lock on given mutex.
|
||
|
bool try_acquire(Mutex& m, bool write = true) {
|
||
|
bool succeed = write ? m.try_lock() : m.try_lock_shared();
|
||
|
if (succeed) {
|
||
|
m_mutex = &m;
|
||
|
m_is_writer = write;
|
||
|
}
|
||
|
return succeed;
|
||
|
}
|
||
|
|
||
|
//! Release lock.
|
||
|
void release() {
|
||
|
__TBB_ASSERT(m_mutex != nullptr, "The mutex is not acquired");
|
||
|
Mutex* m = m_mutex;
|
||
|
m_mutex = nullptr;
|
||
|
|
||
|
if (m_is_writer) {
|
||
|
m->unlock();
|
||
|
} else {
|
||
|
m->unlock_shared();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! Upgrade reader to become a writer.
|
||
|
/** Returns whether the upgrade happened without releasing and re-acquiring the lock */
|
||
|
bool upgrade_to_writer() {
|
||
|
__TBB_ASSERT(m_mutex != nullptr, "The mutex is not acquired");
|
||
|
if (m_is_writer) {
|
||
|
return true; // Already a writer
|
||
|
}
|
||
|
m_is_writer = true;
|
||
|
return m_mutex->upgrade();
|
||
|
}
|
||
|
|
||
|
//! Downgrade writer to become a reader.
|
||
|
bool downgrade_to_reader() {
|
||
|
__TBB_ASSERT(m_mutex != nullptr, "The mutex is not acquired");
|
||
|
if (m_is_writer) {
|
||
|
m_mutex->downgrade();
|
||
|
m_is_writer = false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool is_writer() const {
|
||
|
__TBB_ASSERT(m_mutex != nullptr, "The mutex is not acquired");
|
||
|
return m_is_writer;
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
//! The pointer to the current mutex that is held, or nullptr if no mutex is held.
|
||
|
Mutex* m_mutex {nullptr};
|
||
|
|
||
|
//! If mutex != nullptr, then is_writer is true if holding a writer lock, false if holding a reader lock.
|
||
|
/** Not defined if not holding a lock. */
|
||
|
bool m_is_writer {false};
|
||
|
};
|
||
|
|
||
|
} // namespace d1
|
||
|
} // namespace detail
|
||
|
} // namespace tbb
|
||
|
|
||
|
#endif // __TBB_detail_scoped_lock_H
|