// record.cpp #include "pch.h" #include "pdfile.h" #include "../util/processinfo.h" #include "../util/net/listen.h" #include "pagefault.h" namespace mongo { namespace ps { enum State { In , Out, Unk }; enum Constants { SliceSize = 65536 , MaxChain = 20 , // intentionally very low NumSlices = 10 , RotateTimeSecs = 90 }; int hash( size_t region ) { return abs( ( ( 7 + (int)(region & 0xFFFF) ) * ( 11 + (int)( ( region >> 16 ) & 0xFFFF ) ) #if defined(_WIN64) || defined(__amd64__) * ( 13 + (int)( ( region >> 32 ) & 0xFFFF ) ) * ( 17 + (int)( ( region >> 48 ) & 0xFFFF ) ) #endif ) % SliceSize ); } /** * simple hash map for region -> status * this constitures a single region of time * it does chaining, but very short chains */ class Slice { struct Entry { size_t region; unsigned long long value; }; public: Slice() { reset(); } void reset() { memset( _data , 0 , SliceSize * sizeof(Entry) ); } State get( int regionHash , size_t region , short offset ) { DEV assert( hash( region ) == regionHash ); Entry * e = _get( regionHash , region , false ); if ( ! e ) return Unk; return ( e->value & ( ((unsigned long long)1) << offset ) ) ? In : Out; } /** * @return true if added, false if full */ bool in( int regionHash , size_t region , short offset ) { DEV assert( hash( region ) == regionHash ); Entry * e = _get( regionHash , region , true ); if ( ! e ) return false; e->value |= ((unsigned long long)1) << offset; return true; } private: Entry* _get( int start , size_t region , bool add ) { for ( int i=0; i ( 1000 * RotateTimeSecs ) ) { _rotate(); } } for ( int i=0; i HeaderSize ) { // this also makes sure lengthWithHeaders is in memory char * addr = data; char * end = data + netLength(); for ( ; addr <= end ; addr += 2048 ) { __record_touch_dummy += addr[0]; break; // TODO: remove this, pending SERVER-3711 // note if this is a touch of a deletedrecord, we don't want to touch more than the first part. we may simply // be updated the linked list and a deletedrecord could be gigantic. similar circumstance just less extreme // exists for any record if we are just updating its header, say on a remove(); some sort of hints might be // useful. if ( ! entireRecrd ) break; } } } const bool blockSupported = ProcessInfo::blockCheckSupported(); bool Record::likelyInPhysicalMemory() { DEV if ( rand() % 100 == 0 ) return false; if ( ! MemoryTrackingEnabled ) return true; const size_t page = (size_t)data >> 12; const size_t region = page >> 6; const size_t offset = page & 0x3f; if ( ps::rolling.access( region , offset , false ) ) { #ifdef _DEBUG if ( blockSupported && ! ProcessInfo::blockInMemory( data ) ) { warning() << "we think data is in ram but system says no" << endl; } #endif return true; } if ( ! blockSupported ) { // this means we don't fallback to system call // and assume things aren't in memory // possible we yield too much - but better than not yielding through a fault return false; } return ProcessInfo::blockInMemory( data ); } Record* Record::accessed() { const size_t page = (size_t)data >> 12; const size_t region = page >> 6; const size_t offset = page & 0x3f; ps::rolling.access( region , offset , true ); return this; } Record* DiskLoc::rec() const { Record *r = DataFileMgr::getRecord(*this); memconcept::is(r, memconcept::concept::record); #if defined(_PAGEFAULTEXCEPTION) DEV ONCE { log() << "_DEBUG info _PAGEFAULTEXCEPTION is ON -- experimental at this time" << endl; } bool fault = !r->likelyInPhysicalMemory(); if( cc().allowedToThrowPageFaultException() && ! r->likelyInPhysicalMemory() ) { if( cc().getPageFaultRetryableSection()->laps() > 100 ) { log() << "info pagefaultexception _laps > 100" << endl; } else { throw PageFaultException(r); } } #else DEV ONCE { log() << "_DEBUG info _PAGEFAULTEXCEPTION is off" << endl; } #endif return r; } }