MongoDB  2.0.3
mmap.h
00001 // mmap.h
00002 
00003 /*    Copyright 2009 10gen Inc.
00004  *
00005  *    Licensed under the Apache License, Version 2.0 (the "License");
00006  *    you may not use this file except in compliance with the License.
00007  *    You may obtain a copy of the License at
00008  *
00009  *    http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  *    Unless required by applicable law or agreed to in writing, software
00012  *    distributed under the License is distributed on an "AS IS" BASIS,
00013  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  *    See the License for the specific language governing permissions and
00015  *    limitations under the License.
00016  */
00017 
00018 #pragma once
00019 #include <boost/thread/xtime.hpp>
00020 #include "concurrency/rwlock.h"
00021 
00022 namespace mongo {
00023 
00024     class MAdvise { 
00025         void *_p;
00026         unsigned _len;
00027     public:
00028         enum Advice { Sequential=1 };
00029         MAdvise(void *p, unsigned len, Advice a); 
00030         ~MAdvise(); // destructor resets the range to MADV_NORMAL
00031     };
00032 
00033     /* the administrative-ish stuff here */
00034     class MongoFile : boost::noncopyable {
00035     public:
00037         class Flushable {
00038         public:
00039             virtual ~Flushable() {}
00040             virtual void flush() = 0;
00041         };
00042 
00043         virtual ~MongoFile() {}
00044 
00045         enum Options {
00046             SEQUENTIAL = 1, // hint - e.g. FILE_FLAG_SEQUENTIAL_SCAN on windows
00047             READONLY = 2    // not contractually guaranteed, but if specified the impl has option to fault writes
00048         };
00049 
00053         template < class F >
00054         static void forEach( F fun );
00055 
00058         static set<MongoFile*>& getAllFiles()  { return mmfiles; }
00059 
00060         // callbacks if you need them
00061         static void (*notifyPreFlush)();
00062         static void (*notifyPostFlush)();
00063 
00064         static int flushAll( bool sync ); // returns n flushed
00065         static long long totalMappedLength();
00066         static void closeAllFiles( stringstream &message );
00067 
00068 #if defined(_DEBUG)
00069         static void markAllWritable();
00070         static void unmarkAllWritable();
00071 #else
00072         static void markAllWritable() { }
00073         static void unmarkAllWritable() { }
00074 #endif
00075 
00076         static bool exists(boost::filesystem::path p) { return boost::filesystem::exists(p); }
00077 
00078         virtual bool isMongoMMF() { return false; }
00079 
00080         string filename() const { return _filename; }
00081         void setFilename(string fn);
00082 
00083     private:
00084         string _filename;
00085         static int _flushAll( bool sync ); // returns n flushed
00086     protected:
00087         virtual void close() = 0;
00088         virtual void flush(bool sync) = 0;
00093         virtual Flushable * prepareFlush() = 0;
00094 
00095         void created(); /* subclass must call after create */
00096 
00097         /* subclass must call in destructor (or at close).
00098            removes this from pathToFile and other maps
00099            safe to call more than once, albeit might be wasted work
00100            ideal to call close to the close, if the close is well before object destruction
00101         */
00102         void destroyed(); 
00103 
00104         virtual unsigned long long length() const = 0;
00105 
00106         // only supporting on posix mmap
00107         virtual void _lock() {}
00108         virtual void _unlock() {}
00109 
00110         static set<MongoFile*> mmfiles;
00111     public:
00112         static map<string,MongoFile*> pathToFile;
00113 
00114         // lock order: lock dbMutex before this if you lock both
00115         static RWLockRecursive mmmutex;
00116     };
00117 
00124     class MongoFileFinder : boost::noncopyable {
00125     public:
00126         MongoFileFinder() : _lk(MongoFile::mmmutex) { }
00127 
00131         MongoFile* findByPath(string path) {
00132             map<string,MongoFile*>::iterator i = MongoFile::pathToFile.find(path);
00133             return  i == MongoFile::pathToFile.end() ? NULL : i->second;
00134         }
00135 
00136     private:
00137         RWLockRecursive::Shared _lk;
00138     };
00139 
00140     struct MongoFileAllowWrites {
00141         MongoFileAllowWrites() {
00142             MongoFile::markAllWritable();
00143         }
00144         ~MongoFileAllowWrites() {
00145             MongoFile::unmarkAllWritable();
00146         }
00147     };
00148 
00149     class MemoryMappedFile : public MongoFile {
00150     protected:
00151         virtual void* viewForFlushing() { 
00152             if( views.size() == 0 )
00153                 return 0;
00154             assert( views.size() == 1 );
00155             return views[0];
00156         }
00157     public:
00158         MemoryMappedFile();
00159 
00160         virtual ~MemoryMappedFile() {
00161             RWLockRecursive::Exclusive lk(mmmutex);
00162             close();
00163         }
00164 
00165         virtual void close();
00166 
00167         // Throws exception if file doesn't exist. (dm may2010: not sure if this is always true?)
00168         void* map(const char *filename);
00169 
00171         void* mapWithOptions(const char *filename, int options);
00172 
00173         /* Creates with length if DNE, otherwise uses existing file length,
00174            passed length.
00175            @param options MongoFile::Options bits
00176         */
00177         void* map(const char *filename, unsigned long long &length, int options = 0 );
00178 
00179         /* Create. Must not exist.
00180            @param zero fill file with zeros when true
00181         */
00182         void* create(string filename, unsigned long long len, bool zero);
00183 
00184         void flush(bool sync);
00185         virtual Flushable * prepareFlush();
00186 
00187         long shortLength() const          { return (long) len; }
00188         unsigned long long length() const { return len; }
00189 
00193         void* createReadOnlyMap();
00194         void* createPrivateMap();
00195 
00197         static void makeWritable(void *, unsigned len)
00198 #if defined(_WIN32)
00199             ;
00200 #else
00201         { }
00202 #endif
00203 
00204     private:
00205         static void updateLength( const char *filename, unsigned long long &length );
00206 
00207         HANDLE fd;
00208         HANDLE maphandle;
00209         vector<void *> views;
00210         unsigned long long len;
00211         
00212 #ifdef _WIN32
00213         boost::shared_ptr<mutex> _flushMutex;
00214         void clearWritableBits(void *privateView);
00215     public:
00216         static const unsigned ChunkSize = 64 * 1024 * 1024;
00217         static const unsigned NChunks = 1024 * 1024;
00218 #else
00219         void clearWritableBits(void *privateView) { }
00220 #endif
00221 
00222     protected:
00223         // only posix mmap implementations will support this
00224         virtual void _lock();
00225         virtual void _unlock();
00226 
00228         void* remapPrivateView(void *oldPrivateAddr);
00229     };
00230 
00231     typedef MemoryMappedFile MMF;
00232 
00234     template < class F >
00235     inline void MongoFile::forEach( F p ) {
00236         RWLockRecursive::Shared lklk(mmmutex);
00237         for ( set<MongoFile*>::iterator i = mmfiles.begin(); i != mmfiles.end(); i++ )
00238             p(*i);
00239     }
00240 
00241 #if defined(_WIN32)    
00242     class ourbitset {
00243         volatile unsigned bits[MemoryMappedFile::NChunks]; // volatile as we are doing double check locking
00244     public:
00245         ourbitset() { 
00246             memset((void*) bits, 0, sizeof(bits));
00247         }
00248         bool get(unsigned i) const { 
00249             unsigned x = i / 32;
00250             assert( x < MemoryMappedFile::NChunks );
00251             return (bits[x] & (1 << (i%32))) != 0;
00252         }
00253         void set(unsigned i) { 
00254             unsigned x = i / 32;
00255             wassert( x < (MemoryMappedFile::NChunks*2/3) ); // warn if getting close to limit
00256             assert( x < MemoryMappedFile::NChunks );
00257             bits[x] |= (1 << (i%32));
00258         }
00259         void clear(unsigned i) { 
00260             unsigned x = i / 32;
00261             assert( x < MemoryMappedFile::NChunks );
00262             bits[x] &= ~(1 << (i%32));
00263         }
00264     };
00265     extern ourbitset writable;
00266     void makeChunkWritable(size_t chunkno);
00267     inline void MemoryMappedFile::makeWritable(void *_p, unsigned len) {
00268         size_t p = (size_t) _p;
00269         unsigned a = p/ChunkSize;
00270         unsigned b = (p+len)/ChunkSize;
00271         for( unsigned i = a; i <= b; i++ ) {
00272             if( !writable.get(i) ) {
00273                 makeChunkWritable(i);
00274             }
00275         }
00276     }
00277 
00278 #endif
00279 
00280 } // namespace mongo