mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 23:21:59 +00:00
Updated RWLockImpl: Fixed suboptimacy (readers queuing up) + minor tweaks to names and comments (#10303)
* Readers queue correction * Comments, renamings and cosmetics
This commit is contained in:
parent
80873d79e3
commit
d5b3b2c7b6
@ -154,23 +154,17 @@ RWLockImpl::getLock(RWLockImpl::Type type, const String & query_id, const std::c
|
|||||||
writers_queue.emplace_back(type); /// SM1: may throw (nothing to roll back)
|
writers_queue.emplace_back(type); /// SM1: may throw (nothing to roll back)
|
||||||
}
|
}
|
||||||
else if (readers_queue.empty() ||
|
else if (readers_queue.empty() ||
|
||||||
(rdlock_owner == readers_queue.begin() && !writers_queue.empty()))
|
(rdlock_owner == readers_queue.begin() && readers_queue.size() == 1 && !writers_queue.empty()))
|
||||||
{
|
{
|
||||||
readers_queue.emplace_back(type); /// SM1: may throw (nothing to roll back)
|
readers_queue.emplace_back(type); /// SM1: may throw (nothing to roll back)
|
||||||
}
|
}
|
||||||
GroupsContainer::iterator it_group =
|
GroupsContainer::iterator it_group =
|
||||||
(type == Type::Write) ? std::prev(writers_queue.end()) : std::prev(readers_queue.end());
|
(type == Type::Write) ? std::prev(writers_queue.end()) : std::prev(readers_queue.end());
|
||||||
|
|
||||||
|
/// Lock is free to acquire
|
||||||
if (rdlock_owner == readers_queue.end() && wrlock_owner == writers_queue.end())
|
if (rdlock_owner == readers_queue.end() && wrlock_owner == writers_queue.end())
|
||||||
{
|
{
|
||||||
if (type == Type::Read)
|
(type == Read ? rdlock_owner : wrlock_owner) = it_group; /// SM2: nothrow
|
||||||
{
|
|
||||||
rdlock_owner = it_group; /// SM2: nothrow
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wrlock_owner = it_group; /// SM2: nothrow
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -192,17 +186,10 @@ RWLockImpl::getLock(RWLockImpl::Type type, const String & query_id, const std::c
|
|||||||
/// Step 3a. Check if we must handle timeout and exit
|
/// Step 3a. Check if we must handle timeout and exit
|
||||||
if (!wait_result) /// Wait timed out!
|
if (!wait_result) /// Wait timed out!
|
||||||
{
|
{
|
||||||
|
/// Rollback(SM1): nothrow
|
||||||
if (it_group->requests == 0)
|
if (it_group->requests == 0)
|
||||||
{
|
{
|
||||||
/// Roll back SM1
|
(type == Read ? readers_queue : writers_queue).erase(it_group);
|
||||||
if (type == Read)
|
|
||||||
{
|
|
||||||
readers_queue.erase(it_group); /// Rollback(SM1): nothrow
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
writers_queue.erase(it_group); /// Rollback(SM1): nothrow
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -224,7 +211,7 @@ RWLockImpl::getLock(RWLockImpl::Type type, const String & query_id, const std::c
|
|||||||
/// Methods std::list<>::emplace_back() and std::unordered_map<>::emplace() provide strong exception safety
|
/// Methods std::list<>::emplace_back() and std::unordered_map<>::emplace() provide strong exception safety
|
||||||
/// We only need to roll back the changes to these objects: owner_queries and the readers/writers queue
|
/// We only need to roll back the changes to these objects: owner_queries and the readers/writers queue
|
||||||
if (it_group->requests == 0)
|
if (it_group->requests == 0)
|
||||||
eraseGroup(it_group); /// Rollback(SM1): nothrow
|
dropOwnerGroupAndPassOwnership(it_group); /// Rollback(SM1): nothrow
|
||||||
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
@ -272,11 +259,11 @@ void RWLockImpl::unlock(GroupsContainer::iterator group_it, const String & query
|
|||||||
|
|
||||||
/// If we are the last remaining referrer, remove this QNode and notify the next one
|
/// If we are the last remaining referrer, remove this QNode and notify the next one
|
||||||
if (--group_it->requests == 0) /// SM: nothrow
|
if (--group_it->requests == 0) /// SM: nothrow
|
||||||
eraseGroup(group_it);
|
dropOwnerGroupAndPassOwnership(group_it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void RWLockImpl::eraseGroup(GroupsContainer::iterator group_it) noexcept
|
void RWLockImpl::dropOwnerGroupAndPassOwnership(GroupsContainer::iterator group_it) noexcept
|
||||||
{
|
{
|
||||||
rdlock_owner = readers_queue.end();
|
rdlock_owner = readers_queue.end();
|
||||||
wrlock_owner = writers_queue.end();
|
wrlock_owner = writers_queue.end();
|
||||||
|
@ -19,9 +19,15 @@ class RWLockImpl;
|
|||||||
using RWLock = std::shared_ptr<RWLockImpl>;
|
using RWLock = std::shared_ptr<RWLockImpl>;
|
||||||
|
|
||||||
|
|
||||||
/// Implements shared lock with FIFO service
|
/// Implements Readers-Writers locking algorithm that serves requests in "Phase Fair" order.
|
||||||
/// (Phase Fair RWLock as suggested in https://www.cs.unc.edu/~anderson/papers/rtsj10-for-web.pdf)
|
/// (Phase Fair RWLock as suggested in https://www.cs.unc.edu/~anderson/papers/rtsj10-for-web.pdf)
|
||||||
/// Can be acquired recursively (for the same query) in Read mode
|
/// It is used for synchronizing access to various objects on query level (i.e. Storages).
|
||||||
|
///
|
||||||
|
/// In general, ClickHouse processes queries by multiple threads of execution in parallel.
|
||||||
|
/// As opposed to the standard OS synchronization primitives (mutexes), this implementation allows
|
||||||
|
/// unlock() to be called by a thread other than the one, that called lock().
|
||||||
|
/// It is also possible to acquire RWLock in Read mode without waiting (FastPath) by multiple threads,
|
||||||
|
/// that execute the same query (share the same query_id).
|
||||||
///
|
///
|
||||||
/// NOTE: it is important to allow acquiring the same lock in Read mode without waiting if it is already
|
/// NOTE: it is important to allow acquiring the same lock in Read mode without waiting if it is already
|
||||||
/// acquired by another thread of the same query. Otherwise the following deadlock is possible:
|
/// acquired by another thread of the same query. Otherwise the following deadlock is possible:
|
||||||
@ -82,6 +88,6 @@ private:
|
|||||||
private:
|
private:
|
||||||
RWLockImpl() = default;
|
RWLockImpl() = default;
|
||||||
void unlock(GroupsContainer::iterator group_it, const String & query_id) noexcept;
|
void unlock(GroupsContainer::iterator group_it, const String & query_id) noexcept;
|
||||||
void eraseGroup(GroupsContainer::iterator group_it) noexcept;
|
void dropOwnerGroupAndPassOwnership(GroupsContainer::iterator group_it) noexcept;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user