/** Copyright (c) 2013 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Author: Leonardo de Moura C++11 does not support std::shared_mutex and std::shared_lock yet. This piece of code is based on the proposal submitted to the C++ standardization committee. The proposal was written by Howard Hinnant. The proposal is also part of the Boost library which is licensed under http://www.boost.org/LICENSE_1_0.txt */ #include "util/debug.h" #include "util/shared_mutex.h" namespace lean { shared_mutex::shared_mutex():m_rw_counter(0), m_state(0) {} shared_mutex::~shared_mutex() { std::lock_guard lock(m_mutex); } void shared_mutex::lock() { std::unique_lock lock(m_mutex); if (m_rw_owner == std::this_thread::get_id()) { m_rw_counter++; return; // already has the lock } while (m_state & write_entered) m_gate1.wait(lock); m_state |= write_entered; while (m_state & readers) m_gate2.wait(lock); lean_assert(m_rw_counter == 0); m_rw_owner = std::this_thread::get_id(); m_rw_counter = 1; } bool shared_mutex::try_lock() { std::unique_lock lock(m_mutex); if (m_rw_owner == std::this_thread::get_id()) { m_rw_counter++; return true; // already has the lock } if (m_state == 0) { m_state = write_entered; lean_assert(m_rw_counter == 0); m_rw_owner = std::this_thread::get_id(); m_rw_counter = 1; return true; } return false; } void shared_mutex::unlock() { std::lock_guard lock(m_mutex); lean_assert(m_rw_owner == std::this_thread::get_id()); lean_assert(m_rw_counter > 0); m_rw_counter--; if (m_rw_counter > 0) return; // keep the lock m_rw_owner = std::thread::id(); // reset owner lean_assert(m_rw_counter == 0); lean_assert(m_rw_owner != std::this_thread::get_id()); m_state = 0; m_gate1.notify_all(); } void shared_mutex::lock_shared() { std::unique_lock lock(m_mutex); if (m_rw_owner == std::this_thread::get_id()) { lean_assert(m_rw_counter > 0); m_rw_counter++; return; // already has the lock } while ((m_state & write_entered) || (m_state & readers) == readers) m_gate1.wait(lock); unsigned num_readers = (m_state & readers) + 1; m_state &= ~readers; m_state |= num_readers; } bool shared_mutex::try_lock_shared() { std::unique_lock lock(m_mutex); if (m_rw_owner == std::this_thread::get_id()) { lean_assert(m_rw_counter > 0); m_rw_counter++; return true; // already has the lock } unsigned num_readers = m_state & readers; if (!(m_state & write_entered) && num_readers != readers) { ++num_readers; m_state &= ~readers; m_state |= num_readers; return true; } return false; } void shared_mutex::unlock_shared() { std::lock_guard lock(m_mutex); if (m_rw_owner == std::this_thread::get_id()) { // if we have a rw (aka unshared) lock, then // the shared lock must be nested. lean_assert(m_rw_counter > 1); m_rw_counter--; return; } lean_assert(m_rw_counter == 0); unsigned num_readers = (m_state & readers) - 1; m_state &= ~readers; m_state |= num_readers; if (m_state & write_entered) { if (num_readers == 0) m_gate2.notify_one(); } else { if (num_readers == readers - 1) m_gate1.notify_one(); } } }