blob: 72e735f6996f3338ac72b5f7c6b118d79c46976e [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 <utility>
#include "transaction/AccessMode.hpp"
#include "transaction/Lock.hpp"
#include "transaction/Transaction.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::kAlreadyInOwned;
}
}
// 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::kAlreadyInPending;
}
}
// 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::kPlacedInPending;
}
}
lock_own_list.push_back(std::make_pair(tid, Lock(rid, access_mode)));
return LockTableResult::kPlacedInOwned;
} 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::kPlacedInPending;
}
}
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::kDeleteFromOwned;
}
}
// 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::kDeleteFromPending;
}
}
// Execution reaches here, if we cannot find the corresponding lock entry
// in the both list.
return LockTableResult::kDeleteError;
}
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