//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2014-2017 ArangoDB GmbH, Cologne, Germany /// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany /// /// 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. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Daniel H. Larkin //////////////////////////////////////////////////////////////////////////////// #include "Cache/PlainBucket.h" #include "Basics/Common.h" #include "Cache/CachedValue.h" #include #include using namespace arangodb::cache; PlainBucket::PlainBucket() { _state.lock(); clear(); } bool PlainBucket::lock(uint64_t maxTries) { return _state.lock(maxTries); } void PlainBucket::unlock() { TRI_ASSERT(_state.isLocked()); _state.unlock(); } bool PlainBucket::isLocked() const { return _state.isLocked(); } bool PlainBucket::isMigrated() const { TRI_ASSERT(isLocked()); return _state.isSet(BucketState::Flag::migrated); } bool PlainBucket::isFull() const { TRI_ASSERT(isLocked()); bool hasEmptySlot = false; for (size_t i = 0; i < slotsData; i++) { size_t slot = slotsData - (i + 1); if (_cachedHashes[slot] == 0) { hasEmptySlot = true; break; } } return !hasEmptySlot; } CachedValue* PlainBucket::find(uint32_t hash, void const* key, size_t keySize, bool moveToFront) { TRI_ASSERT(isLocked()); CachedValue* result = nullptr; for (size_t i = 0; i < slotsData; i++) { if (_cachedHashes[i] == 0) { break; } if (_cachedHashes[i] == hash && _cachedData[i]->sameKey(key, keySize)) { result = _cachedData[i]; if (moveToFront) { moveSlot(i, true); } break; } } return result; } // requires there to be an open slot, otherwise will not be inserted void PlainBucket::insert(uint32_t hash, CachedValue* value) { TRI_ASSERT(isLocked()); for (size_t i = 0; i < slotsData; i++) { if (_cachedHashes[i] == 0) { // found an empty slot _cachedHashes[i] = hash; _cachedData[i] = value; if (i != 0) { moveSlot(i, true); } return; } } } CachedValue* PlainBucket::remove(uint32_t hash, void const* key, size_t keySize) { TRI_ASSERT(isLocked()); CachedValue* value = find(hash, key, keySize, false); if (value != nullptr) { evict(value, false); } return value; } CachedValue* PlainBucket::evictionCandidate(bool ignoreRefCount) const { TRI_ASSERT(isLocked()); for (size_t i = 0; i < slotsData; i++) { size_t slot = slotsData - (i + 1); if (_cachedData[slot] == nullptr) { continue; } if (ignoreRefCount || _cachedData[slot]->isFreeable()) { return _cachedData[slot]; } } return nullptr; } void PlainBucket::evict(CachedValue* value, bool optimizeForInsertion) { TRI_ASSERT(isLocked()); for (size_t i = 0; i < slotsData; i++) { size_t slot = slotsData - (i + 1); if (_cachedData[slot] == value) { // found a match _cachedHashes[slot] = 0; _cachedData[slot] = nullptr; moveSlot(slot, optimizeForInsertion); return; } } } void PlainBucket::clear() { TRI_ASSERT(isLocked()); _state.clear(); // "clear" will keep the lock! for (size_t i = 0; i < slotsData; ++i) { _cachedHashes[i] = 0; _cachedData[i] = nullptr; } _state.unlock(); } void PlainBucket::moveSlot(size_t slot, bool moveToFront) { TRI_ASSERT(isLocked()); uint32_t hash = _cachedHashes[slot]; CachedValue* value = _cachedData[slot]; size_t i = slot; if (moveToFront) { // move slot to front for (; i >= 1; i--) { _cachedHashes[i] = _cachedHashes[i - 1]; _cachedData[i] = _cachedData[i - 1]; } } else { // move slot to back for (; (i < slotsData - 1) && (_cachedHashes[i + 1] != 0); i++) { _cachedHashes[i] = _cachedHashes[i + 1]; _cachedData[i] = _cachedData[i + 1]; } } _cachedHashes[i] = hash; _cachedData[i] = value; }