/** * Copyright (C) 2013 MongoDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #include "mongo/db/exec/working_set.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/storage/record_fetcher.h" namespace mongo { using std::string; WorkingSet::MemberHolder::MemberHolder() : member(NULL) { } WorkingSet::MemberHolder::~MemberHolder() {} WorkingSet::WorkingSet() : _freeList(INVALID_ID) { } WorkingSet::~WorkingSet() { for (size_t i = 0; i < _data.size(); i++) { delete _data[i].member; } } WorkingSetID WorkingSet::allocate() { if (_freeList == INVALID_ID) { // The free list is empty so we need to make a single new WSM to return. This relies on // vector::resize being amortized O(1) for efficient allocation. Note that the free list // remains empty until something is returned by a call to free(). WorkingSetID id = _data.size(); _data.resize(_data.size() + 1); _data.back().nextFreeOrSelf = id; _data.back().member = new WorkingSetMember(); return id; } // Pop the head off the free list and return it. WorkingSetID id = _freeList; _freeList = _data[id].nextFreeOrSelf; _data[id].nextFreeOrSelf = id; // set to self to mark as in-use return id; } void WorkingSet::free(const WorkingSetID& i) { MemberHolder& holder = _data[i]; verify(i < _data.size()); // ID has been allocated. verify(holder.nextFreeOrSelf == i); // ID currently in use. // Free resources and push this WSM to the head of the freelist. holder.member->clear(); holder.nextFreeOrSelf = _freeList; _freeList = i; } void WorkingSet::flagForReview(const WorkingSetID& i) { WorkingSetMember* member = get(i); verify(WorkingSetMember::OWNED_OBJ == member->state); _flagged.insert(i); } const unordered_set& WorkingSet::getFlagged() const { return _flagged; } bool WorkingSet::isFlagged(WorkingSetID id) const { invariant(id < _data.size()); return _flagged.end() != _flagged.find(id); } void WorkingSet::clear() { for (size_t i = 0; i < _data.size(); i++) { delete _data[i].member; } _data.clear(); // Since working set is now empty, the free list pointer should // point to nothing. _freeList = INVALID_ID; _flagged.clear(); } // // Iteration // WorkingSet::iterator::iterator(WorkingSet* ws, size_t index) : _ws(ws), _index(index) { // If we're currently not pointing at an allocated member, then we have // to advance to the first one, unless we're already at the end. if (_index < _ws->_data.size() && isFree()) { advance(); } } void WorkingSet::iterator::advance() { // Move forward at least once in the data list. _index++; // While we haven't hit the end and the current member is not in use. (Skips ahead until // we find the next allocated member.) while (_index < _ws->_data.size() && isFree()) { _index++; } } bool WorkingSet::iterator::isFree() const { return _ws->_data[_index].nextFreeOrSelf != _index; } void WorkingSet::iterator::free() { dassert(!isFree()); _ws->free(_index); } void WorkingSet::iterator::operator++() { dassert(_index < _ws->_data.size()); advance(); } bool WorkingSet::iterator::operator==(const WorkingSet::iterator& other) const { return (_index == other._index); } bool WorkingSet::iterator::operator!=(const WorkingSet::iterator& other) const { return (_index != other._index); } WorkingSetMember& WorkingSet::iterator::operator*() { dassert(_index < _ws->_data.size() && !isFree()); return *_ws->_data[_index].member; } WorkingSetMember* WorkingSet::iterator::operator->() { dassert(_index < _ws->_data.size() && !isFree()); return _ws->_data[_index].member; } WorkingSet::iterator WorkingSet::begin() { return WorkingSet::iterator(this, 0); } WorkingSet::iterator WorkingSet::end() { return WorkingSet::iterator(this, _data.size()); } // // WorkingSetMember // WorkingSetMember::WorkingSetMember() : state(WorkingSetMember::INVALID), isSuspicious(false) { } WorkingSetMember::~WorkingSetMember() { } void WorkingSetMember::clear() { for (size_t i = 0; i < WSM_COMPUTED_NUM_TYPES; i++) { _computed[i].reset(); } keyData.clear(); obj.reset(); state = WorkingSetMember::INVALID; } bool WorkingSetMember::hasLoc() const { return state == LOC_AND_IDX || state == LOC_AND_UNOWNED_OBJ || state == LOC_AND_OWNED_OBJ; } bool WorkingSetMember::hasObj() const { return hasOwnedObj() || hasUnownedObj(); } bool WorkingSetMember::hasOwnedObj() const { return state == OWNED_OBJ || state == LOC_AND_OWNED_OBJ; } bool WorkingSetMember::hasUnownedObj() const { return state == LOC_AND_UNOWNED_OBJ; } bool WorkingSetMember::hasComputed(const WorkingSetComputedDataType type) const { return _computed[type].get(); } const WorkingSetComputedData* WorkingSetMember::getComputed(const WorkingSetComputedDataType type) const { verify(_computed[type]); return _computed[type].get(); } void WorkingSetMember::addComputed(WorkingSetComputedData* data) { verify(!hasComputed(data->type())); _computed[data->type()].reset(data); } void WorkingSetMember::setFetcher(RecordFetcher* fetcher) { _fetcher.reset(fetcher); } RecordFetcher* WorkingSetMember::releaseFetcher() { return _fetcher.release(); } bool WorkingSetMember::hasFetcher() const { return NULL != _fetcher.get(); } bool WorkingSetMember::getFieldDotted(const string& field, BSONElement* out) const { // If our state is such that we have an object, use it. if (hasObj()) { *out = obj.value().getFieldDotted(field); return true; } // Our state should be such that we have index data/are covered. for (size_t i = 0; i < keyData.size(); ++i) { BSONObjIterator keyPatternIt(keyData[i].indexKeyPattern); BSONObjIterator keyDataIt(keyData[i].keyData); while (keyPatternIt.more()) { BSONElement keyPatternElt = keyPatternIt.next(); verify(keyDataIt.more()); BSONElement keyDataElt = keyDataIt.next(); if (field == keyPatternElt.fieldName()) { *out = keyDataElt; return true; } } } return false; } size_t WorkingSetMember::getMemUsage() const { size_t memUsage = 0; if (hasLoc()) { memUsage += sizeof(RecordId); } // XXX: Unowned objects count towards current size. // See SERVER-12579 if (hasObj()) { memUsage += obj.value().objsize(); } for (size_t i = 0; i < keyData.size(); ++i) { const IndexKeyDatum& keyDatum = keyData[i]; memUsage += keyDatum.keyData.objsize(); } return memUsage; } } // namespace mongo