diff options
Diffstat (limited to 'src/mongo/util/mmap.h')
-rw-r--r-- | src/mongo/util/mmap.h | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/src/mongo/util/mmap.h b/src/mongo/util/mmap.h new file mode 100644 index 00000000000..2d4454bbc7f --- /dev/null +++ b/src/mongo/util/mmap.h @@ -0,0 +1,305 @@ +// mmap.h + +/* Copyright 2009 10gen Inc. + * + * 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. + */ + +#pragma once +#include <boost/thread/xtime.hpp> +#include "concurrency/rwlock.h" + +namespace mongo { + + class MAdvise { + void *_p; + unsigned _len; + public: + enum Advice { Sequential=1 }; + MAdvise(void *p, unsigned len, Advice a); + ~MAdvise(); // destructor resets the range to MADV_NORMAL + }; + + // lock order: lock dbMutex before this if you lock both + class LockMongoFilesShared { + friend class LockMongoFilesExclusive; + static RWLockRecursiveNongreedy mmmutex; + static unsigned era; + RWLockRecursive::Shared lk; + public: + LockMongoFilesShared() : lk(mmmutex) { } + + /** era changes anytime memory maps come and go. thus you can use this as a cheap way to verify + that things are still in the condition you expected. of course you must be shared locked + otherwise someone could be in progress. if you have unlocked this is a reasonable way to + check your memory mapped pointer is still good. + */ + static unsigned getEra() { return era; } + + static void assertExclusivelyLocked() { mmmutex.assertExclusivelyLocked(); } + }; + + class LockMongoFilesExclusive { + RWLockRecursive::Exclusive lk; + public: + LockMongoFilesExclusive() : lk(LockMongoFilesShared::mmmutex) { + LockMongoFilesShared::era++; + } + }; + + /* the administrative-ish stuff here */ + class MongoFile : boost::noncopyable { + public: + /** Flushable has to fail nicely if the underlying object gets killed */ + class Flushable { + public: + virtual ~Flushable() {} + virtual void flush() = 0; + }; + + virtual ~MongoFile() {} + + enum Options { + SEQUENTIAL = 1, // hint - e.g. FILE_FLAG_SEQUENTIAL_SCAN on windows + READONLY = 2 // not contractually guaranteed, but if specified the impl has option to fault writes + }; + + /** @param fun is called for each MongoFile. + calledl from within a mutex that MongoFile uses. so be careful not to deadlock. + */ + template < class F > + static void forEach( F fun ); + + /** note: you need to be in mmmutex when using this. forEach (above) handles that for you automatically. +*/ + static set<MongoFile*>& getAllFiles() { return mmfiles; } + + // callbacks if you need them + static void (*notifyPreFlush)(); + static void (*notifyPostFlush)(); + + static int flushAll( bool sync ); // returns n flushed + static long long totalMappedLength(); + static void closeAllFiles( stringstream &message ); + +#if defined(_DEBUG) + static void markAllWritable(); + static void unmarkAllWritable(); +#else + static void markAllWritable() { } + static void unmarkAllWritable() { } +#endif + + static bool exists(boost::filesystem::path p) { return boost::filesystem::exists(p); } + + virtual bool isMongoMMF() { return false; } + + string filename() const { return _filename; } + void setFilename(string fn); + + private: + string _filename; + static int _flushAll( bool sync ); // returns n flushed + protected: + virtual void close() = 0; + virtual void flush(bool sync) = 0; + /** + * returns a thread safe object that you can call flush on + * Flushable has to fail nicely if the underlying object gets killed + */ + virtual Flushable * prepareFlush() = 0; + + void created(); /* subclass must call after create */ + + /* subclass must call in destructor (or at close). + removes this from pathToFile and other maps + safe to call more than once, albeit might be wasted work + ideal to call close to the close, if the close is well before object destruction + */ + void destroyed(); + + virtual unsigned long long length() const = 0; + + // only supporting on posix mmap + virtual void _lock() {} + virtual void _unlock() {} + + static set<MongoFile*> mmfiles; + public: + static map<string,MongoFile*> pathToFile; + }; + + /** look up a MMF by filename. scoped mutex locking convention. + example: + MMFFinderByName finder; + MongoMMF *a = finder.find("file_name_a"); + MongoMMF *b = finder.find("file_name_b"); + */ + class MongoFileFinder : boost::noncopyable { + public: + MongoFileFinder() { } + + /** @return The MongoFile object associated with the specified file name. If no file is open + with the specified name, returns null. + */ + MongoFile* findByPath(string path) { + map<string,MongoFile*>::iterator i = MongoFile::pathToFile.find(path); + return i == MongoFile::pathToFile.end() ? NULL : i->second; + } + + private: + LockMongoFilesShared _lk; + }; + + struct MongoFileAllowWrites { + MongoFileAllowWrites() { + MongoFile::markAllWritable(); + } + ~MongoFileAllowWrites() { + MongoFile::unmarkAllWritable(); + } + }; + + class MemoryMappedFile : public MongoFile { + protected: + virtual void* viewForFlushing() { + if( views.size() == 0 ) + return 0; + assert( views.size() == 1 ); + return views[0]; + } + public: + MemoryMappedFile(); + + virtual ~MemoryMappedFile() { + LockMongoFilesExclusive lk; + close(); + } + + virtual void close(); + + // Throws exception if file doesn't exist. (dm may2010: not sure if this is always true?) + void* map(const char *filename); + + /** @param options see MongoFile::Options + */ + void* mapWithOptions(const char *filename, int options); + + /* Creates with length if DNE, otherwise uses existing file length, + passed length. + @param options MongoFile::Options bits + */ + void* map(const char *filename, unsigned long long &length, int options = 0 ); + + /* Create. Must not exist. + @param zero fill file with zeros when true + */ + void* create(string filename, unsigned long long len, bool zero); + + void flush(bool sync); + virtual Flushable * prepareFlush(); + + long shortLength() const { return (long) len; } + unsigned long long length() const { return len; } + + /** create a new view with the specified properties. + automatically cleaned up upon close/destruction of the MemoryMappedFile object. + */ + void* createReadOnlyMap(); + void* createPrivateMap(); + + /** make the private map range writable (necessary for our windows implementation) */ + static void makeWritable(void *, unsigned len) +#if defined(_WIN32) + ; +#else + { } +#endif + + private: + static void updateLength( const char *filename, unsigned long long &length ); + + HANDLE fd; + HANDLE maphandle; + vector<void *> views; + unsigned long long len; + +#ifdef _WIN32 + boost::shared_ptr<mutex> _flushMutex; + void clearWritableBits(void *privateView); + public: + static const unsigned ChunkSize = 64 * 1024 * 1024; + static const unsigned NChunks = 1024 * 1024; +#else + void clearWritableBits(void *privateView) { } +#endif + + protected: + // only posix mmap implementations will support this + virtual void _lock(); + virtual void _unlock(); + + /** close the current private view and open a new replacement */ + void* remapPrivateView(void *oldPrivateAddr); + }; + + typedef MemoryMappedFile MMF; + + /** p is called from within a mutex that MongoFile uses. so be careful not to deadlock. */ + template < class F > + inline void MongoFile::forEach( F p ) { + LockMongoFilesShared lklk; + for ( set<MongoFile*>::iterator i = mmfiles.begin(); i != mmfiles.end(); i++ ) + p(*i); + } + +#if defined(_WIN32) + class ourbitset { + volatile unsigned bits[MemoryMappedFile::NChunks]; // volatile as we are doing double check locking + public: + ourbitset() { + memset((void*) bits, 0, sizeof(bits)); + } + bool get(unsigned i) const { + unsigned x = i / 32; + assert( x < MemoryMappedFile::NChunks ); + return (bits[x] & (1 << (i%32))) != 0; + } + void set(unsigned i) { + unsigned x = i / 32; + wassert( x < (MemoryMappedFile::NChunks*2/3) ); // warn if getting close to limit + assert( x < MemoryMappedFile::NChunks ); + bits[x] |= (1 << (i%32)); + } + void clear(unsigned i) { + unsigned x = i / 32; + assert( x < MemoryMappedFile::NChunks ); + bits[x] &= ~(1 << (i%32)); + } + }; + extern ourbitset writable; + void makeChunkWritable(size_t chunkno); + inline void MemoryMappedFile::makeWritable(void *_p, unsigned len) { + size_t p = (size_t) _p; + unsigned a = p/ChunkSize; + unsigned b = (p+len)/ChunkSize; + for( unsigned i = a; i <= b; i++ ) { + if( !writable.get(i) ) { + makeChunkWritable(i); + } + } + } + +#endif + +} // namespace mongo |