blob: 77986f6922e78931342a0e8c7553b969db7525c4 [file] [log] [blame]
/**
* Copyright 2016, Quickstep Research Group, Computer Sciences Department,
* University of Wisconsin—Madison.
*
* 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.
**/
#include "transaction/LockTable.hpp"
#include <list>
#include <unordered_map>
#include <utility>
#include "threading/SharedMutex.hpp"
#include "transaction/AccessMode.hpp"
#include "transaction/Lock.hpp"
#include "transaction/ResourceId.hpp"
#include "transaction/Transaction.hpp"
#include "utility/Macros.hpp"
namespace quickstep {
namespace transaction {
LockTableResult
LockTable::putLock(const transaction_id tid,
const ResourceId &rid,
const AccessMode access_mode) {
// TODO(hakan): Lock upgrade is not supported.
lock_list_pair &lock_list_pair = internal_map_[rid];
// Each resource id entry has own list and pending list.
lock_own_list &lock_own_list = lock_list_pair.first;
lock_pending_list &lock_pending_list = lock_list_pair.second;
// Check this resource id already has the same lock request from
// the same transaction in the own list.
for (lock_own_list::const_iterator it = lock_own_list.cbegin();
it != lock_own_list.cend(); ++it) {
if (it->first == tid && it->second.getAccessMode() == access_mode) {
return LockTableResult::kALREADY_IN_OWNED;
}
}
// Check this resource id already has the same lock request from
// the same transaction in the pending list.
for (lock_pending_list::const_iterator it = lock_pending_list.cbegin();
it != lock_pending_list.cend(); ++it) {
if (it->first == tid && it->second.getAccessMode() == access_mode) {
return LockTableResult::kALREADY_IN_PENDING;
}
}
// If the execution can reach this point, it means the resource id
// does not have duplicate lock record (for both in owned and pending).
if (lock_pending_list.empty()) {
for (lock_own_list::const_iterator it = lock_own_list.cbegin();
it != lock_own_list.cend(); ++it) {
if (!access_mode.isCompatible(it->second.getAccessMode())) {
lock_pending_list.push_back(std::make_pair(tid,
Lock(rid, access_mode)));
return LockTableResult::kPLACED_IN_PENDING;
}
}
lock_own_list.push_back(std::make_pair(tid, Lock(rid, access_mode)));
return LockTableResult::kPLACED_IN_OWNED;
} else {
// If the pending list is not empty, even if the lock request is compatible
// with other owned lock entries, we put the new request into the pending
// list to eliminate starvation.
lock_pending_list.push_back(std::make_pair(tid, Lock(rid, access_mode)));
return LockTableResult::kPLACED_IN_PENDING;
}
}
LockTableResult
LockTable::deleteLock(const transaction_id tid,
const ResourceId &rid) {
lock_list_pair &lock_list_pair = internal_map_[rid];
// Each resource id has its own and pending locks list.
lock_own_list &lock_own_list = lock_list_pair.first;
lock_pending_list &lock_pending_list = lock_list_pair.second;
// Iterate over owned locks list to see the lock entry of the transaction
// on the resource id exists.
for (lock_own_list::const_iterator it = lock_own_list.begin();
it != lock_own_list.cend(); ++it) {
if (it->first == tid) {
// If it exists, erase it from the owned list.
lock_own_list.erase(it);
// Since we erased a lock entry from owned list, the first entries
// in the pending list can be pushed to owned list if they are
// compatible with the remaining owned entries.
movePendingToOwned(rid);
return LockTableResult::kDEL_FROM_OWNED;
}
}
// Iterate over pending locks list to check the lock entry of the transaction
// on this resource id exists.
for (lock_pending_list::const_iterator it = lock_pending_list.begin();
it != lock_pending_list.cend(); ++it) {
if (it->first == tid) {
// If it exists, erase it from pending list.
lock_pending_list.erase(it);
return LockTableResult::kDEL_FROM_PENDING;
}
}
// Execution reaches here, if we cannot find the corresponding lock entry
// in the both list.
return LockTableResult::kDEL_ERROR;
}
void LockTable::movePendingToOwned(const ResourceId &rid) {
lock_list_pair &lock_list_pair = internal_map_[rid];
lock_own_list &lock_own_list = lock_list_pair.first;
lock_pending_list &lock_pending_list = lock_list_pair.second;
// Iterate over pending list to pending requests compatible with the
// all entries in the resource ids owned lock list.
for (lock_pending_list::const_iterator pending_it = lock_pending_list.cbegin();
pending_it != lock_pending_list.cend(); ++pending_it) {
transaction_id pending_tid = pending_it->first;
AccessMode pending_mode = pending_it->second.getAccessMode();
bool is_compatible_with_own_list = true;
// Now compare against the all entries in the owned lock list.
for (lock_own_list::const_iterator owned_it = lock_own_list.cbegin();
owned_it != lock_pending_list.cend(); ++owned_it) {
AccessMode owned_mode = owned_it->second.getAccessMode();
if (!pending_mode.isCompatible(owned_mode)) {
// If it is not compatible, we will not move this entry.
is_compatible_with_own_list = false;
break;
}
}
// If this pending lock entry is compatible with the all entries in the
// owned lock list, we should move it from pending list to owned list.
if (is_compatible_with_own_list) {
// Erase the entry from the list. Get the new iterator.
pending_it = lock_pending_list.erase(pending_it);
// Put the corresponding entry to the own list.
lock_own_list.emplace_back(pending_tid, Lock(rid, pending_mode));
// Move iterator one step backward because erasing an element moves the
// iterator to the next element.
--pending_it;
} else {
// There is no need to iterate pending list anymore since
// we found first incompatible one. Checking, and accepting other pending
// entries may cause starvation.
break;
}
}
}
LockTable::iterator LockTable::begin() {
return internal_map_.begin();
}
LockTable::iterator LockTable::end() {
return internal_map_.end();
}
LockTable::const_iterator LockTable::begin() const {
return internal_map_.begin();
}
LockTable::const_iterator LockTable::end() const {
return internal_map_.end();
}
void LockTable::latchShared() {
mutex_.lockShared();
}
void LockTable::unlatchShared() {
mutex_.unlockShared();
}
void LockTable::latchExclusive() {
mutex_.lock();
}
void LockTable::unlatchExclusive() {
mutex_.unlock();
}
} // namespace transaction
} // namespace quickstep