/** * Copyright (C) 2009-2014 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. */ #pragma once #include #include "mongo/db/storage/mmap_v1/durop.h" #include "mongo/util/concurrency/mutex.h" namespace mongo { namespace dur { typedef std::vector > DurOpsVector; /** * Declaration of an intent to write to a region of a memory mapped view. We store the end * rather than the start pointer to make operator < faster since that is heavily used in * set lookup. */ struct WriteIntent { WriteIntent() : p(0) { } WriteIntent(void *a, unsigned b) : p((char*)a + b), len(b) { } void* start() const { return (char*)p - len; } void* end() const { return p; } unsigned length() const { return len; } bool operator < (const WriteIntent& rhs) const { return end() < rhs.end(); } bool overlaps(const WriteIntent& rhs) const { return (start() <= rhs.end() && end() >= rhs.start()); } bool contains(const WriteIntent& rhs) const { return (start() <= rhs.start() && end() >= rhs.end()); } // merge into me: void absorb(const WriteIntent& other); friend std::ostream& operator << (std::ostream& out, const WriteIntent& wi) { return (out << "p: " << wi.p << " end: " << wi.end() << " len: " << wi.len); } private: void *p; // intent to write up to p unsigned len; // up to this len }; typedef std::vector WriteIntentsVector; /** * Bitmap to remember things we have already marked for journaling. False negatives are ok * if infrequent, since they impact performance. */ template class Already { MONGO_DISALLOW_COPYING(Already); public: Already() { clear(); } void clear() { memset(this, 0, sizeof(*this)); } /** * Checks if we have Already recorded/indicated our write intent for this region of * memory and automatically upgrades the length if the length was shorter previously. * * @return true if already indicated. */ bool checkAndSet(void* p, int len) { const unsigned x = hashPointer(p); std::pair& nd = nodes[x % Prime]; if (nd.first == p) { if (nd.second < len) { nd.second = len; return false; // haven't indicated this len yet } return true; // already indicated } nd.first = p; nd.second = len; return false; // a new set } private: static unsigned hashPointer(void *v) { unsigned x = 0; unsigned char *p = (unsigned char *)&v; for (unsigned i = 0; i < sizeof(void*); i++) { x = x * 131 + p[i]; } return x; } std::pair nodes[Prime]; }; /** * Tracks all write operations on the private view so they can be journaled. */ class CommitJob { MONGO_DISALLOW_COPYING(CommitJob); public: CommitJob(); ~CommitJob(); /** * Note an operation other than a "basic write". */ void noteOp(boost::shared_ptr p); /** * Record/note an intent to write. * * NOTE: Not thread safe. Requires the mutex to be locked. */ void note(void* p, int len); /** * When this value is false we don't have to do any group commit. */ bool hasWritten() const { return _hasWritten; } /** * We use the commitjob object over and over, calling committingReset() rather than * reconstructing. */ void committingReset(); /** * We check how much written and if it is getting to be a lot, we commit sooner. */ size_t bytes() const { return _bytes; } /** * Sorts the internal list of write intents so that overlapping and duplicate items can be * merged. We do the sort here so the caller receives something they must keep const from * their POV. */ const WriteIntentsVector& getIntentsSorted() { sort(_intents.begin(), _intents.end()); return _intents; } const DurOpsVector& ops() const { return _durOps; } SimpleMutex groupCommitMutex; private: void _insertWriteIntent(void* p, int len) { _intents.push_back(WriteIntent(p, len)); wassert(_intents.size() < 2000000); } // Whether we put write intents or durops bool _hasWritten; // Write intents along with a bitmask for whether we have already noted them Already<127> _alreadyNoted; WriteIntentsVector _intents; // All the ops other than basic writes DurOpsVector _durOps; // Used to count the private map used bytes. Note that _lastNotedPos doesn't reset with // each commit, but that is ok we aren't being that precise. size_t _lastNotedPos; size_t _bytes; // Warning logging for large commits uint64_t _lastComplainMs; unsigned _complains; }; } // namespace "dur" } // namespace "mongo"