2018-11-27 16:45:45 +00:00
|
|
|
#include "RWLock.h"
|
2017-09-26 17:19:16 +00:00
|
|
|
#include <Common/Stopwatch.h>
|
2017-09-01 15:05:23 +00:00
|
|
|
#include <Common/Exception.h>
|
2017-09-26 17:19:16 +00:00
|
|
|
#include <Common/CurrentMetrics.h>
|
|
|
|
#include <Common/ProfileEvents.h>
|
|
|
|
|
2019-09-01 01:32:44 +00:00
|
|
|
#include <cassert>
|
|
|
|
|
2017-09-26 17:19:16 +00:00
|
|
|
|
|
|
|
namespace ProfileEvents
|
|
|
|
{
|
|
|
|
extern const Event RWLockAcquiredReadLocks;
|
|
|
|
extern const Event RWLockAcquiredWriteLocks;
|
|
|
|
extern const Event RWLockReadersWaitMilliseconds;
|
|
|
|
extern const Event RWLockWritersWaitMilliseconds;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
namespace CurrentMetrics
|
|
|
|
{
|
|
|
|
extern const Metric RWLockWaitingReaders;
|
|
|
|
extern const Metric RWLockWaitingWriters;
|
|
|
|
extern const Metric RWLockActiveReaders;
|
|
|
|
extern const Metric RWLockActiveWriters;
|
|
|
|
}
|
2017-09-01 15:05:23 +00:00
|
|
|
|
2017-08-31 21:11:25 +00:00
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
2017-09-04 12:49:49 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int LOGICAL_ERROR;
|
2019-09-01 01:32:44 +00:00
|
|
|
extern const int DEADLOCK_AVOIDED;
|
2017-09-04 12:49:49 +00:00
|
|
|
}
|
|
|
|
|
2017-08-31 21:11:25 +00:00
|
|
|
|
2019-02-27 18:34:10 +00:00
|
|
|
class RWLockImpl::LockHolderImpl
|
2017-08-31 21:11:25 +00:00
|
|
|
{
|
2018-11-27 16:45:45 +00:00
|
|
|
RWLock parent;
|
2017-08-31 21:11:25 +00:00
|
|
|
GroupsContainer::iterator it_group;
|
2019-09-05 18:09:33 +00:00
|
|
|
String query_id;
|
2017-09-26 17:19:16 +00:00
|
|
|
CurrentMetrics::Increment active_client_increment;
|
|
|
|
|
2019-09-05 15:14:17 +00:00
|
|
|
LockHolderImpl(RWLock && parent, GroupsContainer::iterator it_group);
|
2017-09-26 17:19:16 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
|
2019-02-27 18:34:10 +00:00
|
|
|
LockHolderImpl(const LockHolderImpl & other) = delete;
|
2017-09-26 17:19:16 +00:00
|
|
|
|
2019-02-27 18:34:10 +00:00
|
|
|
~LockHolderImpl();
|
2017-09-26 17:19:16 +00:00
|
|
|
|
2018-11-27 16:45:45 +00:00
|
|
|
friend class RWLockImpl;
|
2017-09-26 17:19:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2019-09-01 01:32:44 +00:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
/// Global information about all read locks that query has. It is needed to avoid some type of deadlocks.
|
|
|
|
|
|
|
|
class QueryLockInfo
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
std::mutex mutex;
|
|
|
|
std::map<std::string, size_t> queries;
|
|
|
|
|
|
|
|
public:
|
|
|
|
void add(const String & query_id)
|
|
|
|
{
|
|
|
|
std::lock_guard lock(mutex);
|
|
|
|
++queries[query_id];
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove(const String & query_id)
|
|
|
|
{
|
|
|
|
std::lock_guard lock(mutex);
|
|
|
|
auto it = queries.find(query_id);
|
|
|
|
assert(it != queries.end());
|
|
|
|
if (--it->second == 0)
|
|
|
|
queries.erase(it);
|
|
|
|
}
|
|
|
|
|
|
|
|
void check(const String & query_id)
|
|
|
|
{
|
|
|
|
std::lock_guard lock(mutex);
|
|
|
|
if (queries.count(query_id))
|
2019-09-01 19:21:00 +00:00
|
|
|
throw Exception("Possible deadlock avoided. Client should retry.", ErrorCodes::DEADLOCK_AVOIDED);
|
2019-09-01 01:32:44 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
QueryLockInfo all_read_locks;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-02-27 18:34:10 +00:00
|
|
|
RWLockImpl::LockHolder RWLockImpl::getLock(RWLockImpl::Type type, const String & query_id)
|
2017-09-26 17:19:16 +00:00
|
|
|
{
|
2019-09-05 12:45:44 +00:00
|
|
|
const bool request_has_query_id = query_id != NO_QUERY;
|
|
|
|
|
2017-09-26 17:19:16 +00:00
|
|
|
Stopwatch watch(CLOCK_MONOTONIC_COARSE);
|
|
|
|
CurrentMetrics::Increment waiting_client_increment((type == Read) ? CurrentMetrics::RWLockWaitingReaders
|
|
|
|
: CurrentMetrics::RWLockWaitingWriters);
|
|
|
|
auto finalize_metrics = [type, &watch] ()
|
|
|
|
{
|
|
|
|
ProfileEvents::increment((type == Read) ? ProfileEvents::RWLockAcquiredReadLocks
|
|
|
|
: ProfileEvents::RWLockAcquiredWriteLocks);
|
|
|
|
ProfileEvents::increment((type == Read) ? ProfileEvents::RWLockReadersWaitMilliseconds
|
|
|
|
: ProfileEvents::RWLockWritersWaitMilliseconds, watch.elapsedMilliseconds());
|
|
|
|
};
|
2017-08-31 21:11:25 +00:00
|
|
|
|
2019-09-02 00:12:01 +00:00
|
|
|
/// This object is placed above unique_lock, because it may lock in destructor.
|
|
|
|
LockHolder res;
|
|
|
|
|
2019-09-06 15:13:22 +00:00
|
|
|
std::unique_lock lock(mutex);
|
2017-08-31 21:11:25 +00:00
|
|
|
|
2019-09-06 15:13:22 +00:00
|
|
|
/// FastPath - Check if the same query_id already holds the required lock
|
|
|
|
/// in which case we can proceed without waiting
|
2019-09-05 12:45:44 +00:00
|
|
|
if (request_has_query_id)
|
2019-09-02 01:04:41 +00:00
|
|
|
{
|
2019-09-05 18:09:33 +00:00
|
|
|
const auto it_query = owner_queries.find(query_id);
|
|
|
|
if (it_query != owner_queries.end())
|
2019-09-05 12:20:10 +00:00
|
|
|
{
|
2019-09-05 18:09:33 +00:00
|
|
|
const auto current_owner_group = queue.begin();
|
|
|
|
|
|
|
|
/// XXX: it means we can't upgrade lock from read to write - with proper waiting!
|
|
|
|
if (type == Write)
|
|
|
|
throw Exception(
|
|
|
|
"RWLockImpl::getLock(): Cannot acquire exclusive lock while RWLock is already locked",
|
|
|
|
ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
|
|
|
if (current_owner_group->type == Write)
|
|
|
|
throw Exception(
|
|
|
|
"RWLockImpl::getLock(): RWLock is already locked in exclusive mode",
|
|
|
|
ErrorCodes::LOGICAL_ERROR);
|
|
|
|
|
|
|
|
res.reset(new LockHolderImpl(shared_from_this(), current_owner_group));
|
|
|
|
|
|
|
|
++owner_queries[query_id];
|
|
|
|
if (type == Type::Read)
|
|
|
|
all_read_locks.add(query_id);
|
|
|
|
|
|
|
|
res->query_id = query_id;
|
|
|
|
|
|
|
|
finalize_metrics();
|
|
|
|
return res;
|
2019-09-05 12:20:10 +00:00
|
|
|
}
|
2017-09-04 12:49:49 +00:00
|
|
|
}
|
|
|
|
|
2019-09-01 01:32:44 +00:00
|
|
|
/** If the query already has any active read lock and tries to acquire another read lock
|
|
|
|
* but it is not in front of the queue and has to wait, deadlock is possible:
|
|
|
|
*
|
|
|
|
* Example (four queries, two RWLocks - 'a' and 'b'):
|
|
|
|
*
|
|
|
|
* --> time -->
|
|
|
|
*
|
|
|
|
* q1: ra rb
|
|
|
|
* q2: wa
|
|
|
|
* q3: rb ra
|
|
|
|
* q4: wb
|
|
|
|
*
|
|
|
|
* We will throw an exception instead.
|
|
|
|
*/
|
|
|
|
|
2019-09-05 12:49:20 +00:00
|
|
|
GroupsContainer::iterator it_group;
|
2017-08-31 21:11:25 +00:00
|
|
|
if (type == Type::Write || queue.empty() || queue.back().type == Type::Write)
|
|
|
|
{
|
2019-09-05 12:49:20 +00:00
|
|
|
if (type == Type::Read && request_has_query_id && !queue.empty())
|
2019-09-01 01:32:44 +00:00
|
|
|
all_read_locks.check(query_id);
|
|
|
|
|
2019-09-05 15:14:17 +00:00
|
|
|
/// Create a new group of locking requests
|
2017-08-31 21:11:25 +00:00
|
|
|
it_group = queue.emplace(queue.end(), type);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-09-05 12:49:20 +00:00
|
|
|
if (type == Type::Read && request_has_query_id && queue.size() > 1)
|
|
|
|
all_read_locks.check(query_id);
|
|
|
|
|
2017-08-31 21:11:25 +00:00
|
|
|
/// Will append myself to last group
|
|
|
|
it_group = std::prev(queue.end());
|
|
|
|
}
|
2019-09-06 15:13:22 +00:00
|
|
|
|
|
|
|
/// LockHolder needs to be created before waiting to guarantee
|
|
|
|
/// that this group does not get deleted (group's referers is incremented)
|
2019-09-05 15:14:17 +00:00
|
|
|
res.reset(new LockHolderImpl(shared_from_this(), it_group));
|
2017-09-04 12:49:49 +00:00
|
|
|
|
2019-05-05 02:01:23 +00:00
|
|
|
/// Wait a notification until we will be the only in the group.
|
|
|
|
it_group->cv.wait(lock, [&] () { return it_group == queue.begin(); });
|
|
|
|
|
2019-09-05 12:45:44 +00:00
|
|
|
if (request_has_query_id)
|
2019-09-01 01:32:44 +00:00
|
|
|
{
|
2019-09-05 18:09:33 +00:00
|
|
|
++owner_queries[query_id];
|
2019-09-01 01:32:44 +00:00
|
|
|
if (type == Type::Read)
|
|
|
|
all_read_locks.add(query_id);
|
2019-09-05 18:09:33 +00:00
|
|
|
|
2019-09-05 15:14:17 +00:00
|
|
|
res->query_id = query_id;
|
2019-09-01 01:32:44 +00:00
|
|
|
}
|
2017-08-31 21:11:25 +00:00
|
|
|
|
2017-09-26 17:19:16 +00:00
|
|
|
finalize_metrics();
|
2017-09-01 15:05:23 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-02-27 18:34:10 +00:00
|
|
|
RWLockImpl::LockHolderImpl::~LockHolderImpl()
|
2017-08-31 21:11:25 +00:00
|
|
|
{
|
2019-09-01 01:32:44 +00:00
|
|
|
std::lock_guard lock(parent->mutex);
|
2017-08-31 21:11:25 +00:00
|
|
|
|
2019-09-05 15:19:36 +00:00
|
|
|
if (query_id != RWLockImpl::NO_QUERY)
|
|
|
|
{
|
2019-09-05 18:09:33 +00:00
|
|
|
if (--parent->owner_queries.at(query_id) == 0)
|
|
|
|
parent->owner_queries.erase(query_id);
|
2017-09-04 12:49:49 +00:00
|
|
|
|
2019-09-05 15:19:36 +00:00
|
|
|
if (it_group->type == RWLockImpl::Read)
|
|
|
|
all_read_locks.remove(query_id);
|
|
|
|
}
|
2019-09-01 01:32:44 +00:00
|
|
|
|
2019-09-05 15:14:17 +00:00
|
|
|
/// Remove the group if we were the last referer and notify the next group
|
|
|
|
if (--it_group->referers == 0)
|
2017-08-31 21:11:25 +00:00
|
|
|
{
|
2018-08-26 02:08:35 +00:00
|
|
|
auto & parent_queue = parent->queue;
|
|
|
|
parent_queue.erase(it_group);
|
2017-08-31 21:11:25 +00:00
|
|
|
|
2018-08-26 02:08:35 +00:00
|
|
|
if (!parent_queue.empty())
|
|
|
|
parent_queue.front().cv.notify_all();
|
2017-08-31 21:11:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-05 15:14:17 +00:00
|
|
|
RWLockImpl::LockHolderImpl::LockHolderImpl(RWLock && parent_, RWLockImpl::GroupsContainer::iterator it_group_)
|
|
|
|
: parent{std::move(parent_)}, it_group{it_group_},
|
|
|
|
active_client_increment{(it_group_->type == RWLockImpl::Read) ? CurrentMetrics::RWLockActiveReaders
|
2018-11-28 15:50:52 +00:00
|
|
|
: CurrentMetrics::RWLockActiveWriters}
|
2019-09-01 01:32:44 +00:00
|
|
|
{
|
2019-09-06 15:13:22 +00:00
|
|
|
++it_group->referers;
|
2019-09-01 01:32:44 +00:00
|
|
|
}
|
2017-08-31 21:11:25 +00:00
|
|
|
|
|
|
|
}
|