/** * ========================================================================= * File : counter.cpp * Project : 0 A.D. * Description : Interface for counter implementations * ========================================================================= */ // license: GPL; see lib/license.txt #include "precompiled.h" #include "counter.h" #include "lib/bits.h" #include "tsc.h" #include "hpet.h" #include "pmt.h" #include "qpc.h" #include "tgt.h" // to add a new counter type, simply include its header here and // insert a case in ConstructCounterAt's switch statement. //----------------------------------------------------------------------------- // create/destroy counters /** * @return pointer to a newly constructed ICounter subclass of type <id> at * the given address, or 0 iff the ID is invalid. * @param size receives the size [bytes] of the created instance. **/ static ICounter* ConstructCounterAt(uint id, void* address, size_t& size) { // rationale for placement new: see call site. #define CREATE(impl)\ size = sizeof(Counter##impl);\ return new(address) Counter##impl(); #include "lib/nommgr.h" // MMGR interferes with placement new // counters are chosen according to the following order. rationale: // - TSC must come before QPC and PMT to make sure a bug in the latter on // Pentium systems doesn't come up. // - PMT works, but is inexplicably slower than QPC on a PIII Mobile. // - TGT really isn't as safe as the others, so it should be last. // - low-overhead and high-resolution counters are preferred. switch(id) { case 0: CREATE(HPET) case 1: CREATE(TSC) case 2: CREATE(QPC) case 3: CREATE(PMT) case 4: CREATE(TGT) default: size = 0; return 0; } #include "lib/mmgr.h" #undef CREATE } ICounter* CreateCounter(uint id) { // we placement-new the Counter classes in a static buffer. // this is dangerous, but we are careful to ensure alignment. it is // unusual and thus bad, but there's also one advantage: we avoid // using global operator new before the CRT is initialized (risky). // // alternatives: // - defining as static doesn't work because the ctors (necessary for // vptr initialization) run during _cinit, which comes after our // first use of them. // - using static_calloc isn't possible because we don't know the // size until after the alloc / placement new. static const size_t MEM_SIZE = 200; // checked below static u8 mem[MEM_SIZE]; static u8* nextMem = mem; u8* addr = (u8*)round_up((uintptr_t)nextMem, 16); size_t size; ICounter* counter = ConstructCounterAt(id, addr, size); nextMem = addr+size; debug_assert(nextMem < mem+MEM_SIZE); // had enough room? return counter; } void DestroyCounter(ICounter*& counter) { if(!counter) return; counter->Shutdown(); counter->~ICounter(); // must be called due to placement new counter = 0; }