Files
mongo/db/mongommf.cpp

322 lines
11 KiB
C++
Raw Normal View History

2010-10-01 16:14:36 -04:00
// @file mongommf.cpp
/**
* Copyright (C) 2010 10gen 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 <http://www.gnu.org/licenses/>.
*/
2011-01-04 00:40:41 -05:00
/* this module adds some of our layers atop memory mapped files - specifically our handling of private views & such
2010-10-01 16:14:36 -04:00
if you don't care about journaling/durability (temp sort files & such) use MemoryMappedFile class, not this.
*/
#include "pch.h"
2010-11-28 10:13:01 -05:00
#include "cmdline.h"
2010-10-01 16:14:36 -04:00
#include "mongommf.h"
2010-11-26 18:18:24 -05:00
#include "dur.h"
#include "dur_journalformat.h"
2010-11-14 22:28:04 -05:00
#include "../util/mongoutils/str.h"
2010-10-01 16:14:36 -04:00
2010-11-14 22:28:04 -05:00
using namespace mongoutils;
2010-10-01 16:14:36 -04:00
2010-11-14 22:28:04 -05:00
namespace mongo {
2010-11-13 17:42:41 -05:00
2011-02-21 11:19:58 -05:00
#if defined(_WIN32)
extern mutex mapViewMutex;
2011-02-21 11:19:58 -05:00
__declspec(noinline) void makeChunkWritable(size_t chunkno) {
scoped_lock lk(mapViewMutex);
2011-02-21 11:19:58 -05:00
if( writable.get(chunkno) ) // double check lock
return;
// remap all maps in this chunk. common case is a single map, but could have more than one with smallfiles or .ns files
size_t chunkStart = chunkno * MemoryMappedFile::ChunkSize;
size_t chunkNext = chunkStart + MemoryMappedFile::ChunkSize;
scoped_lock lk2(privateViews._mutex());
map<void*,MongoMMF*>::iterator i = privateViews.finditer_inlock((void*) (chunkNext-1));
while( 1 ) {
const pair<void*,MongoMMF*> x = *(--i);
MongoMMF *mmf = x.second;
if( mmf == 0 )
break;
size_t viewStart = (size_t) x.first;
2011-02-24 14:37:40 -05:00
size_t viewEnd = (size_t) (viewStart + mmf->length());
if( viewEnd <= chunkStart )
break;
size_t protectStart = max(viewStart, chunkStart);
dassert(protectStart<chunkNext);
size_t protectEnd = min(viewEnd, chunkNext);
2011-02-21 11:19:58 -05:00
size_t protectSize = protectEnd - protectStart;
dassert(protectSize>0&&protectSize<=MemoryMappedFile::ChunkSize);
DWORD old;
bool ok = VirtualProtect((void*)protectStart, protectSize, PAGE_WRITECOPY, &old);
if( !ok ) {
DWORD e = GetLastError();
log() << "VirtualProtect failed (mcw) " << mmf->filename() << ' ' << chunkno << hex << protectStart << ' ' << protectSize << ' ' << errnoWithDescription(e) << endl;
2011-02-21 11:19:58 -05:00
assert(false);
}
}
writable.set(chunkno);
}
void* MemoryMappedFile::createPrivateMap() {
assert( maphandle );
scoped_lock lk(mapViewMutex);
void *p = MapViewOfFile(maphandle, FILE_MAP_READ, 0, 0, 0);
if ( p == 0 ) {
DWORD e = GetLastError();
2011-04-18 18:43:09 -04:00
log() << "createPrivateMap failed " << filename() << " " <<
errnoWithDescription(e) << " filelen:" << len <<
((sizeof(void*) == 4 ) ? " (32 bit build)" : "") <<
endl;
}
else {
clearWritableBits(p);
views.push_back(p);
}
return p;
}
void* MemoryMappedFile::remapPrivateView(void *oldPrivateAddr) {
2011-02-21 19:09:54 -05:00
dbMutex.assertWriteLocked(); // short window where we are unmapped so must be exclusive
2011-02-21 19:09:54 -05:00
// the mapViewMutex is to assure we get the same address on the remap
scoped_lock lk(mapViewMutex);
clearWritableBits(oldPrivateAddr);
#if 1
// https://jira.mongodb.org/browse/SERVER-2942
DWORD old;
2011-04-15 17:32:45 -04:00
bool ok = VirtualProtect(oldPrivateAddr, (SIZE_T) len, PAGE_READONLY, &old);
if( !ok ) {
DWORD e = GetLastError();
log() << "VirtualProtect failed in remapPrivateView " << filename() << hex << oldPrivateAddr << ' ' << len << ' ' << errnoWithDescription(e) << endl;
assert(false);
}
return oldPrivateAddr;
#else
2011-02-15 15:31:44 -05:00
if( !UnmapViewOfFile(oldPrivateAddr) ) {
DWORD e = GetLastError();
log() << "UnMapViewOfFile failed " << filename() << ' ' << errnoWithDescription(e) << endl;
assert(false);
}
// we want the new address to be the same as the old address in case things keep pointers around (as namespaceindex does).
void *p = MapViewOfFileEx(maphandle, FILE_MAP_READ, 0, 0,
/*dwNumberOfBytesToMap 0 means to eof*/0 /*len*/,
oldPrivateAddr);
if ( p == 0 ) {
DWORD e = GetLastError();
log() << "MapViewOfFileEx failed " << filename() << " " << errnoWithDescription(e) << endl;
assert(p);
}
assert(p == oldPrivateAddr);
return p;
#endif
}
#endif
2011-01-04 00:40:41 -05:00
void MongoMMF::remapThePrivateView() {
2011-01-08 13:22:45 -05:00
assert( cmdLine.dur );
2011-01-28 02:41:59 -05:00
// todo 1.9 : it turns out we require that we always remap to the same address.
// so the remove / add isn't necessary and can be removed
2010-11-27 15:25:08 -05:00
privateViews.remove(_view_private);
2011-01-04 00:40:41 -05:00
_view_private = remapPrivateView(_view_private);
2010-11-27 15:25:08 -05:00
privateViews.add(_view_private, this);
}
2010-11-21 14:02:13 -05:00
/** register view. threadsafe */
void PointerToMMF::add(void *view, MongoMMF *f) {
2011-01-25 16:35:01 -05:00
assert(view);
assert(f);
2010-11-21 14:02:13 -05:00
mutex::scoped_lock lk(_m);
_views.insert( pair<void*,MongoMMF*>(view,f) );
}
/** de-register view. threadsafe */
void PointerToMMF::remove(void *view) {
if( view ) {
mutex::scoped_lock lk(_m);
_views.erase(view);
}
}
2011-01-04 00:40:41 -05:00
PointerToMMF::PointerToMMF() : _m("PointerToMMF") {
2010-11-21 14:02:13 -05:00
#if defined(SIZE_MAX)
size_t max = SIZE_MAX;
#else
size_t max = ~((size_t)0);
#endif
assert( max > (size_t) this ); // just checking that no one redef'd SIZE_MAX and that it is sane
// this way we don't need any boundary checking in _find()
_views.insert( pair<void*,MongoMMF*>((void*)0,(MongoMMF*)0) );
_views.insert( pair<void*,MongoMMF*>((void*)max,(MongoMMF*)0) );
}
/** underscore version of find is for when you are already locked
@param ofs out return our offset in the view
2011-01-04 00:40:41 -05:00
@return the MongoMMF to which this pointer belongs
2010-11-21 14:02:13 -05:00
*/
MongoMMF* PointerToMMF::find_inlock(void *p, /*out*/ size_t& ofs) {
2010-11-21 14:02:13 -05:00
//
// .................memory..........................
// v1 p v2
// [--------------------] [-------]
//
// e.g., _find(p) == v1
//
const pair<void*,MongoMMF*> x = *(--_views.upper_bound(p));
MongoMMF *mmf = x.second;
if( mmf ) {
size_t o = ((char *)p) - ((char*)x.first);
2011-01-04 00:40:41 -05:00
if( o < mmf->length() ) {
2010-11-14 22:28:04 -05:00
ofs = o;
return mmf;
2010-11-13 17:42:41 -05:00
}
}
2010-11-14 22:28:04 -05:00
return 0;
}
2010-11-13 17:42:41 -05:00
2010-11-14 22:28:04 -05:00
/** find associated MMF object for a given pointer.
threadsafe
@param ofs out returns offset into the view of the pointer, if found.
@return the MongoMMF to which this pointer belongs. null if not found.
*/
MongoMMF* PointerToMMF::find(void *p, /*out*/ size_t& ofs) {
mutex::scoped_lock lk(_m);
return find_inlock(p, ofs);
2010-11-14 22:28:04 -05:00
}
2010-11-13 17:42:41 -05:00
2010-11-14 22:28:04 -05:00
PointerToMMF privateViews;
2010-10-04 14:02:52 -04:00
2011-01-08 13:22:45 -05:00
/* void* MongoMMF::switchToPrivateView(void *readonly_ptr) {
2010-11-28 10:13:01 -05:00
assert( cmdLine.dur );
2010-11-20 15:29:49 -05:00
assert( testIntent );
2010-10-01 16:14:36 -04:00
2010-11-13 17:42:41 -05:00
void *p = readonly_ptr;
2010-10-01 16:14:36 -04:00
2010-11-13 17:42:41 -05:00
{
2010-11-13 18:32:41 -05:00
size_t ofs=0;
2010-11-13 17:42:41 -05:00
MongoMMF *mmf = ourReadViews.find(p, ofs);
if( mmf ) {
2010-11-21 14:02:13 -05:00
void *res = ((char *)mmf->_view_private) + ofs;
return res;
2010-10-01 16:14:36 -04:00
}
}
2010-11-13 17:42:41 -05:00
{
2010-11-13 18:32:41 -05:00
size_t ofs=0;
2010-11-14 22:28:04 -05:00
MongoMMF *mmf = privateViews.find(p, ofs);
2010-11-13 17:42:41 -05:00
if( mmf ) {
log() << "dur: perf warning p=" << p << " is already in the writable view of " << mmf->filename() << endl;
2010-10-01 16:14:36 -04:00
return p;
}
}
2010-11-13 17:42:41 -05:00
// did you call writing() with a pointer that isn't into a datafile?
2010-11-21 14:02:13 -05:00
log() << "dur error switchToPrivateView " << p << endl;
2010-11-13 17:42:41 -05:00
return p;
2011-01-08 13:22:45 -05:00
}*/
2010-10-04 14:02:52 -04:00
2011-01-04 00:40:41 -05:00
/* switch to _view_write. normally, this is a bad idea since your changes will not
2010-11-04 17:43:02 -04:00
show up in _view_private if there have been changes there; thus the leading underscore
2011-01-04 00:40:41 -05:00
as a tad of a "warning". but useful when done with some care, such as during
2010-10-04 15:02:15 -04:00
initialization.
2010-10-04 14:02:52 -04:00
*/
2011-01-13 22:33:44 -05:00
void* MongoMMF::_switchToWritableView(void *p) {
size_t ofs;
MongoMMF *f = privateViews.find(p, ofs);
assert( f );
return (((char *)f->_view_write)+ofs);
}
2010-10-01 16:14:36 -04:00
2010-12-14 20:27:24 -05:00
extern string dbpath;
// here so that it is precomputed...
2010-11-14 22:28:04 -05:00
void MongoMMF::setPath(string f) {
string suffix;
string prefix;
bool ok = str::rSplitOn(f, '.', prefix, suffix);
2010-11-14 22:28:04 -05:00
uassert(13520, str::stream() << "MongoMMF only supports filenames in a certain format " << f, ok);
if( suffix == "ns" )
_fileSuffixNo = dur::JEntry::DotNsSuffix;
2011-01-04 00:40:41 -05:00
else
2010-11-14 22:28:04 -05:00
_fileSuffixNo = (int) str::toUnsigned(suffix);
2010-12-14 20:27:24 -05:00
_p = RelativePath::fromFullPath(prefix);
2010-11-14 22:28:04 -05:00
}
2010-10-13 13:06:49 -04:00
bool MongoMMF::open(string fname, bool sequentialHint) {
2010-11-14 22:28:04 -05:00
setPath(fname);
2010-11-04 17:43:02 -04:00
_view_write = mapWithOptions(fname.c_str(), sequentialHint ? SEQUENTIAL : 0);
2010-11-16 16:27:26 -05:00
return finishOpening();
2010-10-01 16:14:36 -04:00
}
2011-01-04 00:40:41 -05:00
bool MongoMMF::create(string fname, unsigned long long& len, bool sequentialHint) {
2010-11-14 22:28:04 -05:00
setPath(fname);
2010-11-04 17:43:02 -04:00
_view_write = map(fname.c_str(), len, sequentialHint ? SEQUENTIAL : 0);
2010-11-16 16:27:26 -05:00
return finishOpening();
}
bool MongoMMF::finishOpening() {
2010-11-04 17:43:02 -04:00
if( _view_write ) {
2010-11-28 10:13:01 -05:00
if( cmdLine.dur ) {
2011-01-08 13:22:45 -05:00
_view_private = createPrivateMap();
if( _view_private == 0 ) {
2011-04-18 18:43:09 -04:00
msgasserted(13636, str::stream() << "file " << filename() << " open/create failed in createPrivateMap (look in log for more information)");
}
privateViews.add(_view_private, this); // note that testIntent builds use this, even though it points to view_write then...
2010-10-01 16:14:36 -04:00
}
2011-01-04 00:40:41 -05:00
else {
2010-11-16 16:40:56 -05:00
_view_private = _view_write;
}
2010-10-01 16:14:36 -04:00
return true;
}
return false;
}
2011-01-04 00:40:41 -05:00
2010-11-27 15:25:08 -05:00
MongoMMF::MongoMMF() : _willNeedRemap(false) {
2011-01-08 13:22:45 -05:00
_view_write = _view_private = 0;
2010-10-01 16:14:36 -04:00
}
2011-01-04 00:40:41 -05:00
MongoMMF::~MongoMMF() {
close();
}
2011-01-04 00:40:41 -05:00
namespace dur {
2010-12-01 13:16:37 -05:00
void closingFileNotification();
2010-11-26 18:18:24 -05:00
}
/*virtual*/ void MongoMMF::close() {
if( cmdLine.dur && _view_write/*actually was opened*/ ) {
dur::closingFileNotification();
2010-10-01 16:14:36 -04:00
}
RWLockRecursive::Exclusive lk(mmmutex);
privateViews.remove(_view_private);
2011-01-08 13:22:45 -05:00
_view_write = _view_private = 0;
MemoryMappedFile::close();
2010-10-01 16:14:36 -04:00
}
}